From 46c7e3ea67bb0f2b776766c3ffd003190703c7a2 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Wed, 26 Mar 2025 09:57:12 -0600 Subject: [PATCH] better handling for versions --- .../buster_rest/dashboards/queryRequests.ts | 6 +- .../api/buster_rest/metrics/queryRequests.ts | 9 +- .../versionHistory/VersionHistoryPanel.tsx | 120 +++++++++++++----- .../FileContainer/FileContainer.tsx | 10 +- web/src/lib/date.ts | 2 +- 5 files changed, 111 insertions(+), 36 deletions(-) diff --git a/web/src/api/buster_rest/dashboards/queryRequests.ts b/web/src/api/buster_rest/dashboards/queryRequests.ts index 5f8c082d5..3a3deb32f 100644 --- a/web/src/api/buster_rest/dashboards/queryRequests.ts +++ b/web/src/api/buster_rest/dashboards/queryRequests.ts @@ -69,7 +69,10 @@ const useGetDashboardAndInitializeMetrics = () => { }; export const useGetDashboard = ( - { id, version_number: version_number_prop }: { id: string | undefined; version_number?: number }, + { + id, + version_number: version_number_prop + }: { id: string | undefined; version_number?: number | null }, select?: (data: BusterDashboardResponse) => TData ) => { const searchParams = useSearchParams(); @@ -77,6 +80,7 @@ export const useGetDashboard = ( const queryVersionNumber = searchParams.get('dashboard_version_number'); const version_number = useMemo(() => { + if (version_number_prop === null) return undefined; return version_number_prop || queryVersionNumber ? parseInt(queryVersionNumber!) : undefined; }, [version_number_prop, queryVersionNumber]); diff --git a/web/src/api/buster_rest/metrics/queryRequests.ts b/web/src/api/buster_rest/metrics/queryRequests.ts index 1e47e2218..4aecab39a 100644 --- a/web/src/api/buster_rest/metrics/queryRequests.ts +++ b/web/src/api/buster_rest/metrics/queryRequests.ts @@ -32,7 +32,13 @@ import { useSearchParams } from 'next/navigation'; * This is a hook that will use the version number from the URL params if it exists. */ export const useGetMetric = ( - { id, version_number: version_number_prop }: { id: string | undefined; version_number?: number }, + { + id, + version_number: version_number_prop + }: { + id: string | undefined; + version_number?: number | null; //if null it will not use a params from the query params + }, select?: (data: IBusterMetric) => TData ) => { const searchParams = useSearchParams(); @@ -44,6 +50,7 @@ export const useGetMetric = ( const queryClient = useQueryClient(); const version_number = useMemo(() => { + if (version_number_prop === null) return undefined; return version_number_prop || queryVersionNumber ? parseInt(queryVersionNumber!) : undefined; }, [version_number_prop, queryVersionNumber]); diff --git a/web/src/components/features/versionHistory/VersionHistoryPanel.tsx b/web/src/components/features/versionHistory/VersionHistoryPanel.tsx index 7ca5d0ca5..21c7d86a4 100644 --- a/web/src/components/features/versionHistory/VersionHistoryPanel.tsx +++ b/web/src/components/features/versionHistory/VersionHistoryPanel.tsx @@ -1,55 +1,57 @@ import { useGetDashboard } from '@/api/buster_rest/dashboards'; import { useGetMetric } from '@/api/buster_rest/metrics'; -import React from 'react'; -import last from 'lodash/last'; +import React, { useMemo } from 'react'; import { Button } from '@/components/ui/buttons'; import { Check3, Xmark } from '@/components/ui/icons'; import { Text } from '@/components/ui/typography'; import { useCloseVersionHistory } from '@/layouts/ChatLayout/FileContainer/FileContainerHeader/MetricContainerHeaderButtons/FileContainerVersionHistory'; -import { ScrollArea } from '@/components/ui/scroll-area'; import { cn } from '@/lib/classMerge'; -import Link from 'next/link'; import { useMemoizedFn } from '@/hooks'; import { useAppLayoutContextSelector } from '@/context/BusterAppLayout'; -import { formatDate, timeFromNow } from '@/lib'; +import { timeFromNow } from '@/lib'; +import { AppPageLayout } from '@/components/ui/layouts'; +import { useSearchParams } from 'next/navigation'; +import last from 'lodash/last'; export const VersionHistoryPanel = React.memo( ({ assetId, type }: { assetId: string; type: 'metric' | 'dashboard' }) => { const onChangeQueryParams = useAppLayoutContextSelector((x) => x.onChangeQueryParams); - const { data: metricVersions, isFetched: isMetricFetched } = useGetMetric( - { id: type === 'metric' ? assetId : undefined }, - (x) => ({ versions: x.versions, latestVersion: last(x.versions) }) - ); - const { data: dashboardVersions, isFetched: isDashboardFetched } = useGetDashboard( - { - id: type === 'dashboard' ? assetId : undefined - }, - (x) => ({ versions: x.dashboard.versions, latestVersion: last(x.dashboard.versions) }) - ); + const { dashboardVersions, selectedVersion: dashboardSelectedVersion } = + useListDashboardVersions({ + assetId, + type + }); + const { metricVersions, selectedVersion: metricSelectedVersion } = useListMetricVersions({ + assetId, + type + }); - const listItems = type === 'metric' ? metricVersions?.versions : dashboardVersions?.versions; - const selectedVersion = - type === 'metric' ? metricVersions?.latestVersion : dashboardVersions?.latestVersion; + const listItems = useMemo(() => { + const items = type === 'metric' ? metricVersions : dashboardVersions; + return items ? [...items].reverse() : undefined; + }, [type, dashboardVersions, metricVersions]); + + const selectedVersion = useMemo(() => { + return type === 'metric' ? metricSelectedVersion : dashboardSelectedVersion; + }, [type, dashboardSelectedVersion, metricSelectedVersion]); const onClickVersionHistory = useMemoizedFn((versionNumber: number) => { onChangeQueryParams({ metric_version_number: versionNumber.toString() }); }); return ( -
- - - + } scrollable> +
{listItems?.map((item) => ( ))} - -
+
+ ); } ); @@ -70,12 +72,14 @@ const ListItem = React.memo(
onClickVersionHistory(version_number)} className={cn( - 'hover:bg-item-hover flex cursor-pointer items-center justify-between space-x-2 px-2.5 py-1.5', + 'hover:bg-item-hover flex cursor-pointer items-center justify-between space-x-2 rounded px-2.5 py-1.5', selected && 'bg-item-select hover:bg-item-select' )}> -
+
{`Version ${version_number}`} - {timeFromNow(updated_at)} + + {timeFromNow(updated_at, false)} +
{selected && (
@@ -92,7 +96,7 @@ const PanelHeader = React.memo(() => { const removeVersionHistoryQueryParams = useCloseVersionHistory(); return ( -
+
Version History
@@ -101,3 +105,61 @@ const PanelHeader = React.memo(() => { PanelHeader.displayName = 'PanelHeader'; VersionHistoryPanel.displayName = 'VersionHistoryPanel'; + +const useListDashboardVersions = ({ + assetId, + type +}: { + assetId: string; + type: 'metric' | 'dashboard'; +}) => { + const selectedVersionParam = useSearchParams().get('dashboard_version_number'); + const { data: dashboardVersions } = useGetDashboard( + { + id: type === 'dashboard' ? assetId : undefined, + version_number: null + }, + (x) => x.dashboard.versions + ); + + const selectedVersion = useMemo(() => { + if (selectedVersionParam) return parseInt(selectedVersionParam); + return last(dashboardVersions)?.version_number; + }, [dashboardVersions, selectedVersionParam]); + + return useMemo(() => { + return { + dashboardVersions, + selectedVersion + }; + }, [dashboardVersions, selectedVersion]); +}; + +const useListMetricVersions = ({ + assetId, + type +}: { + assetId: string; + type: 'metric' | 'dashboard'; +}) => { + const selectedVersionParam = useSearchParams().get('metric_version_number'); + const { data: metricVersions } = useGetMetric( + { + id: type === 'metric' ? assetId : undefined, + version_number: null + }, + (x) => x.versions + ); + + const selectedVersion = useMemo(() => { + if (selectedVersionParam) return parseInt(selectedVersionParam); + return last(metricVersions)?.version_number; + }, [metricVersions, selectedVersionParam]); + + return useMemo(() => { + return { + metricVersions, + selectedVersion + }; + }, [metricVersions, selectedVersion]); +}; diff --git a/web/src/layouts/ChatLayout/FileContainer/FileContainer.tsx b/web/src/layouts/ChatLayout/FileContainer/FileContainer.tsx index 637cdb100..61c82c755 100644 --- a/web/src/layouts/ChatLayout/FileContainer/FileContainer.tsx +++ b/web/src/layouts/ChatLayout/FileContainer/FileContainer.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useMemo, useRef } from 'react'; +import React, { useLayoutEffect, useMemo, useRef } from 'react'; import { FileContainerHeader } from './FileContainerHeader'; import { AppPageLayout, AppSplitter, AppSplitterRef } from '@/components/ui/layouts'; import { useChatLayoutContextSelector } from '../ChatLayoutContext'; @@ -58,7 +58,7 @@ export const FileContainer: React.FC = ({ children }) => { return defaulClosedLayout; }, []); - const animateOpenSplitter = useMemoizedFn((side: 'open' | 'closed') => { + const animateOpenSplitter = useMemoizedFn(async (side: 'open' | 'closed') => { if (side === 'open') { appSplitterRef.current?.animateWidth(defaultOpenLayout[1], 'right'); } else { @@ -75,8 +75,10 @@ export const FileContainer: React.FC = ({ children }) => { ); }, [debouncedSelectedFileViewSecondary, selectedFile?.id, selectedFile?.type]); - useUpdateLayoutEffect(() => { - animateOpenSplitter(isOpenSecondary ? 'open' : 'closed'); + useLayoutEffect(() => { + setTimeout(() => { + animateOpenSplitter(isOpenSecondary ? 'open' : 'closed'); + }, 25); }, [isOpenSecondary]); return ( diff --git a/web/src/lib/date.ts b/web/src/lib/date.ts index 55edbe9e4..f2f397c41 100644 --- a/web/src/lib/date.ts +++ b/web/src/lib/date.ts @@ -207,7 +207,7 @@ export const isDateAfter = ({ return dayjs(date).isAfter(compareDate, interval); }; -export const timeFromNow = (date: string | Date, relative = true) => { +export const timeFromNow = (date: string | Date, relative = false) => { return dayjs(new Date(date)).fromNow(relative); };