mirror of https://github.com/buster-so/buster.git
toggle for metric slideout
This commit is contained in:
parent
4ca917195c
commit
d17899f3d4
|
@ -1,4 +1,4 @@
|
|||
import type { BusterChatMessageResponse } from './chatMessageInterfaces';
|
||||
import type { BusterChatMessage, BusterChatMessageResponse } from './chatMessageInterfaces';
|
||||
|
||||
enum BusterChatStepProgress {
|
||||
IN_PROGRESS = 'in_progress',
|
||||
|
@ -17,5 +17,9 @@ export type ChatPost_generatingTitle = {
|
|||
} & BusterChatStepBase;
|
||||
|
||||
export type ChatPost_generatingMessage = {
|
||||
message: BusterChatMessageResponse;
|
||||
resposnse_message: BusterChatMessageResponse;
|
||||
} & BusterChatStepBase;
|
||||
|
||||
export type ChatPost_complete = {
|
||||
message: BusterChatMessage;
|
||||
} & BusterChatStepBase;
|
||||
|
|
|
@ -8,6 +8,7 @@ export type ChatCreateNewChat = BusterSocketRequestBase<
|
|||
chat_id?: string | null; //send if we are following up on a chat
|
||||
suggestion_id?: string | null; //send if we clicked on a suggestion
|
||||
message_id?: string; //send if we want to REPLACE current message
|
||||
metric_id?: string; //send if we want to create a chat from a metric
|
||||
draft_session_id?: string; //TODO: do we need this?
|
||||
}
|
||||
>;
|
||||
|
@ -39,7 +40,7 @@ export type ChatsDuplicateChat = BusterSocketRequestBase<
|
|||
'/chats/duplicate',
|
||||
{
|
||||
id: string;
|
||||
message_id?: string; //send if we want to duplciate the chat starting from a specific message
|
||||
message_id: string; //send if we want to duplciate the chat starting from a specific message
|
||||
}
|
||||
>;
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ export type ShareRequest = {
|
|||
user_email: string;
|
||||
role: ShareRole;
|
||||
}[];
|
||||
|
||||
remove_users?: string[]; // user_id
|
||||
team_permissions?: {
|
||||
team_id: string;
|
||||
|
|
|
@ -23,52 +23,28 @@ export type MetricSubscribeToMetric = BusterSocketRequestBase<
|
|||
{ id: string; password?: string }
|
||||
>;
|
||||
|
||||
export type MetricCreateNewMetric = BusterSocketRequestBase<
|
||||
'/metrics/post',
|
||||
{
|
||||
dataset_id: string | null;
|
||||
metric_id: string | null;
|
||||
suggestion_id?: string | null;
|
||||
prompt?: string;
|
||||
message_id?: string; //only send if we want to REPLACE current message
|
||||
draft_session_id?: string;
|
||||
}
|
||||
>;
|
||||
|
||||
export type MetricUpdateMetric = BusterSocketRequestBase<
|
||||
'/metrics/update',
|
||||
{
|
||||
id: string; //metric id
|
||||
title?: string;
|
||||
save_to_dashboard?: string;
|
||||
remove_from_dashboard?: string; // dashboard_id optional
|
||||
add_to_collections?: string[]; // collection_id
|
||||
remove_from_collections?: string[]; // collection_id
|
||||
save_draft?: boolean;
|
||||
} & ShareRequest
|
||||
>;
|
||||
|
||||
export type MetricUpdateMessage = BusterSocketRequestBase<
|
||||
'/metrics/messages/update',
|
||||
{
|
||||
id: string; //messageid id
|
||||
chart_config?: BusterChartConfigProps;
|
||||
title?: string;
|
||||
sql?: string;
|
||||
feedback?: 'negative';
|
||||
status?: VerificationStatus;
|
||||
}
|
||||
chart_config?: BusterChartConfigProps;
|
||||
save_draft?: boolean; //send if we want the metric (which is currently in draft) to be saved
|
||||
feedback?: 'negative'; //send if we want to update the feedback of the metric
|
||||
status?: VerificationStatus; //admin only: send if we want to update the status of the metric
|
||||
} & ShareRequest
|
||||
>;
|
||||
|
||||
export type MetricDelete = BusterSocketRequestBase<'/metrics/delete', { ids: string[] }>;
|
||||
|
||||
export type MetricGetDataByMessageId = BusterSocketRequestBase<'/metrics/data', { id: string }>;
|
||||
|
||||
export type MetricSearch = BusterSocketRequestBase<
|
||||
'/metrics/search',
|
||||
{
|
||||
prompt: string;
|
||||
}
|
||||
>;
|
||||
export type MetricSearch = BusterSocketRequestBase<'/metrics/search', { prompt: string }>;
|
||||
|
||||
export type MetricDuplicate = BusterSocketRequestBase<
|
||||
'/metrics/duplicate',
|
||||
|
@ -84,9 +60,7 @@ export type MetricEmits =
|
|||
| MetricListEmitPayload
|
||||
| MetricUnsubscribeEmitPayload
|
||||
| MetricUpdateMetric
|
||||
| MetricCreateNewMetric
|
||||
| MetricSubscribeToMetric
|
||||
| MetricDelete
|
||||
| MetricUpdateMessage
|
||||
| MetricGetDataByMessageId
|
||||
| MetricSearch;
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
export default function Page() {
|
||||
return <></>;
|
||||
import { MetricController } from '@appControllers/MetricController';
|
||||
import { AppAssetCheckLayout } from '@appLayouts/AppAssetCheckLayout';
|
||||
|
||||
export default function Page({
|
||||
params: { chatId, metricId }
|
||||
}: {
|
||||
params: { chatId: string; metricId: string };
|
||||
}) {
|
||||
return (
|
||||
<AppAssetCheckLayout metricId={metricId} type="metric">
|
||||
<MetricController metricId={metricId} />
|
||||
</AppAssetCheckLayout>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { AppAssetCheckLayout } from '@/app/app/_layouts/AppAssetCheckLayout';
|
||||
import { MetricController } from '@appControllers/MetricController';
|
||||
import { AppAssetCheckLayout } from '@appLayouts/AppAssetCheckLayout';
|
||||
|
||||
export default function MetricPage({
|
||||
params: { metricId },
|
||||
|
@ -10,16 +11,8 @@ export default function MetricPage({
|
|||
const embedView = embed === 'true';
|
||||
|
||||
return (
|
||||
<AppAssetCheckLayout metricId={metricId} type="metric">
|
||||
<></>
|
||||
</AppAssetCheckLayout>
|
||||
// <AppAssetCheckLayout metricId={metricId} type="metric">
|
||||
<MetricController metricId={metricId} />
|
||||
// </AppAssetCheckLayout>
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
/* <MetricContentController
|
||||
metricLayout={metricLayout}
|
||||
metricId={metricId}
|
||||
chartOnlyView={embedView}
|
||||
/> */
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export const MetricController: React.FC<{
|
||||
metricId: string;
|
||||
}> = React.memo(({ metricId }) => {
|
||||
return <div>MetricController</div>;
|
||||
});
|
||||
|
||||
MetricController.displayName = 'MetricController';
|
|
@ -0,0 +1 @@
|
|||
export * from './MetricController';
|
|
@ -17,7 +17,7 @@ export const ChatContainer = React.memo(
|
|||
}, [chatContentRef, scroll?.top]);
|
||||
|
||||
return (
|
||||
<div ref={ref} className="flex h-full w-full flex-col">
|
||||
<div ref={ref} className="flex h-full w-full min-w-[225px] flex-col">
|
||||
<ChatHeader showScrollOverflow={showScrollOverflow} />
|
||||
<ChatContent chatContentRef={chatContentRef} />
|
||||
</div>
|
||||
|
|
|
@ -48,7 +48,7 @@ export const ChatInput: React.FC = React.memo(() => {
|
|||
<div
|
||||
className={cx(
|
||||
styles.inputCard,
|
||||
'z-10 flex flex-col items-center space-y-1.5 px-3 pb-2 pt-0.5'
|
||||
'z-10 mx-3 mt-0.5 flex flex-col items-center space-y-1.5 overflow-hidden pb-2'
|
||||
)}>
|
||||
<div
|
||||
className={cx(
|
||||
|
@ -60,7 +60,7 @@ export const ChatInput: React.FC = React.memo(() => {
|
|||
variant="borderless"
|
||||
onBlur={onBlurInput}
|
||||
onFocus={onFocusInput}
|
||||
className="inline-block !pl-3.5 !pr-9 align-middle"
|
||||
className="inline-block !pl-3.5 !pr-9 !pt-2 align-middle"
|
||||
placeholder="Ask a follow up..."
|
||||
value={inputValue}
|
||||
autoFocus={true}
|
||||
|
@ -78,9 +78,7 @@ export const ChatInput: React.FC = React.memo(() => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<Text size="xs" type="tertiary">
|
||||
Our AI may make mistakes. Check important info.
|
||||
</Text>
|
||||
<AIWarning />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -105,3 +103,15 @@ const useStyles = createStyles(({ token, css }) => ({
|
|||
}
|
||||
`
|
||||
}));
|
||||
|
||||
const AIWarning = React.memo(() => {
|
||||
return (
|
||||
<div className="w-full overflow-hidden truncate text-center">
|
||||
<Text size="xs" type="tertiary" className="truncate">
|
||||
Our AI may make mistakes. Check important info.
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
AIWarning.displayName = 'AIWarning';
|
||||
|
|
|
@ -3,7 +3,7 @@ import {
|
|||
createContext,
|
||||
useContextSelector
|
||||
} from '@fluentui/react-context-selector';
|
||||
import React, { PropsWithChildren, useTransition } from 'react';
|
||||
import React, { PropsWithChildren, useState, useTransition } from 'react';
|
||||
import type { SelectedFile } from '../interfaces';
|
||||
import type { ChatSplitterProps } from '../ChatLayout';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
@ -30,8 +30,6 @@ export const useChatLayout = ({
|
|||
const [isPending, startTransition] = useTransition();
|
||||
const onChangePage = useAppLayoutContextSelector((state) => state.onChangePage);
|
||||
|
||||
const selectedLayout = defaultSelectedLayout;
|
||||
|
||||
const animateOpenSplitter = useMemoizedFn((side: 'left' | 'right' | 'both') => {
|
||||
if (appSplitterRef.current) {
|
||||
const { animateWidth, isSideClosed } = appSplitterRef.current;
|
||||
|
@ -41,6 +39,8 @@ export const useChatLayout = ({
|
|||
animateWidth('100%', 'right');
|
||||
} else if (side === 'both' && (isSideClosed('right') || isSideClosed('left'))) {
|
||||
animateWidth(DEFAULT_CHAT_OPTION, 'left');
|
||||
setIsPureChat(false);
|
||||
setIsPureFile(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -56,7 +56,6 @@ export const useChatLayout = ({
|
|||
|
||||
if (route) {
|
||||
onChangePage(route);
|
||||
setIsPureChat(false);
|
||||
startTransition(() => {
|
||||
animateOpenSplitter('both');
|
||||
});
|
||||
|
@ -64,15 +63,34 @@ export const useChatLayout = ({
|
|||
});
|
||||
|
||||
const onCollapseFileClick = useMemoizedFn((close?: boolean) => {
|
||||
const isCloseAction = close ?? selectedLayout === 'both';
|
||||
if (isCloseAction) {
|
||||
animateOpenSplitter('left');
|
||||
const isCloseAction = close ?? isCollapseOpen;
|
||||
|
||||
if (defaultSelectedLayout === 'file') {
|
||||
if (!isCloseAction && defaultSelectedFile) {
|
||||
setIsCollapseOpen(true);
|
||||
animateOpenSplitter('both');
|
||||
} else {
|
||||
setIsCollapseOpen(false);
|
||||
animateOpenSplitter('right');
|
||||
}
|
||||
} else {
|
||||
animateOpenSplitter('both');
|
||||
if (isCloseAction) {
|
||||
animateOpenSplitter('left');
|
||||
} else {
|
||||
animateOpenSplitter('both');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const { setIsPureChat, isPureFile, isPureChat } = useAutoSetLayout({
|
||||
const {
|
||||
setIsPureChat,
|
||||
setIsCollapseOpen,
|
||||
isPureFile,
|
||||
isPureChat,
|
||||
setIsPureFile,
|
||||
collapseDirection,
|
||||
isCollapseOpen
|
||||
} = useAutoSetLayout({
|
||||
defaultSelectedLayout
|
||||
});
|
||||
|
||||
|
@ -83,6 +101,8 @@ export const useChatLayout = ({
|
|||
|
||||
return {
|
||||
...fileLayoutContext,
|
||||
collapseDirection,
|
||||
isCollapseOpen,
|
||||
isPureFile,
|
||||
isPureChat,
|
||||
onSetSelectedFile,
|
||||
|
|
|
@ -7,7 +7,7 @@ interface FileContainerProps {
|
|||
|
||||
export const FileContainer: React.FC<FileContainerProps> = React.memo(({ children }) => {
|
||||
return (
|
||||
<div className="flex min-w-[370px] flex-col">
|
||||
<div className="flex min-w-[325px] flex-col">
|
||||
<FileContainerHeader />
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
@ -1,29 +1,38 @@
|
|||
import { AppMaterialIcons } from '@/components';
|
||||
import { Button } from 'antd';
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { useChatLayoutContextSelector } from '../../ChatLayoutContext';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
const animation = {
|
||||
initial: { opacity: 0 },
|
||||
animate: { opacity: 1 },
|
||||
exit: { opacity: 0 }
|
||||
};
|
||||
|
||||
export const CollapseFileButton: React.FC<{
|
||||
showCollapseButton: boolean;
|
||||
isOpen: boolean;
|
||||
}> = React.memo(({ showCollapseButton, isOpen }) => {
|
||||
const onCollapseFileClick = useChatLayoutContextSelector((state) => state.onCollapseFileClick);
|
||||
const icon = !isOpen ? 'keyboard_double_arrow_left' : 'keyboard_double_arrow_right';
|
||||
collapseDirection: 'left' | 'right';
|
||||
onCollapseFileClick: (value?: boolean) => void;
|
||||
}> = React.memo(({ showCollapseButton, isOpen, collapseDirection, onCollapseFileClick }) => {
|
||||
const icon = useMemo(() => {
|
||||
if (collapseDirection === 'left') {
|
||||
return isOpen ? 'keyboard_double_arrow_left' : 'keyboard_double_arrow_right';
|
||||
}
|
||||
return isOpen ? 'keyboard_double_arrow_right' : 'keyboard_double_arrow_left';
|
||||
}, [isOpen, collapseDirection]);
|
||||
|
||||
const onClick = useMemoizedFn(() => {
|
||||
onCollapseFileClick();
|
||||
});
|
||||
|
||||
const animation = useMemo(() => {
|
||||
const baseAnimation = {
|
||||
initial: { opacity: 0 },
|
||||
animate: { opacity: 1 },
|
||||
exit: { opacity: 0 }
|
||||
};
|
||||
|
||||
return baseAnimation;
|
||||
}, [collapseDirection, isOpen]);
|
||||
|
||||
return (
|
||||
<AnimatePresence mode="wait">
|
||||
<AnimatePresence mode="wait" initial={false}>
|
||||
{showCollapseButton && (
|
||||
<motion.div variants={animation}>
|
||||
<Button onClick={onClick} type="text" icon={<AppMaterialIcons icon={icon} />}></Button>
|
||||
|
|
|
@ -23,9 +23,11 @@ export const FileContainerHeader: React.FC = React.memo(() => {
|
|||
const { styles, cx } = useStyles();
|
||||
const selectedFileType = useChatLayoutContextSelector((x) => x.selectedFileType);
|
||||
const selectedFileView = useChatLayoutContextSelector((x) => x.selectedFileView);
|
||||
const onCollapseFileClick = useChatLayoutContextSelector((state) => state.onCollapseFileClick);
|
||||
const collapseDirection = useChatLayoutContextSelector((state) => state.collapseDirection);
|
||||
const isCollapseOpen = useChatLayoutContextSelector((state) => state.isCollapseOpen);
|
||||
|
||||
const showCollapseButton = true;
|
||||
const isCollapseOpen = true; //I could get the defaultSelectedLayout from the context and check if it is 'both'?
|
||||
|
||||
const SelectedFileSegment = React.useMemo(
|
||||
() => (selectedFileType ? SelectedFileSegmentRecord[selectedFileType] : () => <></>),
|
||||
|
@ -39,9 +41,17 @@ export const FileContainerHeader: React.FC = React.memo(() => {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={cx(styles.container, 'flex w-full items-center justify-between space-x-3.5 px-3')}>
|
||||
className={cx(
|
||||
styles.container,
|
||||
'flex w-full items-center justify-between space-x-1 overflow-hidden px-3'
|
||||
)}>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<CollapseFileButton showCollapseButton={showCollapseButton} isOpen={isCollapseOpen} />
|
||||
<CollapseFileButton
|
||||
collapseDirection={collapseDirection}
|
||||
showCollapseButton={showCollapseButton}
|
||||
isOpen={isCollapseOpen}
|
||||
onCollapseFileClick={onCollapseFileClick}
|
||||
/>
|
||||
{selectedFileView && <SelectedFileSegment selectedFileView={selectedFileView} />}
|
||||
</div>
|
||||
<SelectedFileButtons selectedFileView={selectedFileView} />
|
||||
|
|
|
@ -1,22 +1,31 @@
|
|||
import { useUpdateLayoutEffect } from 'ahooks';
|
||||
import { useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
export const useAutoSetLayout = ({
|
||||
defaultSelectedLayout
|
||||
}: {
|
||||
defaultSelectedLayout: 'chat' | 'file' | 'both' | undefined;
|
||||
}): {
|
||||
isPureFile: boolean;
|
||||
isPureChat: boolean;
|
||||
setIsPureChat: (value: boolean) => void;
|
||||
} => {
|
||||
}) => {
|
||||
const [isPureFile, setIsPureFile] = useState(defaultSelectedLayout === 'file');
|
||||
const [isPureChat, setIsPureChat] = useState(defaultSelectedLayout === 'chat');
|
||||
const [isCollapseOpen, setIsCollapseOpen] = useState(isPureChat ? true : false);
|
||||
|
||||
useUpdateLayoutEffect(() => {
|
||||
if (isPureFile === true) setIsPureFile(defaultSelectedLayout === 'file');
|
||||
if (isPureChat === true) setIsPureChat(defaultSelectedLayout === 'chat');
|
||||
}, [defaultSelectedLayout]);
|
||||
|
||||
return { isPureFile, isPureChat, setIsPureChat };
|
||||
const collapseDirection: 'left' | 'right' = useMemo(() => {
|
||||
return defaultSelectedLayout === 'file' ? 'left' : 'right';
|
||||
}, [defaultSelectedLayout]);
|
||||
|
||||
return {
|
||||
isPureFile,
|
||||
isPureChat,
|
||||
setIsPureChat,
|
||||
setIsPureFile,
|
||||
collapseDirection,
|
||||
setIsCollapseOpen,
|
||||
isCollapseOpen
|
||||
};
|
||||
};
|
||||
|
|
|
@ -205,7 +205,7 @@ const MetricsEmptyState: React.FC<{
|
|||
<ListEmptyStateWithButton
|
||||
title="You don’t have any logs yet."
|
||||
description="You don’t have any logs. As soon as you do, they will start to appear here."
|
||||
buttonText="New metric"
|
||||
buttonText="New chat"
|
||||
onClick={openNewMetricModal}
|
||||
/>
|
||||
);
|
||||
|
@ -215,7 +215,7 @@ const MetricsEmptyState: React.FC<{
|
|||
<ListEmptyStateWithButton
|
||||
title="You don’t have any metrics yet."
|
||||
description="You don’t have any metrics. As soon as you do, they will start to appear here."
|
||||
buttonText="New metric"
|
||||
buttonText="New chat"
|
||||
onClick={openNewMetricModal}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -37,7 +37,7 @@ export const MetricSidebarHeader: React.FC<{
|
|||
icon={<AppMaterialIcons icon="edit_square" />}
|
||||
type="default"
|
||||
onClick={onToggleChatsModalPreflight}>
|
||||
New metric
|
||||
New Chat
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue