update params for fetching metric data

This commit is contained in:
Nate Kelley 2025-03-19 14:47:15 -06:00
parent 72eafd83bd
commit e12fe28b04
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
32 changed files with 269 additions and 279 deletions

96
web/package-lock.json generated
View File

@ -55,11 +55,11 @@
"monaco-sql-languages": "^0.13.1",
"monaco-yaml": "^5.3.1",
"mutative": "^1.1.0",
"next": "14.2.24",
"next": "14.2.25",
"next-themes": "^0.4.6",
"papaparse": "^5.5.2",
"pluralize": "^8.0.0",
"posthog-js": "^1.231.2",
"posthog-js": "^1.231.3",
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"react": "^18",
@ -3989,9 +3989,9 @@
}
},
"node_modules/@next/env": {
"version": "14.2.24",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.24.tgz",
"integrity": "sha512-LAm0Is2KHTNT6IT16lxT+suD0u+VVfYNQqM+EJTKuFRRuY2z+zj01kueWXPCxbMBDt0B5vONYzabHGUNbZYAhA==",
"version": "14.2.25",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.25.tgz",
"integrity": "sha512-JnzQ2cExDeG7FxJwqAksZ3aqVJrHjFwZQAEJ9gQZSoEhIow7SNoKZzju/AwQ+PLIR4NY8V0rhcVozx/2izDO0w==",
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
@ -4005,9 +4005,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
"version": "14.2.24",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.24.tgz",
"integrity": "sha512-7Tdi13aojnAZGpapVU6meVSpNzgrFwZ8joDcNS8cJVNuP3zqqrLqeory9Xec5TJZR/stsGJdfwo8KeyloT3+rQ==",
"version": "14.2.25",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.25.tgz",
"integrity": "sha512-09clWInF1YRd6le00vt750s3m7SEYNehz9C4PUcSu3bAdCTpjIV4aTYQZ25Ehrr83VR1rZeqtKUPWSI7GfuKZQ==",
"cpu": [
"arm64"
],
@ -4021,9 +4021,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "14.2.24",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.24.tgz",
"integrity": "sha512-lXR2WQqUtu69l5JMdTwSvQUkdqAhEWOqJEYUQ21QczQsAlNOW2kWZCucA6b3EXmPbcvmHB1kSZDua/713d52xg==",
"version": "14.2.25",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.25.tgz",
"integrity": "sha512-V+iYM/QR+aYeJl3/FWWU/7Ix4b07ovsQ5IbkwgUK29pTHmq+5UxeDr7/dphvtXEq5pLB/PucfcBNh9KZ8vWbug==",
"cpu": [
"x64"
],
@ -4037,9 +4037,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "14.2.24",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.24.tgz",
"integrity": "sha512-nxvJgWOpSNmzidYvvGDfXwxkijb6hL9+cjZx1PVG6urr2h2jUqBALkKjT7kpfurRWicK6hFOvarmaWsINT1hnA==",
"version": "14.2.25",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.25.tgz",
"integrity": "sha512-LFnV2899PJZAIEHQ4IMmZIgL0FBieh5keMnriMY1cK7ompR+JUd24xeTtKkcaw8QmxmEdhoE5Mu9dPSuDBgtTg==",
"cpu": [
"arm64"
],
@ -4053,9 +4053,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "14.2.24",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.24.tgz",
"integrity": "sha512-PaBgOPhqa4Abxa3y/P92F3kklNPsiFjcjldQGT7kFmiY5nuFn8ClBEoX8GIpqU1ODP2y8P6hio6vTomx2Vy0UQ==",
"version": "14.2.25",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.25.tgz",
"integrity": "sha512-QC5y5PPTmtqFExcKWKYgUNkHeHE/z3lUsu83di488nyP0ZzQ3Yse2G6TCxz6nNsQwgAx1BehAJTZez+UQxzLfw==",
"cpu": [
"arm64"
],
@ -4069,9 +4069,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "14.2.24",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.24.tgz",
"integrity": "sha512-vEbyadiRI7GOr94hd2AB15LFVgcJZQWu7Cdi9cWjCMeCiUsHWA0U5BkGPuoYRnTxTn0HacuMb9NeAmStfBCLoQ==",
"version": "14.2.25",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.25.tgz",
"integrity": "sha512-y6/ML4b9eQ2D/56wqatTJN5/JR8/xdObU2Fb1RBidnrr450HLCKr6IJZbPqbv7NXmje61UyxjF5kvSajvjye5w==",
"cpu": [
"x64"
],
@ -4085,9 +4085,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "14.2.24",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.24.tgz",
"integrity": "sha512-df0FC9ptaYsd8nQCINCzFtDWtko8PNRTAU0/+d7hy47E0oC17tI54U/0NdGk7l/76jz1J377dvRjmt6IUdkpzQ==",
"version": "14.2.25",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.25.tgz",
"integrity": "sha512-sPX0TSXHGUOZFvv96GoBXpB3w4emMqKeMgemrSxI7A6l55VBJp/RKYLwZIB9JxSqYPApqiREaIIap+wWq0RU8w==",
"cpu": [
"x64"
],
@ -4101,9 +4101,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "14.2.24",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.24.tgz",
"integrity": "sha512-ZEntbLjeYAJ286eAqbxpZHhDFYpYjArotQ+/TW9j7UROh0DUmX7wYDGtsTPpfCV8V+UoqHBPU7q9D4nDNH014Q==",
"version": "14.2.25",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.25.tgz",
"integrity": "sha512-ReO9S5hkA1DU2cFCsGoOEp7WJkhFzNbU/3VUF6XxNGUCQChyug6hZdYL/istQgfT/GWE6PNIg9cm784OI4ddxQ==",
"cpu": [
"arm64"
],
@ -4117,9 +4117,9 @@
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "14.2.24",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.24.tgz",
"integrity": "sha512-9KuS+XUXM3T6v7leeWU0erpJ6NsFIwiTFD5nzNg8J5uo/DMIPvCp3L1Ao5HjbHX0gkWPB1VrKoo/Il4F0cGK2Q==",
"version": "14.2.25",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.25.tgz",
"integrity": "sha512-DZ/gc0o9neuCDyD5IumyTGHVun2dCox5TfPQI/BJTYwpSNYM3CZDI4i6TOdjeq1JMo+Ug4kPSMuZdwsycwFbAw==",
"cpu": [
"ia32"
],
@ -4133,9 +4133,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "14.2.24",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.24.tgz",
"integrity": "sha512-cXcJ2+x0fXQ2CntaE00d7uUH+u1Bfp/E0HsNQH79YiLaZE5Rbm7dZzyAYccn3uICM7mw+DxoMqEfGXZtF4Fgaw==",
"version": "14.2.25",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.25.tgz",
"integrity": "sha512-KSznmS6eFjQ9RJ1nEc66kJvtGIL1iZMYmGEXsZPh2YtnLtqrgdVvKXJY2ScjjoFnG6nGLyPFR0UiEvDwVah4Tw==",
"cpu": [
"x64"
],
@ -16547,12 +16547,12 @@
"license": "MIT"
},
"node_modules/next": {
"version": "14.2.24",
"resolved": "https://registry.npmjs.org/next/-/next-14.2.24.tgz",
"integrity": "sha512-En8VEexSJ0Py2FfVnRRh8gtERwDRaJGNvsvad47ShkC2Yi8AXQPXEA2vKoDJlGFSj5WE5SyF21zNi4M5gyi+SQ==",
"version": "14.2.25",
"resolved": "https://registry.npmjs.org/next/-/next-14.2.25.tgz",
"integrity": "sha512-N5M7xMc4wSb4IkPvEV5X2BRRXUmhVHNyaXwEM86+voXthSZz8ZiRyQW4p9mwAoAPIm6OzuVZtn7idgEJeAJN3Q==",
"license": "MIT",
"dependencies": {
"@next/env": "14.2.24",
"@next/env": "14.2.25",
"@swc/helpers": "0.5.5",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001579",
@ -16567,15 +16567,15 @@
"node": ">=18.17.0"
},
"optionalDependencies": {
"@next/swc-darwin-arm64": "14.2.24",
"@next/swc-darwin-x64": "14.2.24",
"@next/swc-linux-arm64-gnu": "14.2.24",
"@next/swc-linux-arm64-musl": "14.2.24",
"@next/swc-linux-x64-gnu": "14.2.24",
"@next/swc-linux-x64-musl": "14.2.24",
"@next/swc-win32-arm64-msvc": "14.2.24",
"@next/swc-win32-ia32-msvc": "14.2.24",
"@next/swc-win32-x64-msvc": "14.2.24"
"@next/swc-darwin-arm64": "14.2.25",
"@next/swc-darwin-x64": "14.2.25",
"@next/swc-linux-arm64-gnu": "14.2.25",
"@next/swc-linux-arm64-musl": "14.2.25",
"@next/swc-linux-x64-gnu": "14.2.25",
"@next/swc-linux-x64-musl": "14.2.25",
"@next/swc-win32-arm64-msvc": "14.2.25",
"@next/swc-win32-ia32-msvc": "14.2.25",
"@next/swc-win32-x64-msvc": "14.2.25"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
@ -17578,9 +17578,9 @@
"license": "MIT"
},
"node_modules/posthog-js": {
"version": "1.231.2",
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.231.2.tgz",
"integrity": "sha512-KTOZTvPbVZoZYC4vqZUmPoG/brbyNqqeeUllH4MZBIW0gyC6JJJYsU+za1of9La73F4KBN1ZM3O0tXpFrLSAww==",
"version": "1.231.3",
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.231.3.tgz",
"integrity": "sha512-IHbdMpI9btpgn7xIlq+cFH47227NTxD6so9jWYLdVD7Qt5rLTm0Ho61iUFHj1gwd+pFXP3H2TCBJ9+dBgZ0zFw==",
"license": "MIT",
"dependencies": {
"core-js": "^3.38.1",

View File

@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"dev": "next dev --turbo",
"build": "next build",
"start": "next start",
"lint": "next lint",
@ -63,11 +63,11 @@
"monaco-sql-languages": "^0.13.1",
"monaco-yaml": "^5.3.1",
"mutative": "^1.1.0",
"next": "14.2.24",
"next": "14.2.25",
"next-themes": "^0.4.6",
"papaparse": "^5.5.2",
"pluralize": "^8.0.0",
"posthog-js": "^1.231.2",
"posthog-js": "^1.231.3",
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"react": "^18",

View File

@ -144,7 +144,7 @@ export const DEFAULT_IBUSTER_METRIC: Required<IBusterMetric> = {
version_number: 1,
description: '',
time_frame: '',
code: null,
sql: null,
feedback: null,
dataset_id: '',
dataset_name: null,

View File

@ -24,7 +24,7 @@ export type BusterMetric = {
sent_by_id: string;
sent_by_name: string;
sent_by_avatar_url: string | null;
code: string | null;
sql: string | null;
feedback: 'negative' | null;
dashboards: {
id: string;

View File

@ -1,6 +1,6 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { QueryClient } from '@tanstack/react-query';
import { useMemoizedFn } from '@/hooks';
import { useDebounceFn, useMemoizedFn } from '@/hooks';
import {
deleteMetrics,
duplicateMetric,
@ -23,7 +23,7 @@ import type { IBusterMetric } from '@/api/asset_interfaces/metric';
import { dashboardQueryKeys } from '@/api/query_keys/dashboard';
export const useGetMetric = <TData = IBusterMetric>(
id: string | undefined,
{ id, version_number }: { id: string | undefined; version_number?: number },
select?: (data: IBusterMetric) => TData
) => {
const getAssetPassword = useBusterAssetsContextSelector((x) => x.getAssetPassword);
@ -101,27 +101,33 @@ export const prefetchGetMetricsList = async (
return queryClient;
};
export const useGetMetricData = (params: { id: string }) => {
export const useGetMetricData = ({
id,
version_number
}: {
id: string;
version_number?: number;
}) => {
const queryFn = useMemoizedFn(() => {
return getMetricData(params);
return getMetricData({ id, version_number });
});
return useQuery({
...metricsQueryKeys.metricsGetData(params.id),
...metricsQueryKeys.metricsGetData(id),
queryFn,
enabled: !!params.id
enabled: !!id
});
};
export const prefetchGetMetricDataClient = async (
params: { id: string },
{ id }: { id: string },
queryClient: QueryClient
) => {
const options = metricsQueryKeys.metricsGetData(params.id);
const options = metricsQueryKeys.metricsGetData(id);
const existingData = queryClient.getQueryData(options.queryKey);
if (!existingData) {
await queryClient.prefetchQuery({
...options,
queryFn: () => getMetricData(params)
queryFn: () => getMetricData({ id })
});
}
};
@ -156,49 +162,6 @@ export const useDeleteMetric = () => {
});
};
export const useMetricIndividual = ({ metricId }: { metricId: string }) => {
const {
data: metric,
isFetched: isMetricFetched,
error: metricError,
refetch: refetchMetric
} = useGetMetric(metricId);
const {
data: metricData,
isFetched: isFetchedMetricData,
refetch: refetchMetricData,
dataUpdatedAt: metricDataUpdatedAt,
error: metricDataError
} = useGetMetricData({ id: metricId });
return useMemo(
() => ({
metric: resolveEmptyMetric(metric, metricId),
isMetricFetched,
refetchMetric,
metricError,
metricData,
isFetchedMetricData,
refetchMetricData,
metricDataUpdatedAt,
metricDataError
}),
[
metric,
metricId,
isMetricFetched,
refetchMetric,
metricError,
metricData,
isFetchedMetricData,
refetchMetricData,
metricDataUpdatedAt,
metricDataError
]
);
};
export const useSaveMetricToCollection = () => {
const { data: userFavorites, refetch: refreshFavoritesList } = useGetUserFavorites();
const { mutateAsync: saveMetric } = useSaveMetric();

View File

@ -1,11 +1,8 @@
'use client';
import { IBusterMetric } from '@/api/asset_interfaces';
import { queryKeys } from '@/api/query_keys';
import { useMemoizedFn, useDebounceFn } from '@/hooks';
import { prepareMetricUpdateMetric } from '@/lib/metrics';
import { useQueryClient, useMutation } from '@tanstack/react-query';
import { useTransition, useMemo } from 'react';
import { useSaveMetric } from './queryRequests';
import { create } from 'mutative';
/**
@ -14,11 +11,19 @@ import { create } from 'mutative';
* It will also strip out any values that are not changed from the DEFAULT_CHART_CONFIG.
* It will also update the draft_session_id if it exists.
*/
export const useUpdateMetric = (params?: { wait?: number }) => {
const [isPending, startTransition] = useTransition();
export const useUpdateMetric = () => {
const queryClient = useQueryClient();
const { mutateAsync: saveMetric } = useSaveMetric();
const waitTime = params?.wait || 0;
const { run: saveMetricDebounced } = useDebounceFn(
(newMetric: IBusterMetric, prevMetric: IBusterMetric) => {
const changedValues = prepareMetricUpdateMetric(newMetric, prevMetric);
if (changedValues) {
saveMetric(changedValues);
}
},
{ wait: 650 }
);
const combineAndSaveMetric = useMemoizedFn(
async (newMetricPartial: Partial<IBusterMetric> & { id: string }) => {
@ -41,12 +46,7 @@ export const useUpdateMetric = (params?: { wait?: number }) => {
async (newMetricPartial: Partial<IBusterMetric> & { id: string }) => {
const { newMetric, prevMetric } = await combineAndSaveMetric(newMetricPartial);
if (newMetric && prevMetric) {
startTransition(() => {
const changedValues = prepareMetricUpdateMetric(newMetric, prevMetric);
if (changedValues) {
saveMetric(changedValues);
}
});
saveMetricDebounced(newMetric, prevMetric);
}
return Promise.resolve(newMetric!);
}
@ -56,13 +56,5 @@ export const useUpdateMetric = (params?: { wait?: number }) => {
mutationFn: mutationFn
});
const { run: mutateDebounced } = useDebounceFn(mutationRes.mutateAsync, { wait: waitTime });
return useMemo(
() => ({
...mutationRes,
mutateDebounced
}),
[mutationRes, mutateDebounced]
);
return mutationRes;
};

View File

@ -21,8 +21,16 @@ export const getMetric_server = async ({ id, password }: GetMetricParams) => {
});
};
export const getMetricData = async ({ id }: { id: string }) => {
return mainApi.get<BusterMetricData>(`/metrics/${id}/data`).then((res) => res.data);
export const getMetricData = async ({
id,
version_number
}: {
id: string;
version_number?: number;
}) => {
return mainApi
.get<BusterMetricData>(`/metrics/${id}/data`, { params: { version_number } })
.then((res) => res.data);
};
export const listMetrics = async (params: ListMetricsParams) => {

View File

@ -6,7 +6,7 @@ import { MetricViewChart } from '@/controllers/MetricController/MetricViewChart/
export default function EmbedMetricsPage({ params }: { params: { metricId: string } }) {
const { metricId } = params;
const { isFetched } = useGetMetric(metricId);
const { isFetched } = useGetMetric({ id: metricId });
if (!isFetched) {
return <CircleSpinnerLoaderContainer className="min-h-screen" />;

View File

@ -6,7 +6,7 @@ import { useMemoizedFn } from '@/hooks';
import { BusterRoutes, createBusterRoute } from '@/routes';
import { useBusterNotifications } from '@/context/BusterNotifications';
import { ShareMenuContentEmbedFooter } from './ShareMenuContentEmbed';
import { isEffectiveOwner } from '@/lib/share';
import { getIsEffectiveOwner } from '@/lib/share';
export const ShareMenuContent: React.FC<{
shareAssetConfig: BusterShare;
@ -21,7 +21,7 @@ export const ShareMenuContent: React.FC<{
const permission = shareAssetConfig?.permission;
const publicly_accessible = shareAssetConfig?.publicly_accessible;
const isOwner = isEffectiveOwner(permission);
const isOwner = getIsEffectiveOwner(permission);
const onCopyLink = useMemoizedFn(() => {
let url = '';

View File

@ -29,7 +29,9 @@ export const ShareWithGroupAndTeam: React.FC<{
const { data: collection } = useGetCollection(
assetType === ShareAssetType.COLLECTION ? assetId : undefined
);
const { data: metric } = useGetMetric(assetType === ShareAssetType.METRIC ? assetId : undefined);
const { data: metric } = useGetMetric(
assetType === ShareAssetType.METRIC ? { id: assetId } : { id: undefined }
);
const onUpdateShareRole = useMemoizedFn(
async ({ teamId, role }: { teamId: string; role: ShareRole | null }) => {

View File

@ -3,9 +3,10 @@ import { ShareButton } from './ShareButton';
import { ShareMenu } from '../ShareMenu';
import { ShareAssetType } from '@/api/asset_interfaces';
import { useGetDashboard } from '@/api/buster_rest/dashboards';
import { getShareAssetConfig } from '../ShareMenu/helpers';
export const ShareDashboardButton = React.memo(({ dashboardId }: { dashboardId: string }) => {
const { data: dashboardResponse } = useGetDashboard(dashboardId);
const { data: dashboardResponse } = useGetDashboard(dashboardId, getShareAssetConfig);
return (
<ShareMenu

View File

@ -6,7 +6,7 @@ import { useGetMetric } from '@/api/buster_rest/metrics';
import { getShareAssetConfig } from '../ShareMenu/helpers';
export const ShareMetricButton = React.memo(({ metricId }: { metricId: string }) => {
const { data: shareAssetConfig } = useGetMetric(metricId, getShareAssetConfig);
const { data: shareAssetConfig } = useGetMetric({ id: metricId }, getShareAssetConfig);
return (
<ShareMenu

View File

@ -12,7 +12,7 @@ import { useParams } from 'next/navigation';
export const useUpdateMetricChart = (props?: { metricId?: string }) => {
const params = useParams<{ metricId?: string }>();
const metricId = props?.metricId ?? params.metricId ?? '';
const { mutateDebounced: onUpdateMetricDebounced } = useUpdateMetric({ wait: 600 });
const { mutate: onUpdateMetricDebounced } = useUpdateMetric();
const getMetricMemoized = useGetMetricMemoized();
const onUpdateMetricChartConfig = useMemoizedFn(

View File

@ -34,21 +34,21 @@ const DashboardMetricItemBase: React.FC<{
} = useDashboardMetric({ metricId });
const loadingMetricData = !!metric && !isFetchedMetricData;
const chartOptions = metric.chart_config;
const chartOptions = metric?.chart_config;
const data = metricData?.data || null;
const loading = loadingMetricData;
const dataLength = metricData?.data?.length || 1;
const animate =
!initialAnimationEnded && !isDragOverlay && dataLength < 125 && numberOfMetrics <= 30;
const isTable = metric.chart_config.selectedChartType === 'table';
const isTable = metric?.chart_config.selectedChartType === 'table';
const error = useMemo(() => {
if (metric.error) {
if (metric?.error) {
return metric.error;
}
return undefined;
}, [metric.error, metric.code]);
}, [metric?.error]);
const metricLink = useMemo(() => {
return createBusterRoute({
@ -62,12 +62,7 @@ const DashboardMetricItemBase: React.FC<{
setInitialAnimationEnded(metricId);
});
// const cardClassNamesMemoized = useMemo(() => {
// return {
// body: `h-full w-full overflow-hidden ${isTable ? 'p-0!' : 'px-2! pt-2! pb-0.5!'} relative`,
// header: cx(`p-0! min-h-[52px]! mb-0!`, styles.cardTitle)
// };
// }, [isTable]);
if (!chartOptions) return null;
return (
<Card
@ -75,14 +70,14 @@ const DashboardMetricItemBase: React.FC<{
className={`metric-item flex h-full w-full flex-col overflow-auto ${className}`}>
<CardHeader size="small" className="hover:bg-item-hover border-b">
<MetricTitle
title={metric.title}
timeFrame={metric.time_frame}
title={metric?.title || ''}
timeFrame={metric?.time_frame}
metricLink={metricLink}
isDragOverlay={isDragOverlay}
metricId={metricId}
dashboardId={dashboardId}
readOnly={readOnly}
description={metric.description}
description={metric?.description}
/>
</CardHeader>

View File

@ -1,13 +1,15 @@
import { useDashboardContentControllerContextSelector } from '../DashboardContentControllerContext';
import { useEffect, useMemo, useRef } from 'react';
import { useInViewport } from '@/hooks';
import { useMetricIndividual } from '@/api/buster_rest/metrics';
import { useGetMetric, useGetMetricData } from '@/api/buster_rest/metrics';
export const useDashboardMetric = ({ metricId }: { metricId: string }) => {
const { metric, metricData, isMetricFetched, metricDataUpdatedAt, isFetchedMetricData } =
useMetricIndividual({
metricId
});
const { data: metric, isFetched: isMetricFetched } = useGetMetric({ id: metricId });
const {
data: metricData,
isFetched: isFetchedMetricData,
dataUpdatedAt: metricDataUpdatedAt
} = useGetMetricData({ id: metricId });
const dashboard = useDashboardContentControllerContextSelector(({ dashboard }) => dashboard);
const metricMetadata = useDashboardContentControllerContextSelector(
({ metricMetadata }) => metricMetadata[metricId]

View File

@ -6,22 +6,30 @@ import {
useChatLayoutContextSelector
} from '@/layouts/ChatLayout/ChatLayoutContext';
import { MetricViewComponents } from './config';
import { useMetricIndividual } from '@/api/buster_rest/metrics';
import { FileIndeterminateLoader } from '@/components/features/FileIndeterminateLoader';
import { useMount } from '@/hooks';
import { useGetMetric, useGetMetricData } from '@/api/buster_rest/metrics';
export const MetricController: React.FC<{
metricId: string;
}> = React.memo(({ metricId }) => {
const { isMetricFetched, isFetchedMetricData } = useMetricIndividual({ metricId });
const { isFetched: isMetricFetched } = useGetMetric({ id: metricId });
const { isFetched: isMetricDataFetched } = useGetMetricData({ id: metricId });
const selectedFileView = useChatLayoutContextSelector((x) => x.selectedFileView) || 'chart';
const showLoader = !isMetricFetched || !isFetchedMetricData;
const showLoader = !isMetricFetched || !isMetricDataFetched;
const Component =
selectedFileView in MetricViewComponents
? MetricViewComponents[selectedFileView as MetricFileView]
: () => <></>;
console.log('here', metricId);
useMount(() => {
console.log('mounted', metricId);
});
return (
<>
{showLoader && <FileIndeterminateLoader />}

View File

@ -9,8 +9,8 @@ import { Popover } from '@/components/ui/tooltip/Popover';
import { Button, type ButtonProps } from '@/components/ui/buttons';
export const MetricChartEvaluation: React.FC<{
evaluationScore: IBusterMetric['evaluation_score'];
evaluationSummary: string;
evaluationScore: IBusterMetric['evaluation_score'] | undefined;
evaluationSummary: string | undefined;
}> = React.memo(({ evaluationScore, evaluationSummary }) => {
const text = useMemo(() => {
if (evaluationScore === 'High') return 'High confidence';

View File

@ -10,7 +10,7 @@ import {
ChartType,
ScatterAxis
} from '@/api/asset_interfaces/metric/charts';
import { useMetricIndividual } from '@/api/buster_rest/metrics';
import { useGetMetric, useGetMetricData } from '@/api/buster_rest/metrics';
export const MetricStylingApp: React.FC<{
metricId: string;
@ -18,7 +18,8 @@ export const MetricStylingApp: React.FC<{
const [segment, setSegment] = useState<MetricStylingAppSegments>(
MetricStylingAppSegments.VISUALIZE
);
const { metric, metricData } = useMetricIndividual({ metricId });
const { data: metric } = useGetMetric({ id: metricId });
const { data: metricData } = useGetMetricData({ id: metricId });
if (!metric) return null;

View File

@ -56,6 +56,7 @@ export const SelectChartType: React.FC<SelectChartTypeProps> = ({
const onSelectChartType = useMemoizedFn((chartIconType: ChartIconType) => {
const chartConfig = selectedChartTypeMethod(chartIconType, columnSettings);
console.log('chartConfig', chartConfig);
onUpdateMetricChartConfig({ chartConfig });
});

View File

@ -1,14 +1,13 @@
import React, { useMemo } from 'react';
import { MetricViewChartContent } from './MetricViewChartContent';
import { MetricViewChartHeader } from './MetricViewChartHeader';
import { useMetricIndividual, useUpdateMetric } from '@/api/buster_rest/metrics';
import { useGetMetric, useGetMetricData, useUpdateMetric } from '@/api/buster_rest/metrics';
import { useMemoizedFn } from '@/hooks';
import { inputHasText } from '@/lib/text';
import { MetricChartEvaluation } from './MetricChartEvaluation';
import { ChartType } from '@/api/asset_interfaces/metric/charts/enum';
import { AnimatePresence, motion } from 'framer-motion';
import { cn } from '@/lib/classMerge';
import { ShareRole } from '@/api/asset_interfaces';
import { canEdit } from '@/lib/share';
export const MetricViewChart: React.FC<{
@ -18,14 +17,19 @@ export const MetricViewChart: React.FC<{
cardClassName?: string;
}> = React.memo(
({ metricId, readOnly: readOnlyProp = false, className = '', cardClassName = '' }) => {
const { metric, metricData, metricDataError, isFetchedMetricData } = useMetricIndividual({
metricId
});
const { mutateAsync: updateMetric } = useUpdateMetric();
const { title, description, time_frame, evaluation_score, evaluation_summary } = metric;
const isTable = metric.chart_config.selectedChartType === ChartType.Table;
const { data: metric, isFetched: isMetricFetched } = useGetMetric({ id: metricId });
const {
data: metricData,
isFetched: isFetchedMetricData,
const readOnly = readOnlyProp || !canEdit(metric.permission);
error: metricDataError
} = useGetMetricData({ id: metricId });
const { mutateAsync: updateMetric } = useUpdateMetric();
const { title, description, time_frame, evaluation_score, evaluation_summary } = metric || {};
const isTable = metric?.chart_config.selectedChartType === ChartType.Table;
const readOnly = readOnlyProp || !canEdit(metric?.permission);
const loadingData = !isFetchedMetricData;
const errorData = !!metricDataError;
@ -40,6 +44,8 @@ export const MetricViewChart: React.FC<{
}
});
if (!metric) return null;
return (
<div className={cn('flex h-full flex-col justify-between space-y-3.5 p-5', className)}>
<MetricViewChartCard

View File

@ -4,14 +4,14 @@ import { CodeCard } from '@/components/ui/card';
import { useMemoizedFn } from '@/hooks';
import { SaveResetFilePopup } from '@/components/features/popups/SaveResetFilePopup';
import { useBusterNotifications } from '@/context/BusterNotifications';
import { useMetricIndividual, useUpdateMetric } from '@/api/buster_rest/metrics';
import { useGetMetric, useUpdateMetric } from '@/api/buster_rest/metrics';
export const MetricViewFile: React.FC<MetricViewProps> = React.memo(({ metricId }) => {
const { metric } = useMetricIndividual({ metricId });
const { data: metric } = useGetMetric({ id: metricId });
const { openSuccessMessage } = useBusterNotifications();
const { mutateAsync: updateMetric } = useUpdateMetric();
const { file: fileProp, file_name } = metric;
const { file: fileProp, file_name } = metric || {};
const [file, setFile] = React.useState(fileProp);
@ -36,9 +36,9 @@ export const MetricViewFile: React.FC<MetricViewProps> = React.memo(({ metricId
return (
<div className="relative h-full overflow-hidden p-5">
<CodeCard
code={file}
code={file || ''}
language="yaml"
fileName={file_name}
fileName={file_name || ''}
onChange={setFile}
onMetaEnter={onSaveFile}
/>

View File

@ -1,6 +1,5 @@
import React, { useEffect, useMemo } from 'react';
import type { MetricViewProps } from '../config';
import { useMetricIndividual } from '@/api/buster_rest/metrics';
import { useMemoizedFn, useUnmount } from '@/hooks';
import { IDataResult } from '@/api/asset_interfaces';
import { useMetricLayout } from '../useMetricLayout';
@ -8,6 +7,7 @@ import { useChatLayoutContextSelector } from '@/layouts/ChatLayout/ChatLayoutCon
import { AppSplitterRef } from '@/components/ui/layouts';
import { AppVerticalCodeSplitter } from '@/components/features/layouts/AppVerticalCodeSplitter';
import { useMetricRunSQL } from './useMetricRunSQL';
import { useGetMetric, useGetMetricData } from '@/api/buster_rest/metrics';
const autoSaveId = 'metric-view-results';
@ -21,17 +21,18 @@ export const MetricViewResults: React.FC<MetricViewProps> = React.memo(({ metric
const { runSQL, resetRunSQLData, saveSQL, warnBeforeNavigating, setWarnBeforeNavigating } =
useMetricRunSQL();
const { metric, metricData } = useMetricIndividual({ metricId });
const { data: metric } = useGetMetric({ id: metricId });
const { data: metricData } = useGetMetricData({ id: metricId });
const [sql, setSQL] = React.useState(metric.code || '');
const [sql, setSQL] = React.useState(metric?.sql || '');
const [fetchingData, setFetchingData] = React.useState(false);
const dataSourceId = metric?.data_source_id;
const dataSourceId = metric?.data_source_id || '';
const data: IDataResult = metricData?.dataFromRerun || metricData?.data || null;
const disableSave = useMemo(() => {
return !sql || fetchingData || sql === metric.code;
}, [sql, fetchingData, metric.code]);
return !sql || fetchingData || sql === metric?.sql;
}, [sql, fetchingData, metric?.sql]);
const onRunQuery = useMemoizedFn(async () => {
setFetchingData(true);
@ -71,10 +72,10 @@ export const MetricViewResults: React.FC<MetricViewProps> = React.memo(({ metric
});
useEffect(() => {
if (metric.code) {
setSQL(metric.code);
if (metric?.sql) {
setSQL(metric.sql);
}
}, [metric.code]);
}, [metric?.sql]);
useUnmount(() => {
resetRunSQLData({ metricId });

View File

@ -29,7 +29,7 @@ export const useMetricRunSQL = () => {
const originalConfigs = useRef<{
chartConfig: IBusterMetricChartConfig;
code: string;
sql: string;
data: BusterMetricData['data'];
dataMetadata: BusterMetricData['data_metadata'];
} | null>(null);
@ -43,7 +43,6 @@ export const useMetricRunSQL = () => {
metricId: string;
data: BusterMetricData['data'];
data_metadata: BusterMetricData['data_metadata'];
code: string;
isDataFromRerun: boolean;
}) => {
const options = queryKeys.metricsGetData(metricId);
@ -65,7 +64,7 @@ export const useMetricRunSQL = () => {
if (!originalConfigs.current) {
originalConfigs.current = {
chartConfig: metricMessage?.chart_config!,
code: metricMessage?.code!,
sql: metricMessage?.sql!,
data: currentMessageData?.data!,
dataMetadata: currentMessageData?.data_metadata!
};
@ -84,8 +83,7 @@ export const useMetricRunSQL = () => {
metricId,
data,
isDataFromRerun: true,
data_metadata,
code: sql
data_metadata
});
updateMetricMutation({
id: metricId,
@ -128,7 +126,6 @@ export const useMetricRunSQL = () => {
metricId,
data: originalConfigs.current?.data!,
data_metadata: originalConfigs.current?.dataMetadata!,
code: originalConfigs.current?.code!,
isDataFromRerun: false
});
originalConfigs.current = null;
@ -148,7 +145,7 @@ export const useMetricRunSQL = () => {
const currentMetric = getMetricMemoized(metricId);
const dataSourceId = dataSourceIdProp || currentMetric?.data_source_id;
if ((!ogConfigs || ogConfigs.code !== sql) && dataSourceId) {
if ((!ogConfigs || ogConfigs.sql !== sql) && dataSourceId) {
try {
await runSQL({
metricId,
@ -173,7 +170,6 @@ export const useMetricRunSQL = () => {
metricId,
data: originalConfigs.current?.data!,
data_metadata: originalConfigs.current?.dataMetadata!,
code: originalConfigs.current?.code!,
isDataFromRerun: false
});
}

View File

@ -15,7 +15,6 @@ interface ChatSplitterProps {
export const ChatLayout: React.FC<ChatSplitterProps> = ({ children }) => {
const appSplitterRef = useRef<AppSplitterRef>(null);
const useChatLayoutProps = useChatLayout({ appSplitterRef });
const { renderViewLayoutKey, selectedLayout, selectedFile, onSetSelectedFile, chatId } =
useChatLayoutProps;

View File

@ -13,6 +13,23 @@ export const useChatFileLayout = ({
}) => {
const [fileViews, setFileViews] = useState<Record<string, FileConfig>>({});
const selectedFileView: FileView | undefined = useMemo(() => {
if (!selectedFileId) return undefined;
return (
fileViews[selectedFileId]?.selectedFileView || defaultFileView[selectedFileType as FileType]
);
}, [fileViews, selectedFileId, selectedFileType]);
const selectedFileViewConfig: FileViewConfig | undefined = useMemo(() => {
if (!selectedFileId) return undefined;
return fileViews[selectedFileId]?.fileViewConfig;
}, [fileViews, selectedFileId]);
const selectedFileViewSecondary: FileViewSecondary | null = useMemo(() => {
if (!selectedFileId || !selectedFileViewConfig || !selectedFileView) return null;
return selectedFileViewConfig?.[selectedFileView]?.secondaryView ?? null;
}, [selectedFileViewConfig, selectedFileId, selectedFileView]);
const onSetFileView = useMemoizedFn(
({
fileView,
@ -51,23 +68,6 @@ export const useChatFileLayout = ({
}
);
const selectedFileView: FileView | undefined = useMemo(() => {
if (!selectedFileId) return undefined;
return (
fileViews[selectedFileId]?.selectedFileView || defaultFileView[selectedFileType as FileType]
);
}, [fileViews, selectedFileId, selectedFileType]);
const selectedFileViewConfig: FileViewConfig | undefined = useMemo(() => {
if (!selectedFileId) return undefined;
return fileViews[selectedFileId]?.fileViewConfig;
}, [fileViews, selectedFileId]);
const selectedFileViewSecondary: FileViewSecondary | null = useMemo(() => {
if (!selectedFileId || !selectedFileViewConfig || !selectedFileView) return null;
return selectedFileViewConfig?.[selectedFileView]?.secondaryView ?? null;
}, [selectedFileViewConfig, selectedFileId, selectedFileView]);
const closeSecondaryView = useMemoizedFn(() => {
if (!selectedFileId || !selectedFileViewConfig || !selectedFileView) return;
setFileViews((prev) => {

View File

@ -24,14 +24,15 @@ import { Button } from '@/components/ui/buttons';
import React from 'react';
import { timeFromNow } from '@/lib/date';
import { ASSET_ICONS } from '@/components/features/config/assetIcons';
import { useMemoizedFn, useWhyDidYouUpdate } from '@/hooks';
import { useMemoizedFn } from '@/hooks';
import { useSaveToCollectionsDropdownContent } from '@/components/features/dropdowns/SaveToCollectionsDropdown';
import { ShareAssetType, ShareRole } from '@/api/asset_interfaces/share';
import { ShareAssetType } from '@/api/asset_interfaces/share';
import { useFavoriteStar } from '@/components/features/list/FavoriteStar';
import { timeout } from '@/lib';
import { ShareMenuContent } from '@/components/features/ShareMenu/ShareMenuContent';
import { DASHBOARD_TITLE_INPUT_ID } from '@/controllers/DashboardController/DashboardViewDashboardController/DashboardEditTitle';
import { isEffectiveOwner } from '@/lib/share';
import { canEdit, canFilter, getIsEffectiveOwner } from '@/lib/share';
import { getShareAssetConfig } from '@/components/features/ShareMenu/helpers';
export const DashboardThreeDotMenu = React.memo(({ dashboardId }: { dashboardId: string }) => {
const versionHistoryItems = useVersionHistorySelectMenu({ dashboardId });
@ -42,19 +43,23 @@ export const DashboardThreeDotMenu = React.memo(({ dashboardId }: { dashboardId:
const shareMenu = useShareMenuSelectMenu({ dashboardId });
const addContentToDashboardMenu = useAddContentToDashboardSelectMenu();
const filterDashboardMenu = useFilterDashboardSelectMenu();
const { data: permission } = useGetDashboard(dashboardId, (x) => x.permission);
const isOwner = getIsEffectiveOwner(permission);
const isFilter = canFilter(permission);
const isEditor = canEdit(permission);
const items: DropdownItems = useMemo(
() => [
filterDashboardMenu,
addContentToDashboardMenu,
isFilter && filterDashboardMenu,
isEditor && addContentToDashboardMenu,
{ type: 'divider' },
shareMenu,
isOwner && shareMenu,
collectionSelectMenu,
favoriteDashboard,
versionHistoryItems,
{ type: 'divider' },
renameDashboardMenu,
deleteDashboardMenu
isEditor && renameDashboardMenu,
isOwner && deleteDashboardMenu
],
[
filterDashboardMenu,
@ -63,7 +68,8 @@ export const DashboardThreeDotMenu = React.memo(({ dashboardId }: { dashboardId:
collectionSelectMenu,
favoriteDashboard,
versionHistoryItems,
renameDashboardMenu
renameDashboardMenu,
deleteDashboardMenu
]
);
@ -211,8 +217,8 @@ const useRenameDashboardSelectMenu = ({ dashboardId }: { dashboardId: string })
};
export const useShareMenuSelectMenu = ({ dashboardId }: { dashboardId: string }) => {
const { data: dashboard } = useGetDashboard(dashboardId);
const isOwner = isEffectiveOwner(dashboard?.permission);
const { data: dashboard } = useGetDashboard(dashboardId, getShareAssetConfig);
const isOwner = getIsEffectiveOwner(dashboard?.permission);
return useMemo(
() => ({

View File

@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React from 'react';
import { FileContainerButtonsProps } from '../interfaces';
import { MetricFileViewSecondary, useChatLayoutContextSelector } from '../../../ChatLayoutContext';
import { useMemoizedFn } from '@/hooks';
@ -18,7 +18,7 @@ export const MetricContainerHeaderButtons: React.FC<FileContainerButtonsProps> =
const renderViewLayoutKey = useChatLayoutContextSelector((x) => x.renderViewLayoutKey);
const selectedFileId = useChatIndividualContextSelector((x) => x.selectedFileId)!;
const metricId = selectedFileId;
const { isFetched: isMetricFetched } = useGetMetric(metricId);
const { isFetched: isMetricFetched } = useGetMetric({ id: metricId });
if (!isMetricFetched) return null;

View File

@ -45,10 +45,12 @@ import { METRIC_CHART_CONTAINER_ID } from '@/controllers/MetricController/Metric
import { timeout } from '@/lib';
import { METRIC_CHART_TITLE_INPUT_ID } from '@/controllers/MetricController/MetricViewChart/MetricViewChartHeader';
import { ShareMenuContent } from '@/components/features/ShareMenu/ShareMenuContent';
import { isEffectiveOwner } from '@/lib/share';
import { canEdit, getIsEffectiveOwner, getIsOwner } from '@/lib/share';
import { getShareAssetConfig } from '@/components/features/ShareMenu/helpers';
export const ThreeDotMenuButton = React.memo(({ metricId }: { metricId: string }) => {
const { openSuccessMessage } = useBusterNotifications();
const { data: permission } = useGetMetric({ id: metricId }, (x) => x.permission);
const onSetSelectedFile = useChatLayoutContextSelector((x) => x.onSetSelectedFile);
const dashboardSelectMenu = useDashboardSelectMenu({ metricId });
const versionHistoryItems = useVersionHistorySelectMenu({ metricId });
@ -64,27 +66,35 @@ export const ThreeDotMenuButton = React.memo(({ metricId }: { metricId: string }
const renameMetricMenu = useRenameMetricSelectMenu({ metricId });
const shareMenu = useShareMenuSelectMenu({ metricId });
const isEditor = canEdit(permission);
const isOwnerEffective = getIsEffectiveOwner(permission);
const isOwner = getIsOwner(permission);
const items: DropdownItems = useMemo(
() => [
shareMenu,
statusSelectMenu,
{ type: 'divider' },
dashboardSelectMenu,
collectionSelectMenu,
favoriteMetric,
{ type: 'divider' },
editChartMenu,
resultsViewMenu,
sqlEditorMenu,
versionHistoryItems,
{ type: 'divider' },
downloadCSVMenu,
downloadPNGMenu,
{ type: 'divider' },
renameMetricMenu,
deleteMetricMenu
],
() =>
[
isOwnerEffective && shareMenu,
isEditor && statusSelectMenu,
{ type: 'divider' },
dashboardSelectMenu,
collectionSelectMenu,
favoriteMetric,
{ type: 'divider' },
isEditor && editChartMenu,
resultsViewMenu,
sqlEditorMenu,
isEditor && versionHistoryItems,
{ type: 'divider' },
downloadCSVMenu,
downloadPNGMenu,
{ type: 'divider' },
isEditor && renameMetricMenu,
isOwner && deleteMetricMenu
].filter(Boolean) as DropdownItems,
[
isEditor,
isOwner,
isOwnerEffective,
renameMetricMenu,
dashboardSelectMenu,
deleteMetricMenu,
@ -115,7 +125,7 @@ ThreeDotMenuButton.displayName = 'ThreeDotMenuButton';
const useDashboardSelectMenu = ({ metricId }: { metricId: string }) => {
const { mutateAsync: saveMetricToDashboard } = useSaveMetricToDashboard();
const { mutateAsync: removeMetricFromDashboard } = useRemoveMetricFromDashboard();
const { data: dashboards } = useGetMetric(metricId, (x) => x.dashboards);
const { data: dashboards } = useGetMetric({ id: metricId }, (x) => x.dashboards);
const onSaveToDashboard = useMemoizedFn(async (dashboardIds: string[]) => {
await saveMetricToDashboard({ metricId, dashboardIds });
@ -156,7 +166,7 @@ const useDashboardSelectMenu = ({ metricId }: { metricId: string }) => {
};
const useVersionHistorySelectMenu = ({ metricId }: { metricId: string }) => {
const { data } = useGetMetric(metricId, (x) => ({
const { data } = useGetMetric({ id: metricId }, (x) => ({
versions: x.versions,
version_number: x.version_number
}));
@ -185,7 +195,7 @@ const useVersionHistorySelectMenu = ({ metricId }: { metricId: string }) => {
const useCollectionSelectMenu = ({ metricId }: { metricId: string }) => {
const { mutateAsync: saveMetricToCollection } = useSaveMetricToCollection();
const { mutateAsync: removeMetricFromCollection } = useRemoveMetricFromCollection();
const { data: collections } = useGetMetric(metricId, (x) => x.collections);
const { data: collections } = useGetMetric({ id: metricId }, (x) => x.collections);
const { openInfoMessage } = useBusterNotifications();
const selectedCollections = useMemo(() => {
@ -233,7 +243,7 @@ const useCollectionSelectMenu = ({ metricId }: { metricId: string }) => {
};
const useStatusSelectMenu = ({ metricId }: { metricId: string }) => {
const { data: metric } = useGetMetric(metricId, (x) => x);
const { data: metric } = useGetMetric({ id: metricId }, (x) => x);
const { mutateAsync: updateMetric } = useUpdateMetric();
const onChangeStatus = useMemoizedFn(async (status: VerificationStatus) => {
@ -264,7 +274,7 @@ const useStatusSelectMenu = ({ metricId }: { metricId: string }) => {
};
const useFavoriteMetricSelectMenu = ({ metricId }: { metricId: string }) => {
const { data: title } = useGetMetric(metricId, (x) => x.title);
const { data: title } = useGetMetric({ id: metricId }, (x) => x.title);
const { isFavorited, onFavoriteClick } = useFavoriteStar({
id: metricId,
type: ShareAssetType.METRIC,
@ -341,7 +351,7 @@ const useSQLEditorSelectMenu = () => {
const useDownloadCSVSelectMenu = ({ metricId }: { metricId: string }) => {
const [isDownloading, setIsDownloading] = useState(false);
const { data: metricData } = useGetMetricData({ id: metricId });
const { data: title } = useGetMetric(metricId, (x) => x.title);
const { data: title } = useGetMetric({ id: metricId }, (x) => x.title);
return useMemo(
() => ({
@ -363,10 +373,10 @@ const useDownloadCSVSelectMenu = ({ metricId }: { metricId: string }) => {
};
const useDownloadPNGSelectMenu = ({ metricId }: { metricId: string }) => {
const { openSuccessMessage, openErrorMessage } = useBusterNotifications();
const { data: title } = useGetMetric(metricId, (x) => x.title);
const { openErrorMessage } = useBusterNotifications();
const { data: title } = useGetMetric({ id: metricId }, (x) => x.title);
const { data: selectedChartType } = useGetMetric(
metricId,
{ id: metricId },
(x) => x.chart_config?.selectedChartType
);
@ -433,25 +443,22 @@ const useRenameMetricSelectMenu = ({ metricId }: { metricId: string }) => {
};
export const useShareMenuSelectMenu = ({ metricId }: { metricId: string }) => {
const { data: metric } = useGetMetric(metricId);
const isOwner = isEffectiveOwner(metric?.permission);
const { data: metric } = useGetMetric({ id: metricId }, getShareAssetConfig);
return useMemo(
() => ({
label: 'Share metric',
value: 'share-metric',
icon: <ShareRight />,
disabled: !isOwner,
items: isOwner
? [
<ShareMenuContent
key={metricId}
shareAssetConfig={metric!}
assetId={metricId}
assetType={ShareAssetType.METRIC}
/>
]
: undefined
items: (
<ShareMenuContent
key={metricId}
shareAssetConfig={metric!}
assetId={metricId}
assetType={ShareAssetType.METRIC}
/>
)
}),
[metricId]
);

View File

@ -4,7 +4,7 @@ import { useMemo, useState, useTransition } from 'react';
import type { ChatLayoutView, SelectedFile } from '../../interfaces';
import { usePathname } from 'next/navigation';
import { parsePathnameSegments } from './parsePathnameSegments';
import { useMemoizedFn } from '@/hooks';
import { useMemoizedFn, useWhyDidYouUpdate } from '@/hooks';
import { createChatAssetRoute, createChatRoute } from '../../ChatLayoutContext/helpers';
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
import { initializeSelectedFile } from './initializeSelectedFile';
@ -67,21 +67,23 @@ export const useSelectedFileAndLayout = ({
setRenderViewLayoutKey('both');
setSelectedFile(file);
await onChangePage(route);
startTransition(() => {
onChangePage(route); //this is hack for now...
animateOpenSplitter(isSameAsCurrentFile ? 'left' : 'both');
});
});
return {
onSetSelectedFile,
selectedFile,
selectedLayout,
chatId,
renderViewLayoutKey,
setRenderViewLayoutKey
};
return useMemo(
() => ({
onSetSelectedFile,
selectedFile,
selectedLayout,
chatId,
renderViewLayoutKey,
setRenderViewLayoutKey
}),
[onSetSelectedFile, selectedFile, selectedLayout, chatId, renderViewLayoutKey]
);
};
export type SelectedFileParams = ReturnType<typeof useSelectedFileAndLayout>;

View File

@ -22,7 +22,7 @@ const DEFAULT_COLUMN_SETTINGS_ENTRIES = Object.entries(DEFAULT_COLUMN_SETTINGS);
const DEFAULT_COLUMN_LABEL_FORMATS_ENTRIES = Object.entries(DEFAULT_COLUMN_LABEL_FORMAT);
const getChangedTopLevelMessageValues = (newMetric: IBusterMetric, oldMetric: IBusterMetric) => {
return getChangedValues(oldMetric, newMetric, ['title', 'feedback', 'status', 'code', 'file']);
return getChangedValues(oldMetric, newMetric, ['title', 'feedback', 'status', 'sql', 'file']);
};
const keySpecificHandlers: Partial<Record<keyof IBusterMetricChartConfig, (value: any) => any>> = {

View File

@ -1,10 +1,10 @@
import { ShareRole } from '@/api/asset_interfaces';
export const isOwner = (role: ShareRole | null | undefined) => {
export const getIsOwner = (role: ShareRole | null | undefined) => {
return role === ShareRole.OWNER;
};
export const isEffectiveOwner = (role: ShareRole | null | undefined) => {
export const getIsEffectiveOwner = (role: ShareRole | null | undefined) => {
return role === ShareRole.FULL_ACCESS || role === ShareRole.OWNER;
};
@ -16,7 +16,7 @@ export const canShare = (role: ShareRole | null | undefined) => {
return role === ShareRole.FULL_ACCESS || role === ShareRole.OWNER;
};
export const canFilter = (role: ShareRole) => {
export const canFilter = (role: ShareRole | null | undefined) => {
return (
role === ShareRole.CAN_FILTER ||
role === ShareRole.FULL_ACCESS ||

View File

@ -140,7 +140,7 @@ export const createMockMetric = (id: string): IBusterMetric => {
sent_by_id: '',
sent_by_name: '',
sent_by_avatar_url: '',
code: `WITH records AS (
sql: `WITH records AS (
SELECT
response_time_id,
interaction_id,