mirror of https://github.com/buster-so/buster.git
update params for fetching metric data
This commit is contained in:
parent
72eafd83bd
commit
e12fe28b04
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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" />;
|
||||
|
|
|
@ -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 = '';
|
||||
|
|
|
@ -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 }) => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 />}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 });
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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(
|
||||
() => ({
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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]
|
||||
);
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>> = {
|
||||
|
|
|
@ -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 ||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue