mirror of https://github.com/buster-so/buster.git
new method for viewing secondary panel
This commit is contained in:
parent
9eb70c204f
commit
60d59ff313
|
@ -0,0 +1,9 @@
|
|||
import React from 'react';
|
||||
|
||||
export const VersionHistoryPanel = React.memo(
|
||||
({ assetId, type }: { assetId: string; type: 'metric' | 'dashboard' }) => {
|
||||
return <div>VersionHistoryPanel</div>;
|
||||
}
|
||||
);
|
||||
|
||||
VersionHistoryPanel.displayName = 'VersionHistoryPanel';
|
|
@ -0,0 +1 @@
|
|||
export * from './VersionHistoryPanel';
|
|
@ -14,3 +14,5 @@ export const MetricEditController: React.FC<{
|
|||
});
|
||||
|
||||
MetricEditController.displayName = 'MetricEditController';
|
||||
|
||||
export default MetricEditController;
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
'use client';
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import type { MetricViewProps } from '../config';
|
||||
import { AppSplitter, AppSplitterRef } from '@/components/ui/layouts';
|
||||
import { MetricViewChart } from './MetricViewChart';
|
||||
import { MetricEditController } from './MetricEditController';
|
||||
import { useMetricLayout } from '../useMetricLayout';
|
||||
import { useChatLayoutContextSelector } from '@/layouts/ChatLayout';
|
||||
import { VersionHistoryPanel } from '@/components/features/versionHistory';
|
||||
|
||||
const autoSaveId = 'metric-edit-chart';
|
||||
|
||||
|
@ -23,12 +24,21 @@ export const MetricViewChartController: React.FC<MetricViewProps> = React.memo((
|
|||
type: 'chart'
|
||||
});
|
||||
|
||||
const RightChildren = useMemo(() => {
|
||||
if (!renderSecondary) return null;
|
||||
if (selectedFileViewSecondary === 'chart-edit')
|
||||
return <MetricEditController metricId={metricId} />;
|
||||
if (selectedFileViewSecondary === 'version-history')
|
||||
return <VersionHistoryPanel assetId={metricId} type="metric" />;
|
||||
return null;
|
||||
}, [renderSecondary, metricId]);
|
||||
|
||||
return (
|
||||
<AppSplitter
|
||||
ref={appSplitterRef}
|
||||
initialReady={false}
|
||||
leftChildren={<MetricViewChart metricId={metricId} />}
|
||||
rightChildren={<MetricEditController metricId={metricId} />}
|
||||
rightChildren={RightChildren}
|
||||
rightHidden={!renderSecondary}
|
||||
autoSaveId={autoSaveId}
|
||||
defaultLayout={defaultLayout}
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
export * from './MetricViewChartController';
|
||||
export * from './MetricViewChart';
|
||||
|
|
|
@ -3,13 +3,14 @@ import type { MetricFileView } from '@/layouts/ChatLayout';
|
|||
import { MetricViewChartController } from './MetricViewChart';
|
||||
import { MetricViewFile } from './MetricViewFile';
|
||||
import { MetricViewResults } from './MetricViewResults';
|
||||
import { MetricViewChart } from './MetricViewChart';
|
||||
|
||||
export interface MetricViewProps {
|
||||
metricId: string;
|
||||
}
|
||||
|
||||
export const MetricViewComponents: Record<MetricFileView, React.FC<MetricViewProps>> = {
|
||||
chart: MetricViewChartController,
|
||||
chart: MetricViewChart,
|
||||
results: MetricViewResults,
|
||||
file: MetricViewFile
|
||||
};
|
||||
|
|
|
@ -48,13 +48,20 @@ export const useChatLayoutContext = ({ appSplitterRef }: UseLayoutConfigProps) =
|
|||
|
||||
const onCollapseFileClick = useLayoutCollapse({ onSetSelectedFile });
|
||||
|
||||
const { selectedFileView, selectedFileViewSecondary, onSetFileView, closeSecondaryView } =
|
||||
useLayoutConfig({
|
||||
selectedFileId: selectedFile?.id,
|
||||
selectedFileType: selectedFile?.type
|
||||
});
|
||||
const {
|
||||
selectedFileView,
|
||||
selectedFileViewRenderSecondary,
|
||||
selectedFileViewSecondary,
|
||||
onSetFileView,
|
||||
closeSecondaryView
|
||||
} = useLayoutConfig({
|
||||
selectedFileId: selectedFile?.id,
|
||||
selectedFileType: selectedFile?.type,
|
||||
isVersionHistoryMode
|
||||
});
|
||||
|
||||
return {
|
||||
selectedFileViewRenderSecondary,
|
||||
selectedFileView,
|
||||
selectedFileViewSecondary,
|
||||
onSetFileView,
|
||||
|
|
|
@ -3,7 +3,15 @@ export interface FileConfig {
|
|||
fileViewConfig?: FileViewConfig;
|
||||
}
|
||||
|
||||
export type FileViewConfig = Partial<Record<FileView, { secondaryView: FileViewSecondary }>>;
|
||||
export type FileViewConfig = Partial<
|
||||
Record<
|
||||
FileView,
|
||||
{
|
||||
secondaryView: FileViewSecondary;
|
||||
renderView?: boolean; //this is really just used for metric because it has a vertical view and we don't want to render a right panel. undefined defaults to true
|
||||
}
|
||||
>
|
||||
>;
|
||||
|
||||
export type FileView =
|
||||
| MetricFileView
|
||||
|
@ -21,5 +29,7 @@ export type ValueFileView = 'file';
|
|||
export type DatasetFileView = 'file';
|
||||
export type CollectionFileView = 'file' | 'results';
|
||||
export type ReasoningFileView = 'reasoning';
|
||||
export type FileViewSecondary = null | MetricFileViewSecondary;
|
||||
export type MetricFileViewSecondary = 'chart-edit' | 'sql-edit';
|
||||
|
||||
export type MetricFileViewSecondary = 'chart-edit' | 'sql-edit' | 'version-history';
|
||||
export type DashboardFileViewSecondary = 'version-history';
|
||||
export type FileViewSecondary = null | MetricFileViewSecondary | DashboardFileViewSecondary;
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
'use client';
|
||||
|
||||
import { FileType } from '@/api/asset_interfaces';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { FileConfig, FileView, FileViewConfig, FileViewSecondary } from './interfaces';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { create } from 'mutative';
|
||||
|
||||
export const useLayoutConfig = ({
|
||||
selectedFileId,
|
||||
selectedFileType
|
||||
selectedFileType,
|
||||
isVersionHistoryMode
|
||||
}: {
|
||||
selectedFileId: string | undefined;
|
||||
selectedFileType: FileType | undefined;
|
||||
isVersionHistoryMode: boolean;
|
||||
}) => {
|
||||
const [fileViews, setFileViews] = useState<Record<string, FileConfig>>({});
|
||||
|
||||
|
@ -32,15 +34,25 @@ export const useLayoutConfig = ({
|
|||
return selectedFileViewConfig?.[selectedFileView]?.secondaryView ?? null;
|
||||
}, [selectedFileViewConfig, selectedFileId, selectedFileView]);
|
||||
|
||||
const selectedFileViewRenderSecondary: boolean = useMemo(() => {
|
||||
if (!selectedFileId || !selectedFileViewConfig || !selectedFileView) return false;
|
||||
if (selectedFileViewConfig?.[selectedFileView]?.secondaryView) {
|
||||
return selectedFileViewConfig?.[selectedFileView]?.renderView !== false;
|
||||
}
|
||||
return false;
|
||||
}, [selectedFileViewConfig]);
|
||||
|
||||
const onSetFileView = useMemoizedFn(
|
||||
({
|
||||
fileView,
|
||||
fileId,
|
||||
secondaryView
|
||||
secondaryView,
|
||||
renderView
|
||||
}: {
|
||||
fileView?: FileView;
|
||||
fileId?: string;
|
||||
secondaryView?: FileViewSecondary;
|
||||
renderView?: boolean;
|
||||
}) => {
|
||||
const id = fileId ?? selectedFileId;
|
||||
if (!id) return;
|
||||
|
@ -60,7 +72,8 @@ export const useLayoutConfig = ({
|
|||
...newFileConfig.fileViewConfig,
|
||||
[usedFileView]: {
|
||||
...newFileConfig.fileViewConfig?.[usedFileView],
|
||||
secondaryView
|
||||
secondaryView,
|
||||
renderView
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -80,9 +93,25 @@ export const useLayoutConfig = ({
|
|||
});
|
||||
});
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (
|
||||
isVersionHistoryMode &&
|
||||
selectedFileId &&
|
||||
(selectedFileType === 'metric' || selectedFileType === 'dashboard')
|
||||
) {
|
||||
const fileView = selectedFileType === 'metric' ? 'chart' : 'dashboard';
|
||||
onSetFileView({
|
||||
fileId: selectedFileId,
|
||||
fileView,
|
||||
secondaryView: 'version-history'
|
||||
});
|
||||
}
|
||||
}, [isVersionHistoryMode, selectedFileId]);
|
||||
|
||||
return {
|
||||
selectedFileView,
|
||||
selectedFileViewSecondary,
|
||||
selectedFileViewRenderSecondary,
|
||||
onSetFileView,
|
||||
closeSecondaryView
|
||||
};
|
||||
|
|
|
@ -44,6 +44,13 @@ export const useSelectedFile = ({
|
|||
return false;
|
||||
}, [selectedFile?.type, metricVersionNumber, dashboardVersionNumber]);
|
||||
|
||||
console.log(
|
||||
'isVersionHistoryMode',
|
||||
isVersionHistoryMode,
|
||||
metricVersionNumber,
|
||||
dashboardVersionNumber
|
||||
);
|
||||
|
||||
const [renderViewLayoutKey, setRenderViewLayoutKey] = useState<ChatLayoutView>(
|
||||
selectedLayout || 'chat'
|
||||
);
|
||||
|
@ -96,7 +103,14 @@ export const useSelectedFile = ({
|
|||
renderViewLayoutKey,
|
||||
setRenderViewLayoutKey
|
||||
}),
|
||||
[onSetSelectedFile, selectedFile, selectedLayout, chatId, renderViewLayoutKey]
|
||||
[
|
||||
onSetSelectedFile,
|
||||
isVersionHistoryMode,
|
||||
selectedFile,
|
||||
selectedLayout,
|
||||
chatId,
|
||||
renderViewLayoutKey
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,15 +1,81 @@
|
|||
import React from 'react';
|
||||
'use client';
|
||||
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import { FileContainerHeader } from './FileContainerHeader';
|
||||
import { AppPageLayout } from '@/components/ui/layouts';
|
||||
import { AppPageLayout, AppSplitter, AppSplitterRef } from '@/components/ui/layouts';
|
||||
import { useChatLayoutContextSelector } from '../ChatLayoutContext';
|
||||
import { createAutoSaveId } from '@/components/ui/layouts/AppSplitter/helper';
|
||||
import Cookies from 'js-cookie';
|
||||
import { useMemoizedFn, useUpdateLayoutEffect } from '@/hooks';
|
||||
|
||||
interface FileContainerProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const defaultOpenLayout: [string, string] = ['auto', '310px'];
|
||||
const defaulClosedLayout: [string, string] = ['auto', '0px'];
|
||||
const autoSaveId = 'file-container-splitter';
|
||||
|
||||
export const FileContainer: React.FC<FileContainerProps> = ({ children }) => {
|
||||
const appSplitterRef = useRef<AppSplitterRef>(null);
|
||||
|
||||
const selectedFileViewSecondary = useChatLayoutContextSelector(
|
||||
(x) => x.selectedFileViewSecondary
|
||||
);
|
||||
const selectedFileViewRenderSecondary = useChatLayoutContextSelector(
|
||||
(x) => x.selectedFileViewRenderSecondary
|
||||
);
|
||||
const isOpenSecondary = selectedFileViewRenderSecondary;
|
||||
|
||||
const secondaryLayoutDimensions: [string, string] = useMemo(() => {
|
||||
const cookieKey = createAutoSaveId(autoSaveId);
|
||||
const cookieValue = Cookies.get(cookieKey);
|
||||
if (cookieValue) {
|
||||
try {
|
||||
const parsedValue = JSON.parse(cookieValue) as string[];
|
||||
if (!parsedValue?.some((item) => item === 'auto')) {
|
||||
return parsedValue as [string, string];
|
||||
}
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
}
|
||||
return defaultOpenLayout as [string, string];
|
||||
}, []);
|
||||
|
||||
const defaultLayout: [string, string] = useMemo(() => {
|
||||
if (isOpenSecondary) {
|
||||
return secondaryLayoutDimensions;
|
||||
}
|
||||
return defaulClosedLayout;
|
||||
}, []);
|
||||
|
||||
const animateOpenSplitter = useMemoizedFn((side: 'open' | 'closed') => {
|
||||
if (side === 'open') {
|
||||
appSplitterRef.current?.animateWidth(defaultOpenLayout[1], 'right');
|
||||
} else {
|
||||
appSplitterRef.current?.animateWidth(defaulClosedLayout[1], 'right');
|
||||
}
|
||||
});
|
||||
|
||||
useUpdateLayoutEffect(() => {
|
||||
animateOpenSplitter(isOpenSecondary ? 'open' : 'closed');
|
||||
}, [isOpenSecondary]);
|
||||
|
||||
return (
|
||||
<AppPageLayout className="flex h-full min-w-[380px] flex-col" header={<FileContainerHeader />}>
|
||||
{children}
|
||||
<AppSplitter
|
||||
ref={appSplitterRef}
|
||||
autoSaveId={autoSaveId}
|
||||
defaultLayout={defaultLayout}
|
||||
initialReady={false}
|
||||
leftChildren={children}
|
||||
rightChildren={<div>Right {selectedFileViewSecondary}</div>}
|
||||
allowResize={selectedFileViewRenderSecondary}
|
||||
preserveSide={'right'}
|
||||
rightPanelMinSize={250}
|
||||
rightPanelMaxSize={385}
|
||||
/>
|
||||
</AppPageLayout>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import { Button } from '@/components/ui/buttons';
|
||||
import { ArrowLeft, History } from '@/components/ui/icons';
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useMemo, useTransition } from 'react';
|
||||
import { useChatLayoutContextSelector } from '../../ChatLayoutContext';
|
||||
import { useGetMetric } from '@/api/buster_rest/metrics';
|
||||
import last from 'lodash/last';
|
||||
import { useGetDashboard } from '@/api/buster_rest/dashboards';
|
||||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { timeout } from '@/lib';
|
||||
|
||||
export const FileContainerVersionHistory = React.memo(() => {
|
||||
return (
|
||||
<div className="flex w-full items-center justify-between gap-x-3">
|
||||
<div className="flex w-full items-center justify-between gap-x-1.5">
|
||||
<ExitVersionHistoryButton />
|
||||
<DisplayVersionHistory />
|
||||
</div>
|
||||
|
@ -38,6 +39,7 @@ const DisplayVersionHistory = React.memo(() => {
|
|||
);
|
||||
|
||||
const versionInfo = useMemo(() => {
|
||||
if (!metric?.version_number && !dashboard?.version_number) return null;
|
||||
if (type === 'metric') {
|
||||
return {
|
||||
isCurrent: metric?.version_number === metric?.latestVersion?.version_number,
|
||||
|
@ -67,10 +69,16 @@ const DisplayVersionHistory = React.memo(() => {
|
|||
DisplayVersionHistory.displayName = 'DisplayVersionHistory';
|
||||
|
||||
const ExitVersionHistoryButton = React.memo(() => {
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const onChangeQueryParams = useAppLayoutContextSelector((x) => x.onChangeQueryParams);
|
||||
const closeSecondaryView = useChatLayoutContextSelector((x) => x.closeSecondaryView);
|
||||
|
||||
const removeVersionHistoryQueryParams = useMemoizedFn(() => {
|
||||
onChangeQueryParams({ metric_version_number: null, dashboard_version_number: null });
|
||||
const removeVersionHistoryQueryParams = useMemoizedFn(async () => {
|
||||
closeSecondaryView();
|
||||
await timeout(250);
|
||||
startTransition(() => {
|
||||
onChangeQueryParams({ metric_version_number: null, dashboard_version_number: null });
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
|
@ -17,7 +17,8 @@ export const MetricContainerHeaderSegment: React.FC<FileContainerSegmentProps> =
|
|||
const onSetFileView = useChatLayoutContextSelector((x) => x.onSetFileView);
|
||||
|
||||
const onChange = useMemoizedFn((fileView: SegmentedItem<FileView>) => {
|
||||
onSetFileView({ fileView: fileView.value });
|
||||
const renderView = fileView.value === 'results' ? false : true;
|
||||
onSetFileView({ fileView: fileView.value, renderView });
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { FileView } from '../../ChatLayoutContext/useLayoutConfig';
|
||||
|
||||
export interface FileContainerSegmentProps {
|
||||
export type FileContainerSegmentProps = {
|
||||
selectedFileView: FileView | undefined;
|
||||
selectedFileId: string | undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export interface FileContainerButtonsProps {
|
||||
export type FileContainerButtonsProps = {
|
||||
selectedFileView: FileView | undefined;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import type { FileType } from '@/api/asset_interfaces/chat';
|
||||
import type { FileContainerSecondaryProps } from './interfaces';
|
||||
import type {
|
||||
DashboardFileViewSecondary,
|
||||
MetricFileViewSecondary
|
||||
} from '../../ChatLayoutContext/useLayoutConfig/interfaces';
|
||||
|
||||
const MetricSecondaryRecord: Record<
|
||||
MetricFileViewSecondary,
|
||||
React.FC<FileContainerSecondaryProps>
|
||||
> = {
|
||||
'chart-edit': () => null,
|
||||
'sql-edit': () => null,
|
||||
'version-history': () => null
|
||||
};
|
||||
|
||||
const DashboardSecondaryRecord: Record<
|
||||
DashboardFileViewSecondary,
|
||||
React.FC<FileContainerSecondaryProps>
|
||||
> = {
|
||||
'version-history': () => null
|
||||
};
|
||||
|
||||
const SelectedFileSecondaryRecord: Record<
|
||||
FileType,
|
||||
Record<string, React.FC<FileContainerSecondaryProps>>
|
||||
> = {
|
||||
metric: MetricSecondaryRecord,
|
||||
dashboard: DashboardSecondaryRecord,
|
||||
reasoning: {}
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export * from './FileContainerSecondary';
|
|
@ -0,0 +1,6 @@
|
|||
import type { FileViewSecondary } from '@/layouts/ChatLayout/ChatLayoutContext/useLayoutConfig';
|
||||
|
||||
export type FileContainerSecondaryProps = {
|
||||
selectedFileId: string | undefined;
|
||||
selectedFileViewSecondary: FileViewSecondary | undefined;
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
import type { FileType } from '@/api/asset_interfaces/chat';
|
||||
import type { FileContainerSecondaryProps } from '../interfaces';
|
||||
import React from 'react';
|
||||
import { MetricSecondaryRecord } from './metricPanels';
|
||||
import { DashboardSecondaryRecord } from './dashboardPanels';
|
||||
|
||||
export const SelectedFileSecondaryRecord: Record<
|
||||
FileType,
|
||||
Record<string, React.FC<FileContainerSecondaryProps>>
|
||||
> = {
|
||||
metric: MetricSecondaryRecord,
|
||||
dashboard: DashboardSecondaryRecord,
|
||||
reasoning: {}
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
import { type DashboardFileViewSecondary } from '@/layouts/ChatLayout/ChatLayoutContext/useLayoutConfig';
|
||||
import type { FileContainerSecondaryProps } from '../interfaces';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { loading } from './loading';
|
||||
|
||||
const VersionHistoryPanel = dynamic(
|
||||
() =>
|
||||
import('@/components/features/versionHistory/VersionHistoryPanel').then(
|
||||
(x) => x.VersionHistoryPanel
|
||||
),
|
||||
{ loading }
|
||||
);
|
||||
|
||||
export const DashboardSecondaryRecord: Record<
|
||||
DashboardFileViewSecondary,
|
||||
React.FC<FileContainerSecondaryProps>
|
||||
> = {
|
||||
'version-history': ({ selectedFileId }) => (
|
||||
<VersionHistoryPanel assetId={selectedFileId || ''} type="dashboard" />
|
||||
)
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export * from './allConfigs';
|
|
@ -0,0 +1,9 @@
|
|||
import { CircleSpinnerLoaderContainer } from '@/components/ui/loaders/CircleSpinnerLoaderContainer';
|
||||
|
||||
export const loading = () => {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<CircleSpinnerLoaderContainer />
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
import dynamic from 'next/dynamic';
|
||||
import { loading } from './loading';
|
||||
import type { MetricFileViewSecondary } from '@/layouts/ChatLayout/ChatLayoutContext/useLayoutConfig/interfaces';
|
||||
import type { FileContainerSecondaryProps } from '../interfaces';
|
||||
|
||||
const MetricEditController = dynamic(
|
||||
() =>
|
||||
import('@/controllers/MetricController/MetricViewChart/MetricEditController').then(
|
||||
(x) => x.MetricEditController
|
||||
),
|
||||
{ loading }
|
||||
);
|
||||
const VersionHistoryPanel = dynamic(
|
||||
() =>
|
||||
import('@/components/features/versionHistory/VersionHistoryPanel').then(
|
||||
(x) => x.VersionHistoryPanel
|
||||
),
|
||||
{ loading }
|
||||
);
|
||||
const MetricViewResults = dynamic(
|
||||
() => import('@/controllers/MetricController/MetricViewResults').then((x) => x.MetricViewResults),
|
||||
{ loading }
|
||||
);
|
||||
|
||||
export const MetricSecondaryRecord: Record<
|
||||
MetricFileViewSecondary,
|
||||
React.FC<FileContainerSecondaryProps>
|
||||
> = {
|
||||
'chart-edit': ({ selectedFileId }) => <MetricEditController metricId={selectedFileId || ''} />,
|
||||
'sql-edit': ({ selectedFileId }) => <MetricViewResults metricId={selectedFileId || ''} />,
|
||||
'version-history': ({ selectedFileId }) => (
|
||||
<VersionHistoryPanel assetId={selectedFileId || ''} type="metric" />
|
||||
)
|
||||
};
|
Loading…
Reference in New Issue