mirror of https://github.com/buster-so/buster.git
add delete chat area
This commit is contained in:
parent
7d95f1f008
commit
98d62419b9
|
@ -40,8 +40,11 @@ export interface BusterDashboard
|
|||
deleted_at: string | null;
|
||||
description: string | null;
|
||||
id: string;
|
||||
name: string;
|
||||
title: string;
|
||||
updated_at: string | null;
|
||||
updated_by: string;
|
||||
status: VerificationStatus;
|
||||
version_number: number;
|
||||
file: string; //yaml file
|
||||
file_name: string;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ export const DashboardViewDashboardController: React.FC<DashboardViewProps> = ({
|
|||
});
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-3 overflow-y-auto p-5">
|
||||
<div className="flex flex-col space-y-3 overflow-y-auto p-10">
|
||||
<DashboardEditTitles
|
||||
onUpdateDashboard={onUpdateDashboard}
|
||||
allowEdit={allowEdit}
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
import { Dropdown, MenuProps } from 'antd';
|
||||
import React, { useMemo } from 'react';
|
||||
import { HeaderOptionsRecord } from './config';
|
||||
import { useChatContextSelector } from '../../../ChatContext';
|
||||
import { AppMaterialIcons } from '@/components/icons';
|
||||
|
||||
export const ChatContainerHeaderDropdown: React.FC<{
|
||||
children: React.ReactNode;
|
||||
}> = React.memo(({ children }) => {
|
||||
const selectedFileType = useChatContextSelector((state) => state.selectedFileType);
|
||||
const hasChat = useChatContextSelector((state) => state.hasChat);
|
||||
const onDeleteChat = useChatContextSelector((state) => state.onDeleteChat);
|
||||
|
||||
const menuItem: MenuProps['items'] = useMemo(() => {
|
||||
if (!selectedFileType || !(selectedFileType in HeaderOptionsRecord))
|
||||
return [] as MenuProps['items'];
|
||||
return HeaderOptionsRecord[selectedFileType]();
|
||||
}, [selectedFileType]);
|
||||
return [
|
||||
{
|
||||
label: 'Delete chat',
|
||||
key: 'delete',
|
||||
icon: <AppMaterialIcons icon="delete" />,
|
||||
onClick: () => onDeleteChat()
|
||||
}
|
||||
];
|
||||
}, []);
|
||||
|
||||
const menu = useMemo(() => {
|
||||
return {
|
||||
|
@ -23,7 +29,7 @@ export const ChatContainerHeaderDropdown: React.FC<{
|
|||
return (
|
||||
<div>
|
||||
<Dropdown menu={menu} trigger={['click']}>
|
||||
{children}
|
||||
{hasChat ? children : null}
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import type { FileType } from '@/api/asset_interfaces';
|
||||
import { AppMaterialIcons } from '@/components/icons';
|
||||
import type { MenuProps } from 'antd';
|
||||
|
||||
export const HeaderOptionsRecord: Record<FileType, () => MenuProps['items']> = {
|
||||
dataset: () => [
|
||||
{
|
||||
label: 'Delete',
|
||||
key: 'delete',
|
||||
icon: <AppMaterialIcons icon="delete" />
|
||||
}
|
||||
],
|
||||
collection: () => [
|
||||
{
|
||||
label: 'Delete',
|
||||
key: 'delete',
|
||||
icon: <AppMaterialIcons icon="delete" />
|
||||
}
|
||||
],
|
||||
metric: () => [
|
||||
{
|
||||
label: 'Delete',
|
||||
key: 'delete',
|
||||
icon: <AppMaterialIcons icon="delete" />
|
||||
}
|
||||
],
|
||||
dashboard: () => [
|
||||
{
|
||||
label: 'Delete',
|
||||
key: 'delete',
|
||||
icon: <AppMaterialIcons icon="delete" />
|
||||
}
|
||||
],
|
||||
term: () => [],
|
||||
value: () => []
|
||||
};
|
|
@ -6,6 +6,7 @@ import {
|
|||
} from '@fluentui/react-context-selector';
|
||||
import { useBusterChatIndividual } from '@/context/Chats';
|
||||
import type { SelectedFile } from '../interfaces';
|
||||
import { useChatAssociation } from './useChatAssosciation';
|
||||
|
||||
export const useChatContext = ({
|
||||
chatId,
|
||||
|
@ -16,12 +17,11 @@ export const useChatContext = ({
|
|||
}) => {
|
||||
const selectedFileId = defaultSelectedFile?.id;
|
||||
const selectedFileType = defaultSelectedFile?.type;
|
||||
const metricId = defaultSelectedFile?.type === 'metric' ? defaultSelectedFile?.id : undefined;
|
||||
|
||||
//CHAT
|
||||
const { chat } = useBusterChatIndividual({
|
||||
chatId,
|
||||
metricId
|
||||
defaultSelectedFile
|
||||
});
|
||||
const hasChat = !!chatId && !!chat;
|
||||
const chatTitle = chat?.title;
|
||||
|
@ -33,7 +33,11 @@ export const useChatContext = ({
|
|||
//MESSAGES
|
||||
const currentMessageId = chatMessages[chatMessages.length - 1]?.id;
|
||||
|
||||
//ASSOCIATION
|
||||
const association = useChatAssociation({ chatId });
|
||||
|
||||
return {
|
||||
...association,
|
||||
hasChat,
|
||||
hasFile,
|
||||
selectedFileId,
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
export const useChatAssociation = ({ chatId }: { chatId?: string }) => {
|
||||
const onDeleteChat = useMemoizedFn(async (chatIdProp?: string) => {
|
||||
console.log('delete chat', chatIdProp);
|
||||
});
|
||||
|
||||
return { onDeleteChat };
|
||||
};
|
|
@ -22,6 +22,7 @@ export const CreateChatButton = React.memo(() => {
|
|||
color="default"
|
||||
variant="solid"
|
||||
type="primary"
|
||||
className="ml-1.5"
|
||||
icon={<AppMaterialIcons icon="stars" />}>
|
||||
Edit
|
||||
</Button>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { FileContainerButtonsProps } from './interfaces';
|
||||
import { FileButtonContainer } from './FileButtonContainer';
|
||||
import { useChatContextSelector } from '../../ChatContext';
|
||||
|
@ -7,8 +7,13 @@ import { HideButtonContainer } from './HideButtonContainer';
|
|||
import { useChatLayoutContextSelector } from '../../ChatLayoutContext';
|
||||
import { CreateChatButton } from './CreateChatButtont';
|
||||
import { ShareDashboardButton } from '@appComponents/Buttons/ShareDashboardButton';
|
||||
import { Button } from 'antd';
|
||||
import { Button, Dropdown } from 'antd';
|
||||
import { AppMaterialIcons } from '@/components/icons';
|
||||
import { MenuProps } from 'antd/lib';
|
||||
import { useDashboardContextSelector } from '@/context/Dashboards';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||
import { BusterRoutes } from '@/routes';
|
||||
|
||||
export const DashboardContainerHeaderButtons: React.FC<FileContainerButtonsProps> = React.memo(
|
||||
() => {
|
||||
|
@ -19,6 +24,7 @@ export const DashboardContainerHeaderButtons: React.FC<FileContainerButtonsProps
|
|||
<FileButtonContainer>
|
||||
<SaveToCollectionButton />
|
||||
<ShareDashboardButton dashboardId={selectedFileId} /> <AddContentToDashboardButton />
|
||||
<ThreeDotMenu dashboardId={selectedFileId} />
|
||||
<HideButtonContainer show={isPureFile}>
|
||||
<CreateChatButton />
|
||||
</HideButtonContainer>
|
||||
|
@ -43,3 +49,31 @@ const AddContentToDashboardButton = React.memo(() => {
|
|||
);
|
||||
});
|
||||
AddContentToDashboardButton.displayName = 'AddContentToDashboardButton';
|
||||
|
||||
const ThreeDotMenu = React.memo(({ dashboardId }: { dashboardId: string }) => {
|
||||
const onDeleteDashboard = useDashboardContextSelector((x) => x.onDeleteDashboard);
|
||||
const onChangePage = useAppLayoutContextSelector((x) => x.onChangePage);
|
||||
|
||||
const menu: MenuProps = useMemo(() => {
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
label: 'Delete',
|
||||
key: 'delete',
|
||||
icon: <AppMaterialIcons icon="delete" />,
|
||||
onClick: async () => {
|
||||
await onDeleteDashboard(dashboardId);
|
||||
onChangePage({ route: BusterRoutes.APP_DASHBOARDS });
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}, [dashboardId, onDeleteDashboard, onChangePage]);
|
||||
|
||||
return (
|
||||
<Dropdown menu={menu}>
|
||||
<Button type="text" icon={<AppMaterialIcons icon="more_horiz" />} />
|
||||
</Dropdown>
|
||||
);
|
||||
});
|
||||
ThreeDotMenu.displayName = 'ThreeDotMenu';
|
||||
|
|
|
@ -11,8 +11,8 @@ import { createMockResponseMessageThought, MOCK_CHAT } from './MOCK_CHAT';
|
|||
import { IBusterChat } from './interfaces';
|
||||
import { chatUpgrader } from './helpers';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useBusterMetricsContextSelector } from '../Metrics';
|
||||
import { fallbackToMetricChat } from './helpers/fallbackToMetricChat';
|
||||
import { useFileFallback } from './helpers/useFileFallback';
|
||||
import { SelectedFile } from '@/app/app/_layouts/ChatLayout';
|
||||
|
||||
export const useBusterChat = () => {
|
||||
const busterSocket = useBusterWebSocket();
|
||||
|
@ -104,24 +104,19 @@ export const useBusterChatContextSelector = <T,>(
|
|||
|
||||
export const useBusterChatIndividual = ({
|
||||
chatId: chatIdProp,
|
||||
metricId
|
||||
defaultSelectedFile
|
||||
}: {
|
||||
chatId?: string;
|
||||
//if metricId is provided (without a chatId), we can assume that the chat is a new chat starting from a metric
|
||||
metricId?: string;
|
||||
defaultSelectedFile?: SelectedFile;
|
||||
}) => {
|
||||
const chatId = chatIdProp || '';
|
||||
const chat: IBusterChat | undefined = useBusterChatContextSelector((x) => x.chats[chatId]);
|
||||
const subscribeToChat = useBusterChatContextSelector((x) => x.subscribeToChat);
|
||||
const unsubscribeFromChat = useBusterChatContextSelector((x) => x.unsubscribeFromChat);
|
||||
const metricTitle = useBusterMetricsContextSelector((x) => x.metrics[metricId || '']?.title);
|
||||
const metricVersionNumber = useBusterMetricsContextSelector(
|
||||
(x) => x.metrics[metricId || '']?.version_number
|
||||
);
|
||||
|
||||
const memoizedFallbackToMetricChat = useMemo(() => {
|
||||
return fallbackToMetricChat({ id: metricId || '', title: metricTitle, metricVersionNumber });
|
||||
}, [metricId, metricVersionNumber, metricTitle]);
|
||||
const memoizedFallbackToMetricChat = useFileFallback({
|
||||
defaultSelectedFile
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (chatId) subscribeToChat({ chatId });
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
import { IBusterChat } from '../interfaces';
|
||||
|
||||
export const fallbackToMetricChat = (metric: {
|
||||
id: string;
|
||||
title: string | undefined;
|
||||
metricVersionNumber: number;
|
||||
}): IBusterChat => {
|
||||
return {
|
||||
id: metric.id,
|
||||
isNewChat: true,
|
||||
isFollowupMessage: false,
|
||||
messages: [
|
||||
{
|
||||
request_message: null,
|
||||
response_messages: [
|
||||
{
|
||||
id: 'init',
|
||||
type: 'text',
|
||||
message: `I’ve pulled in your metric. How can I help? Is there anything you'd like to modify?`
|
||||
},
|
||||
{
|
||||
id: metric.id,
|
||||
type: 'file',
|
||||
file_type: 'metric',
|
||||
file_name: metric.title || 'New Metric',
|
||||
version_number: metric.metricVersionNumber,
|
||||
version_id: metric.id,
|
||||
metadata: [
|
||||
{
|
||||
status: 'completed',
|
||||
message: 'Retrieved metric'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
created_at: '',
|
||||
id: 'init-message',
|
||||
isCompletedStream: false
|
||||
}
|
||||
],
|
||||
title: metric.title || 'New Chat',
|
||||
is_favorited: false,
|
||||
updated_at: '',
|
||||
created_at: '',
|
||||
created_by: '',
|
||||
created_by_id: '',
|
||||
created_by_name: '',
|
||||
created_by_avatar: ''
|
||||
};
|
||||
};
|
|
@ -0,0 +1,115 @@
|
|||
import { SelectedFile } from '@/app/app/_layouts/ChatLayout';
|
||||
import { useDashboardContextSelector } from '@/context/Dashboards';
|
||||
import { useBusterMetricsContextSelector } from '@/context/Metrics';
|
||||
import { useMemo } from 'react';
|
||||
import { IBusterChat } from '../interfaces';
|
||||
|
||||
export const useFileFallback = ({
|
||||
defaultSelectedFile
|
||||
}: {
|
||||
//if metricId is provided (without a chatId), we can assume that the chat is a new chat starting from a metric
|
||||
defaultSelectedFile?: SelectedFile;
|
||||
}) => {
|
||||
const fileId = defaultSelectedFile?.id || '';
|
||||
const metricTitle = useBusterMetricsContextSelector((x) => x.metrics[fileId]?.title);
|
||||
const metricVersionNumber = useBusterMetricsContextSelector(
|
||||
(x) => x.metrics[fileId]?.version_number
|
||||
);
|
||||
const dashboardTitle = useDashboardContextSelector((x) => x.dashboards[fileId]?.dashboard?.title);
|
||||
const dashboardVersionNumber = useDashboardContextSelector(
|
||||
(x) => x.dashboards[fileId]?.dashboard?.version_number
|
||||
);
|
||||
|
||||
const fileType: 'metric' | 'dashboard' = useMemo(() => {
|
||||
if (defaultSelectedFile?.type === 'metric') {
|
||||
return 'metric';
|
||||
} else {
|
||||
return 'dashboard';
|
||||
}
|
||||
}, [defaultSelectedFile]);
|
||||
|
||||
const title = useMemo(() => {
|
||||
if (fileType === 'metric') {
|
||||
return metricTitle;
|
||||
} else if (fileType === 'dashboard') {
|
||||
return dashboardTitle;
|
||||
}
|
||||
}, [fileType, metricTitle, dashboardTitle]);
|
||||
|
||||
const versionNumber = useMemo(() => {
|
||||
if (fileType === 'metric') {
|
||||
return metricVersionNumber || 1;
|
||||
} else if (fileType === 'dashboard') {
|
||||
return dashboardVersionNumber || 1;
|
||||
}
|
||||
return 1;
|
||||
}, [fileType, metricVersionNumber, dashboardVersionNumber]);
|
||||
|
||||
const memoizedFallbackToMetricChat = useMemo(() => {
|
||||
console.log(fileType, defaultSelectedFile);
|
||||
return fallbackToFileChat({
|
||||
id: fileId,
|
||||
title: title,
|
||||
versionNumber: versionNumber,
|
||||
type: fileType
|
||||
});
|
||||
}, [fileId, title, versionNumber, fileType]);
|
||||
|
||||
return memoizedFallbackToMetricChat;
|
||||
};
|
||||
|
||||
const fallbackToFileChat = ({
|
||||
id,
|
||||
title,
|
||||
versionNumber,
|
||||
type = 'metric'
|
||||
}: {
|
||||
id: string;
|
||||
title: string | undefined;
|
||||
versionNumber: number;
|
||||
type?: 'metric' | 'dashboard';
|
||||
}): IBusterChat => {
|
||||
console.log(type);
|
||||
return {
|
||||
id,
|
||||
isNewChat: true,
|
||||
isFollowupMessage: false,
|
||||
messages: [
|
||||
{
|
||||
request_message: null,
|
||||
response_messages: [
|
||||
{
|
||||
id: 'init',
|
||||
type: 'text',
|
||||
message: `I’ve pulled in your ${type}. How can I help? Is there anything you'd like to modify?`
|
||||
},
|
||||
{
|
||||
id,
|
||||
type: 'file',
|
||||
file_type: type,
|
||||
file_name: title || `New ${type}`,
|
||||
version_number: versionNumber,
|
||||
version_id: id,
|
||||
metadata: [
|
||||
{
|
||||
status: 'completed',
|
||||
message: `Retrieved ${type}`
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
created_at: '',
|
||||
id: 'init-message',
|
||||
isCompletedStream: false
|
||||
}
|
||||
],
|
||||
title: title || 'New Chat',
|
||||
is_favorited: false,
|
||||
updated_at: '',
|
||||
created_at: '',
|
||||
created_by: '',
|
||||
created_by_id: '',
|
||||
created_by_name: '',
|
||||
created_by_avatar: ''
|
||||
};
|
||||
};
|
|
@ -39,6 +39,7 @@ export const useDashboards = () => {
|
|||
//DASHBOARD INDIVIDUAL
|
||||
const {
|
||||
dashboards,
|
||||
getDashboardMemoized,
|
||||
removeItemFromIndividualDashboard,
|
||||
refreshDashboard,
|
||||
onVerifiedDashboard,
|
||||
|
@ -99,6 +100,7 @@ export const useDashboards = () => {
|
|||
unsubscribeFromDashboardsList,
|
||||
onShareDashboard,
|
||||
openAddContentModal,
|
||||
getDashboardMemoized,
|
||||
setOpenAddContentModal
|
||||
};
|
||||
};
|
||||
|
|
|
@ -72,7 +72,26 @@ export const generateMockDashboard = (numMetrics: number): DashboardMockResponse
|
|||
|
||||
const dashboard: BusterDashboard = {
|
||||
id: '123',
|
||||
name: 'Mock Dashboard',
|
||||
title: 'Mock Dashboard',
|
||||
file: `title: Mock Dashboard
|
||||
description: A sample dashboard configuration
|
||||
version: 1.0
|
||||
layout:
|
||||
rows:
|
||||
- id: row1
|
||||
columns:
|
||||
- width: 6
|
||||
metric_id: metric-1
|
||||
- width: 6
|
||||
metric_id: metric-2
|
||||
- id: row2
|
||||
columns:
|
||||
- width: 12
|
||||
metric_id: metric-3
|
||||
theme: light
|
||||
refresh_interval: 300`,
|
||||
file_name: 'mock-file.yaml',
|
||||
version_number: 1,
|
||||
description: null,
|
||||
created_at: new Date().toISOString(),
|
||||
created_by: 'user-123',
|
||||
|
@ -113,4 +132,4 @@ export const generateMockDashboard = (numMetrics: number): DashboardMockResponse
|
|||
// Example usage:
|
||||
// const { dashboard, response } = generateMockDashboard(12);
|
||||
|
||||
export const MOCK_DASHBOARD_RESPONSE = generateMockDashboard(15).response;
|
||||
export const MOCK_DASHBOARD_RESPONSE = generateMockDashboard(3).response;
|
||||
|
|
|
@ -7,6 +7,7 @@ import { useDashboardUpdateConfig } from './useDashboardUpdateConfig';
|
|||
import { useDashboardSubscribe } from './useDashboardSubscribe';
|
||||
import { useDashboardCreate } from './useDashboardCreate';
|
||||
import { useShareDashboard } from './useShareDashboard';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
export const useDashboardIndividual = ({
|
||||
refreshDashboardsList,
|
||||
|
@ -53,6 +54,10 @@ export const useDashboardIndividual = ({
|
|||
onUpdateDashboard
|
||||
});
|
||||
|
||||
const getDashboardMemoized = useMemoizedFn((id: string) => {
|
||||
return dashboards[id];
|
||||
});
|
||||
|
||||
return {
|
||||
dashboards,
|
||||
onRemoveFromCollection,
|
||||
|
@ -63,6 +68,7 @@ export const useDashboardIndividual = ({
|
|||
onVerifiedDashboard,
|
||||
openedDashboardId,
|
||||
creatingDashboard,
|
||||
getDashboardMemoized,
|
||||
onCreateNewDashboard,
|
||||
onDeleteDashboard,
|
||||
subscribeToDashboard,
|
||||
|
|
Loading…
Reference in New Issue