balibabu
feat: API access key management #2846 (#2865)
f850783
raw
history blame
11.7 kB
import { ReactComponent as ChatAppCube } from '@/assets/svg/chat-app-cube.svg';
import RenameModal from '@/components/rename-modal';
import { DeleteOutlined, EditOutlined, KeyOutlined } from '@ant-design/icons';
import {
Avatar,
Button,
Card,
Divider,
Dropdown,
Flex,
MenuProps,
Space,
Spin,
Tag,
Tooltip,
Typography,
} from 'antd';
import { MenuItemProps } from 'antd/lib/menu/MenuItem';
import classNames from 'classnames';
import { useCallback, useState } from 'react';
import ChatConfigurationModal from './chat-configuration-modal';
import ChatContainer from './chat-container';
import {
useDeleteConversation,
useDeleteDialog,
useEditDialog,
useHandleItemHover,
useRenameConversation,
useSelectDerivedConversationList,
} from './hooks';
import SvgIcon from '@/components/svg-icon';
import {
useClickConversationCard,
useClickDialogCard,
useFetchNextDialogList,
useGetChatSearchParams,
} from '@/hooks/chat-hooks';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
import { IDialog } from '@/interfaces/database/chat';
import ChatIdModal from './chat-id-modal';
import styles from './index.less';
const { Text } = Typography;
const Chat = () => {
const { data: dialogList, loading: dialogLoading } = useFetchNextDialogList();
const { onRemoveDialog } = useDeleteDialog();
const { onRemoveConversation } = useDeleteConversation();
const { handleClickDialog } = useClickDialogCard();
const { handleClickConversation } = useClickConversationCard();
const { dialogId, conversationId } = useGetChatSearchParams();
const {
list: conversationList,
addTemporaryConversation,
loading: conversationLoading,
} = useSelectDerivedConversationList();
const { activated, handleItemEnter, handleItemLeave } = useHandleItemHover();
const {
activated: conversationActivated,
handleItemEnter: handleConversationItemEnter,
handleItemLeave: handleConversationItemLeave,
} = useHandleItemHover();
const {
conversationRenameLoading,
initialConversationName,
onConversationRenameOk,
conversationRenameVisible,
hideConversationRenameModal,
showConversationRenameModal,
} = useRenameConversation();
const {
dialogSettingLoading,
initialDialog,
onDialogEditOk,
dialogEditVisible,
clearDialog,
hideDialogEditModal,
showDialogEditModal,
} = useEditDialog();
const { t } = useTranslate('chat');
const {
visible: overviewVisible,
hideModal: hideOverviewModal,
showModal: showOverviewModal,
} = useSetModalState();
const { currentRecord, setRecord } = useSetSelectedRecord<IDialog>();
const [controller, setController] = useState(new AbortController());
const handleAppCardEnter = (id: string) => () => {
handleItemEnter(id);
};
const handleConversationCardEnter = (id: string) => () => {
handleConversationItemEnter(id);
};
const handleShowChatConfigurationModal =
(dialogId?: string): any =>
(info: any) => {
info?.domEvent?.preventDefault();
info?.domEvent?.stopPropagation();
showDialogEditModal(dialogId);
};
const handleRemoveDialog =
(dialogId: string): MenuItemProps['onClick'] =>
({ domEvent }) => {
domEvent.preventDefault();
domEvent.stopPropagation();
onRemoveDialog([dialogId]);
};
const handleShowOverviewModal =
(dialog: IDialog): any =>
(info: any) => {
info?.domEvent?.preventDefault();
info?.domEvent?.stopPropagation();
setRecord(dialog);
showOverviewModal();
};
const handleRemoveConversation =
(conversationId: string): MenuItemProps['onClick'] =>
({ domEvent }) => {
domEvent.preventDefault();
domEvent.stopPropagation();
onRemoveConversation([conversationId]);
};
const handleShowConversationRenameModal =
(conversationId: string): MenuItemProps['onClick'] =>
({ domEvent }) => {
domEvent.preventDefault();
domEvent.stopPropagation();
showConversationRenameModal(conversationId);
};
const handleDialogCardClick = useCallback(
(dialogId: string) => () => {
handleClickDialog(dialogId);
},
[handleClickDialog],
);
const handleConversationCardClick = useCallback(
(conversationId: string, isNew: boolean) => () => {
handleClickConversation(conversationId, isNew ? 'true' : '');
setController((pre) => {
pre.abort();
return new AbortController();
});
},
[handleClickConversation],
);
const handleCreateTemporaryConversation = useCallback(() => {
addTemporaryConversation();
}, [addTemporaryConversation]);
const buildAppItems = (dialog: IDialog) => {
const dialogId = dialog.id;
const appItems: MenuProps['items'] = [
{
key: '1',
onClick: handleShowChatConfigurationModal(dialogId),
label: (
<Space>
<EditOutlined />
{t('edit', { keyPrefix: 'common' })}
</Space>
),
},
{ type: 'divider' },
{
key: '2',
onClick: handleRemoveDialog(dialogId),
label: (
<Space>
<DeleteOutlined />
{t('delete', { keyPrefix: 'common' })}
</Space>
),
},
{ type: 'divider' },
{
key: '3',
onClick: handleShowOverviewModal(dialog),
label: (
<Space>
<KeyOutlined />
{t('overview')}
</Space>
),
},
];
return appItems;
};
const buildConversationItems = (conversationId: string) => {
const appItems: MenuProps['items'] = [
{
key: '1',
onClick: handleShowConversationRenameModal(conversationId),
label: (
<Space>
<EditOutlined />
{t('rename', { keyPrefix: 'common' })}
</Space>
),
},
{ type: 'divider' },
{
key: '2',
onClick: handleRemoveConversation(conversationId),
label: (
<Space>
<DeleteOutlined />
{t('delete', { keyPrefix: 'common' })}
</Space>
),
},
];
return appItems;
};
return (
<Flex className={styles.chatWrapper}>
<Flex className={styles.chatAppWrapper}>
<Flex flex={1} vertical>
<Button type="primary" onClick={handleShowChatConfigurationModal()}>
{t('createAssistant')}
</Button>
<Divider></Divider>
<Flex className={styles.chatAppContent} vertical gap={10}>
<Spin spinning={dialogLoading} wrapperClassName={styles.chatSpin}>
{dialogList.map((x) => (
<Card
key={x.id}
hoverable
className={classNames(styles.chatAppCard, {
[styles.chatAppCardSelected]: dialogId === x.id,
})}
onMouseEnter={handleAppCardEnter(x.id)}
onMouseLeave={handleItemLeave}
onClick={handleDialogCardClick(x.id)}
>
<Flex justify="space-between" align="center">
<Space size={15}>
<Avatar src={x.icon} shape={'square'} />
<section>
<b>
<Text
ellipsis={{ tooltip: x.name }}
style={{ width: 130 }}
>
{x.name}
</Text>
</b>
<div>{x.description}</div>
</section>
</Space>
{activated === x.id && (
<section>
<Dropdown menu={{ items: buildAppItems(x) }}>
<ChatAppCube
className={styles.cubeIcon}
></ChatAppCube>
</Dropdown>
</section>
)}
</Flex>
</Card>
))}
</Spin>
</Flex>
</Flex>
</Flex>
<Divider type={'vertical'} className={styles.divider}></Divider>
<Flex className={styles.chatTitleWrapper}>
<Flex flex={1} vertical>
<Flex
justify={'space-between'}
align="center"
className={styles.chatTitle}
>
<Space>
<b>{t('chat')}</b>
<Tag>{conversationList.length}</Tag>
</Space>
<Tooltip title={t('newChat')}>
<div>
<SvgIcon
name="plus-circle-fill"
width={20}
onClick={handleCreateTemporaryConversation}
></SvgIcon>
</div>
</Tooltip>
</Flex>
<Divider></Divider>
<Flex vertical gap={10} className={styles.chatTitleContent}>
<Spin
spinning={conversationLoading}
wrapperClassName={styles.chatSpin}
>
{conversationList.map((x) => (
<Card
key={x.id}
hoverable
onClick={handleConversationCardClick(x.id, x.is_new)}
onMouseEnter={handleConversationCardEnter(x.id)}
onMouseLeave={handleConversationItemLeave}
className={classNames(styles.chatTitleCard, {
[styles.chatTitleCardSelected]: x.id === conversationId,
})}
>
<Flex justify="space-between" align="center">
<div>
<Text
ellipsis={{ tooltip: x.name }}
style={{ width: 150 }}
>
{x.name}
</Text>
</div>
{conversationActivated === x.id && x.id !== '' && (
<section>
<Dropdown
menu={{ items: buildConversationItems(x.id) }}
>
<ChatAppCube
className={styles.cubeIcon}
></ChatAppCube>
</Dropdown>
</section>
)}
</Flex>
</Card>
))}
</Spin>
</Flex>
</Flex>
</Flex>
<Divider type={'vertical'} className={styles.divider}></Divider>
<ChatContainer controller={controller}></ChatContainer>
{dialogEditVisible && (
<ChatConfigurationModal
visible={dialogEditVisible}
initialDialog={initialDialog}
showModal={showDialogEditModal}
hideModal={hideDialogEditModal}
loading={dialogSettingLoading}
onOk={onDialogEditOk}
clearDialog={clearDialog}
></ChatConfigurationModal>
)}
<RenameModal
visible={conversationRenameVisible}
hideModal={hideConversationRenameModal}
onOk={onConversationRenameOk}
initialName={initialConversationName}
loading={conversationRenameLoading}
></RenameModal>
{overviewVisible && (
<ChatIdModal
visible={overviewVisible}
hideModal={hideOverviewModal}
id={currentRecord.id}
name={currentRecord.name}
idKey="dialogId"
></ChatIdModal>
)}
</Flex>
);
};
export default Chat;