open in metric page should collapse the menu

This commit is contained in:
Nate Kelley 2025-04-16 11:12:40 -06:00
parent 24c561a07a
commit 7f970b4e9a
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
7 changed files with 130 additions and 101 deletions

View File

@ -13,7 +13,6 @@ import { AppTooltip } from '@/components/ui/tooltip';
import Link from 'next/link'; import Link from 'next/link';
import { useGetFileLink } from '@/context/Assets/useGetFileLink'; import { useGetFileLink } from '@/context/Assets/useGetFileLink';
import { useChatLayoutContextSelector } from '@/layouts/ChatLayout'; import { useChatLayoutContextSelector } from '@/layouts/ChatLayout';
import { useRouter } from 'next/navigation';
import { useCallback } from 'react'; import { useCallback } from 'react';
export const VersionHistoryPanel = React.memo( export const VersionHistoryPanel = React.memo(

View File

@ -242,7 +242,7 @@ export const DropdownContent = <T,>({
}}> }}>
{hasShownItem ? ( {hasShownItem ? (
<> <>
{selectedItems.map((item) => { {selectedItems.map((item, index) => {
// Only increment index for selectable items // Only increment index for selectable items
if ((item as DropdownItem).value && !(item as DropdownItem).items) { if ((item as DropdownItem).value && !(item as DropdownItem).items) {
hotkeyIndex++; hotkeyIndex++;
@ -250,7 +250,7 @@ export const DropdownContent = <T,>({
return ( return (
<DropdownItemSelector <DropdownItemSelector
key={dropdownItemKey(item, hotkeyIndex)} key={dropdownItemKey(item, index)}
item={item} item={item}
index={hotkeyIndex} index={hotkeyIndex}
selectType={selectType} selectType={selectType}
@ -264,7 +264,7 @@ export const DropdownContent = <T,>({
{selectedItems.length > 0 && <DropdownMenuSeparator />} {selectedItems.length > 0 && <DropdownMenuSeparator />}
{dropdownItems.map((item) => { {dropdownItems.map((item, index) => {
// Only increment index for selectable items // Only increment index for selectable items
if ((item as DropdownItem).value && !(item as DropdownItem).items) { if ((item as DropdownItem).value && !(item as DropdownItem).items) {
hotkeyIndex++; hotkeyIndex++;
@ -272,7 +272,7 @@ export const DropdownContent = <T,>({
return ( return (
<DropdownItemSelector <DropdownItemSelector
key={dropdownItemKey(item, hotkeyIndex)} key={dropdownItemKey(item, index)}
item={item} item={item}
index={hotkeyIndex} index={hotkeyIndex}
selectType={selectType} selectType={selectType}

View File

@ -27,7 +27,6 @@ export const useAutoChangeLayout = ({
const secondaryView = useChatLayoutContextSelector((x) => x.secondaryView); const secondaryView = useChatLayoutContextSelector((x) => x.secondaryView);
const dashboardVersionNumber = useChatLayoutContextSelector((x) => x.dashboardVersionNumber); const dashboardVersionNumber = useChatLayoutContextSelector((x) => x.dashboardVersionNumber);
const metricVersionNumber = useChatLayoutContextSelector((x) => x.metricVersionNumber); const metricVersionNumber = useChatLayoutContextSelector((x) => x.metricVersionNumber);
const isVersionHistoryMode = useChatLayoutContextSelector((x) => x.isVersionHistoryMode);
const isCompletedStream = useGetChatMessage(lastMessageId, (x) => x?.isCompletedStream); const isCompletedStream = useGetChatMessage(lastMessageId, (x) => x?.isCompletedStream);
const getInitialChatFileHref = useGetInitialChatFile(); const getInitialChatFileHref = useGetInitialChatFile();

View File

@ -92,7 +92,9 @@ export const useLayoutConfig = ({
return; return;
} }
if (secondaryView) { if (!chatId) {
animateOpenSplitter('right');
} else if (secondaryView) {
animateOpenSplitter('right'); animateOpenSplitter('right');
//if the chat is open, we need to wait for the splitter to close before opening the secondary view //if the chat is open, we need to wait for the splitter to close before opening the secondary view
@ -192,7 +194,7 @@ export const useLayoutConfig = ({
fileView, fileView,
secondaryView: secondaryViewFromSelected secondaryView: secondaryViewFromSelected
}); });
}, [metricId, secondaryView, dashboardId, currentRoute]); }, [metricId, secondaryView, chatId, dashboardId, currentRoute]);
return { return {
selectedLayout, selectedLayout,

View File

@ -17,11 +17,15 @@ import { canEdit, getIsEffectiveOwner } from '@/lib/share';
import Link from 'next/link'; import Link from 'next/link';
import { assetParamsToRoute } from '@/lib/assets'; import { assetParamsToRoute } from '@/lib/assets';
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout'; import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
import { useIsMetricReadOnly } from '@/context/Metrics/useIsMetricReadOnly';
export const MetricContainerHeaderButtons: React.FC<FileContainerButtonsProps> = React.memo(() => { export const MetricContainerHeaderButtons: React.FC<FileContainerButtonsProps> = React.memo(() => {
const selectedLayout = useChatLayoutContextSelector((x) => x.selectedLayout); const selectedLayout = useChatLayoutContextSelector((x) => x.selectedLayout);
const selectedFileId = useChatIndividualContextSelector((x) => x.selectedFileId)!; const metricId = useChatIndividualContextSelector((x) => x.selectedFileId)!;
const metricId = selectedFileId; const metricVersionNumber = useChatLayoutContextSelector((x) => x.metricVersionNumber);
const { isViewingOldVersion } = useIsMetricReadOnly({
metricId: metricId || ''
});
const { error: metricError, data: permission } = useGetMetric( const { error: metricError, data: permission } = useGetMetric(
{ id: metricId }, { id: metricId },
{ select: (x) => x.permission } { select: (x) => x.permission }
@ -35,12 +39,16 @@ export const MetricContainerHeaderButtons: React.FC<FileContainerButtonsProps> =
return ( return (
<FileButtonContainer> <FileButtonContainer>
{isEditor && <EditChartButton metricId={metricId} />} {isEditor && !isViewingOldVersion && <EditChartButton metricId={metricId} />}
{isEffectiveOwner && <EditSQLButton metricId={metricId} />} {isEffectiveOwner && !isViewingOldVersion && <EditSQLButton metricId={metricId} />}
<SaveToCollectionButton metricId={metricId} /> <SaveToCollectionButton metricId={metricId} />
<SaveToDashboardButton metricId={metricId} /> <SaveToDashboardButton metricId={metricId} />
{isEffectiveOwner && <ShareMetricButton metricId={metricId} />} {isEffectiveOwner && !isViewingOldVersion && <ShareMetricButton metricId={metricId} />}
<ThreeDotMenuButton metricId={metricId} /> <ThreeDotMenuButton
metricId={metricId}
isViewingOldVersion={isViewingOldVersion}
versionNumber={metricVersionNumber}
/>
<HideButtonContainer show={selectedLayout === 'file-only'}> <HideButtonContainer show={selectedLayout === 'file-only'}>
<CreateChatButton assetId={metricId} assetType="metric" /> <CreateChatButton assetId={metricId} assetType="metric" />
</HideButtonContainer> </HideButtonContainer>
@ -55,8 +63,8 @@ const EditChartButton = React.memo(({ metricId }: { metricId: string }) => {
(x) => x.selectedFileViewSecondary (x) => x.selectedFileViewSecondary
); );
const onChangePage = useAppLayoutContextSelector((x) => x.onChangePage); const onChangePage = useAppLayoutContextSelector((x) => x.onChangePage);
const onChangeQueryParams = useAppLayoutContextSelector((x) => x.onChangeQueryParams);
const chatId = useChatIndividualContextSelector((x) => x.chatId); const chatId = useChatIndividualContextSelector((x) => x.chatId);
const metricVersionNumber = useChatLayoutContextSelector((x) => x.metricVersionNumber);
const editableSecondaryView: MetricFileViewSecondary = 'chart-edit'; const editableSecondaryView: MetricFileViewSecondary = 'chart-edit';
const isSelectedView = selectedFileViewSecondary === editableSecondaryView; const isSelectedView = selectedFileViewSecondary === editableSecondaryView;
@ -66,7 +74,8 @@ const EditChartButton = React.memo(({ metricId }: { metricId: string }) => {
chatId, chatId,
assetId: metricId, assetId: metricId,
type: 'metric', type: 'metric',
secondaryView: null secondaryView: null,
versionNumber: metricVersionNumber
}); });
} }
@ -74,9 +83,10 @@ const EditChartButton = React.memo(({ metricId }: { metricId: string }) => {
chatId, chatId,
assetId: metricId, assetId: metricId,
type: 'metric', type: 'metric',
secondaryView: 'chart-edit' secondaryView: 'chart-edit',
versionNumber: metricVersionNumber
}); });
}, [chatId, metricId, isSelectedView]); }, [chatId, metricId, isSelectedView, metricVersionNumber]);
const onClickButton = useMemoizedFn(() => { const onClickButton = useMemoizedFn(() => {
onChangePage(href, { shallow: true }); onChangePage(href, { shallow: true });
@ -108,6 +118,7 @@ const EditSQLButton = React.memo(({ metricId }: { metricId: string }) => {
); );
const onSetFileView = useChatLayoutContextSelector((x) => x.onSetFileView); const onSetFileView = useChatLayoutContextSelector((x) => x.onSetFileView);
const chatId = useChatIndividualContextSelector((x) => x.chatId); const chatId = useChatIndividualContextSelector((x) => x.chatId);
const metricVersionNumber = useChatLayoutContextSelector((x) => x.metricVersionNumber);
const editableSecondaryView: MetricFileViewSecondary = 'sql-edit'; const editableSecondaryView: MetricFileViewSecondary = 'sql-edit';
const isSelectedView = selectedFileViewSecondary === editableSecondaryView; const isSelectedView = selectedFileViewSecondary === editableSecondaryView;
@ -116,9 +127,10 @@ const EditSQLButton = React.memo(({ metricId }: { metricId: string }) => {
chatId, chatId,
assetId: metricId, assetId: metricId,
type: 'metric', type: 'metric',
secondaryView: 'sql-edit' secondaryView: 'sql-edit',
versionNumber: metricVersionNumber
}); });
}, [chatId, metricId]); }, [chatId, metricId, metricVersionNumber]);
const onClickButton = useMemoizedFn(() => { const onClickButton = useMemoizedFn(() => {
const secondaryView = isSelectedView ? null : editableSecondaryView; const secondaryView = isSelectedView ? null : editableSecondaryView;
@ -147,8 +159,3 @@ const SaveToDashboardButton = React.memo(({ metricId }: { metricId: string }) =>
return <SaveMetricToDashboardButton metricIds={[metricId]} />; return <SaveMetricToDashboardButton metricIds={[metricId]} />;
}); });
SaveToDashboardButton.displayName = 'SaveToDashboardButton'; SaveToDashboardButton.displayName = 'SaveToDashboardButton';
const ShareMetricButtonLocal = React.memo(({ metricId }: { metricId: string }) => {
return <ShareMetricButton metricId={metricId} />;
});
ShareMetricButtonLocal.displayName = 'ShareMetricButtonLocal';

View File

@ -56,82 +56,92 @@ import { BusterRoutes, createBusterRoute } from '@/routes';
import { useListVersionDropdownItems } from '@/components/features/versionHistory/useListVersionDropdownItems'; import { useListVersionDropdownItems } from '@/components/features/versionHistory/useListVersionDropdownItems';
import { useChatIndividualContextSelector } from '@/layouts/ChatLayout/ChatContext'; import { useChatIndividualContextSelector } from '@/layouts/ChatLayout/ChatContext';
export const ThreeDotMenuButton = React.memo(({ metricId }: { metricId: string }) => { export const ThreeDotMenuButton = React.memo(
const chatId = useChatIndividualContextSelector((x) => x.chatId); ({
const { openSuccessMessage } = useBusterNotifications(); metricId,
const { data: permission } = useGetMetric({ id: metricId }, { select: (x) => x.permission }); isViewingOldVersion,
const openFullScreenMetric = useOpenFullScreenMetric({ metricId }); versionNumber
const onSetSelectedFile = useChatLayoutContextSelector((x) => x.onSetSelectedFile); }: {
const dashboardSelectMenu = useDashboardSelectMenu({ metricId }); metricId: string;
const versionHistoryItems = useVersionHistorySelectMenu({ metricId }); isViewingOldVersion: boolean;
const collectionSelectMenu = useCollectionSelectMenu({ metricId }); versionNumber: number | undefined;
const statusSelectMenu = useStatusSelectMenu({ metricId }); }) => {
const favoriteMetric = useFavoriteMetricSelectMenu({ metricId }); const chatId = useChatIndividualContextSelector((x) => x.chatId);
const editChartMenu = useEditChartSelectMenu(); const { openSuccessMessage } = useBusterNotifications();
const resultsViewMenu = useResultsViewSelectMenu(); const { data: permission } = useGetMetric({ id: metricId }, { select: (x) => x.permission });
const sqlEditorMenu = useSQLEditorSelectMenu(); const openFullScreenMetric = useOpenFullScreenMetric({ metricId, versionNumber });
const downloadCSVMenu = useDownloadCSVSelectMenu({ metricId }); const onSetSelectedFile = useChatLayoutContextSelector((x) => x.onSetSelectedFile);
const downloadPNGMenu = useDownloadPNGSelectMenu({ metricId }); const dashboardSelectMenu = useDashboardSelectMenu({ metricId });
const deleteMetricMenu = useDeleteMetricSelectMenu({ metricId }); const versionHistoryItems = useVersionHistorySelectMenu({ metricId });
const renameMetricMenu = useRenameMetricSelectMenu({ metricId }); const collectionSelectMenu = useCollectionSelectMenu({ metricId });
const shareMenu = useShareMenuSelectMenu({ metricId }); const statusSelectMenu = useStatusSelectMenu({ metricId });
const favoriteMetric = useFavoriteMetricSelectMenu({ metricId });
const editChartMenu = useEditChartSelectMenu();
const resultsViewMenu = useResultsViewSelectMenu();
const sqlEditorMenu = useSQLEditorSelectMenu();
const downloadCSVMenu = useDownloadCSVSelectMenu({ metricId });
const downloadPNGMenu = useDownloadPNGSelectMenu({ metricId });
const deleteMetricMenu = useDeleteMetricSelectMenu({ metricId });
const renameMetricMenu = useRenameMetricSelectMenu({ metricId });
const shareMenu = useShareMenuSelectMenu({ metricId });
const isEditor = canEdit(permission); const isEditor = canEdit(permission);
const isOwnerEffective = getIsEffectiveOwner(permission); const isOwnerEffective = getIsEffectiveOwner(permission);
const isOwner = getIsOwner(permission); const isOwner = getIsOwner(permission);
const items: DropdownItems = useMemo( const items: DropdownItems = useMemo(
() => () =>
[
chatId && openFullScreenMetric,
isOwnerEffective && !isViewingOldVersion && shareMenu,
isEditor && !isViewingOldVersion && statusSelectMenu,
{ type: 'divider' },
!isViewingOldVersion && dashboardSelectMenu,
!isViewingOldVersion && collectionSelectMenu,
!isViewingOldVersion && favoriteMetric,
{ type: 'divider' },
isEditor && !isViewingOldVersion && editChartMenu,
!isViewingOldVersion && resultsViewMenu,
!isViewingOldVersion && sqlEditorMenu,
isEditor && versionHistoryItems,
{ type: 'divider' },
downloadCSVMenu,
downloadPNGMenu,
{ type: 'divider' },
isEditor && !isViewingOldVersion && renameMetricMenu,
isOwner && !isViewingOldVersion && deleteMetricMenu
].filter(Boolean) as DropdownItems,
[ [
chatId && openFullScreenMetric, chatId,
isOwnerEffective && shareMenu, openFullScreenMetric,
isEditor && statusSelectMenu, isEditor,
{ type: 'divider' }, isOwner,
isOwnerEffective,
renameMetricMenu,
dashboardSelectMenu, dashboardSelectMenu,
collectionSelectMenu, deleteMetricMenu,
favoriteMetric,
{ type: 'divider' },
isEditor && editChartMenu,
resultsViewMenu,
sqlEditorMenu,
isEditor && versionHistoryItems,
{ type: 'divider' },
downloadCSVMenu, downloadCSVMenu,
downloadPNGMenu, downloadPNGMenu,
{ type: 'divider' }, openSuccessMessage,
isEditor && renameMetricMenu, onSetSelectedFile,
isOwner && deleteMetricMenu versionHistoryItems,
].filter(Boolean) as DropdownItems, favoriteMetric,
[ statusSelectMenu,
chatId, collectionSelectMenu,
openFullScreenMetric, editChartMenu,
isEditor, resultsViewMenu,
isOwner, sqlEditorMenu,
isOwnerEffective, shareMenu
renameMetricMenu, ]
dashboardSelectMenu, );
deleteMetricMenu,
downloadCSVMenu,
downloadPNGMenu,
openSuccessMessage,
onSetSelectedFile,
versionHistoryItems,
favoriteMetric,
statusSelectMenu,
collectionSelectMenu,
editChartMenu,
resultsViewMenu,
sqlEditorMenu,
shareMenu
]
);
return ( return (
<Dropdown items={items} side="bottom" align="end" contentClassName="max-h-fit" modal> <Dropdown items={items} side="bottom" align="end" contentClassName="max-h-fit" modal>
<Button prefix={<Dots />} variant="ghost" /> <Button prefix={<Dots />} variant="ghost" />
</Dropdown> </Dropdown>
); );
}); }
);
ThreeDotMenuButton.displayName = 'ThreeDotMenuButton'; ThreeDotMenuButton.displayName = 'ThreeDotMenuButton';
const useDashboardSelectMenu = ({ metricId }: { metricId: string }) => { const useDashboardSelectMenu = ({ metricId }: { metricId: string }) => {
@ -499,17 +509,29 @@ export const useShareMenuSelectMenu = ({ metricId }: { metricId: string }) => {
); );
}; };
const useOpenFullScreenMetric = ({ metricId }: { metricId: string }) => { const useOpenFullScreenMetric = ({
metricId,
versionNumber
}: {
metricId: string;
versionNumber: number | undefined;
}) => {
return useMemo( return useMemo(
() => ({ () => ({
label: 'Open in metric page', label: 'Open in metric page',
value: 'open-in-full-screen', value: 'open-in-full-screen',
icon: <ArrowUpRight />, icon: <ArrowUpRight />,
link: createBusterRoute({ link: versionNumber
route: BusterRoutes.APP_METRIC_ID_CHART, ? createBusterRoute({
metricId route: BusterRoutes.APP_METRIC_ID_VERSION_NUMBER,
}) metricId,
versionNumber
})
: createBusterRoute({
route: BusterRoutes.APP_METRIC_ID_CHART,
metricId
})
}), }),
[metricId] [metricId, versionNumber]
); );
}; };

View File

@ -57,7 +57,6 @@ export type BusterAppRoutesWithArgs = {
[BusterAppRoutes.APP_METRIC_ID_CHART]: { [BusterAppRoutes.APP_METRIC_ID_CHART]: {
route: BusterAppRoutes.APP_METRIC_ID_CHART; route: BusterAppRoutes.APP_METRIC_ID_CHART;
metricId: string; metricId: string;
versionNumber?: number;
secondaryView?: MetricFileViewSecondary; secondaryView?: MetricFileViewSecondary;
}; };
[BusterAppRoutes.APP_METRIC_ID_VERSION_NUMBER]: { [BusterAppRoutes.APP_METRIC_ID_VERSION_NUMBER]: {
@ -132,6 +131,7 @@ export type BusterAppRoutesWithArgs = {
route: BusterAppRoutes.APP_CHAT_ID_METRIC_ID_CHART; route: BusterAppRoutes.APP_CHAT_ID_METRIC_ID_CHART;
chatId: string; chatId: string;
metricId: string; metricId: string;
versionNumber?: number;
secondaryView?: MetricFileViewSecondary; secondaryView?: MetricFileViewSecondary;
}; };
[BusterAppRoutes.APP_CHAT_ID_METRIC_ID_VERSION_NUMBER]: { [BusterAppRoutes.APP_CHAT_ID_METRIC_ID_VERSION_NUMBER]: {