Skip to content

Commit

Permalink
feat: Expose the agent's chat window to third parties #1842 (#1897)
Browse files Browse the repository at this point in the history
### What problem does this PR solve?

feat: Expose the agent's chat window to third parties #1842
### Type of change


- [x] New Feature (non-breaking change which adds functionality)
  • Loading branch information
cike8899 authored Aug 9, 2024
1 parent 37be0ff commit 827042f
Show file tree
Hide file tree
Showing 14 changed files with 290 additions and 358 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import { Button, Modal, Space, Table } from 'antd';
import { useOperateApiKey } from '../hooks';

const ChatApiKeyModal = ({
visible,
dialogId,
hideModal,
}: IModalProps<any> & { dialogId: string }) => {
idKey,
}: IModalProps<any> & { dialogId: string; idKey: string }) => {
const { createToken, removeToken, tokenList, listLoading, creatingLoading } =
useOperateApiKey(visible, dialogId);
useOperateApiKey(dialogId, idKey);
const { t } = useTranslate('chat');

const columns: TableProps<IToken>['columns'] = [
Expand Down Expand Up @@ -48,7 +48,7 @@ const ChatApiKeyModal = ({
<>
<Modal
title={t('apiKey')}
open={visible}
open
onCancel={hideModal}
cancelButtonProps={{ style: { display: 'none' } }}
style={{ top: 300 }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import LineChart from '@/components/line-chart';
import { useFetchNextStats } from '@/hooks/chat-hooks';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
import { IModalProps } from '@/interfaces/common';
import { IDialog, IStats } from '@/interfaces/database/chat';
import { IStats } from '@/interfaces/database/chat';
import { formatDate } from '@/utils/date';
import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd';
import { RangePickerProps } from 'antd/es/date-picker';
Expand All @@ -10,7 +11,6 @@ import camelCase from 'lodash/camelCase';
import ChatApiKeyModal from '../chat-api-key-modal';
import EmbedModal from '../embed-modal';
import {
useFetchStatsOnMount,
usePreviewChat,
useSelectChartStatsList,
useShowEmbedModal,
Expand Down Expand Up @@ -40,8 +40,10 @@ const StatsLineChart = ({ statsType }: { statsType: keyof IStats }) => {
const ChatOverviewModal = ({
visible,
hideModal,
dialog,
}: IModalProps<any> & { dialog: IDialog }) => {
id,
name = '',
idKey,
}: IModalProps<any> & { id: string; name?: string; idKey: string }) => {
const { t } = useTranslate('chat');
const {
visible: apiKeyVisible,
Expand All @@ -54,15 +56,15 @@ const ChatOverviewModal = ({
showEmbedModal,
embedToken,
errorContextHolder,
} = useShowEmbedModal(dialog.id);
} = useShowEmbedModal(id, idKey);

const { pickerValue, setPickerValue } = useFetchStatsOnMount(visible);
const { pickerValue, setPickerValue } = useFetchNextStats();

const disabledDate: RangePickerProps['disabledDate'] = (current) => {
return current && current > dayjs().endOf('day');
};

const { handlePreview, contextHolder } = usePreviewChat(dialog.id);
const { handlePreview, contextHolder } = usePreviewChat(id, idKey);

return (
<>
Expand Down Expand Up @@ -97,7 +99,7 @@ const ChatOverviewModal = ({
</a>
</Space>
</Card>
<Card title={`${dialog.name} Web App`}>
<Card title={`${name} Web App`}>
<Flex gap={8} vertical>
<Space size={'middle'}>
<Button onClick={handlePreview}>{t('preview')}</Button>
Expand All @@ -124,11 +126,13 @@ const ChatOverviewModal = ({
<StatsLineChart statsType={'uv'}></StatsLineChart>
</div>
</Flex>
<ChatApiKeyModal
visible={apiKeyVisible}
hideModal={hideApiKeyModal}
dialogId={dialog.id}
></ChatApiKeyModal>
{apiKeyVisible && (
<ChatApiKeyModal
hideModal={hideApiKeyModal}
dialogId={id}
idKey={idKey}
></ChatApiKeyModal>
)}
<EmbedModal
token={embedToken}
visible={embedVisible}
Expand Down
File renamed without changes.
File renamed without changes.
151 changes: 151 additions & 0 deletions web/src/components/api-service/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import {
useCreateNextToken,
useFetchNextStats,
useFetchTokenList,
useRemoveNextToken,
} from '@/hooks/chat-hooks';
import {
useSetModalState,
useShowDeleteConfirm,
useTranslate,
} from '@/hooks/common-hooks';
import { IStats } from '@/interfaces/database/chat';
import { message } from 'antd';
import { useCallback } from 'react';

export const useOperateApiKey = (dialogId: string, idKey: string) => {
const { removeToken } = useRemoveNextToken();
const { createToken, loading: creatingLoading } = useCreateNextToken();
const { data: tokenList, loading: listLoading } = useFetchTokenList({
[idKey]: dialogId,
});

const showDeleteConfirm = useShowDeleteConfirm();

const onRemoveToken = (token: string, tenantId: string) => {
showDeleteConfirm({
onOk: () => removeToken({ dialogId, tokens: [token], tenantId }),
});
};

const onCreateToken = useCallback(() => {
createToken({ [idKey]: dialogId });
}, [createToken, idKey, dialogId]);

return {
removeToken: onRemoveToken,
createToken: onCreateToken,
tokenList,
creatingLoading,
listLoading,
};
};

type ChartStatsType = {
[k in keyof IStats]: Array<{ xAxis: string; yAxis: number }>;
};

export const useSelectChartStatsList = (): ChartStatsType => {
const { data: stats } = useFetchNextStats();

return Object.keys(stats).reduce((pre, cur) => {
const item = stats[cur as keyof IStats];
if (item.length > 0) {
pre[cur as keyof IStats] = item.map((x) => ({
xAxis: x[0] as string,
yAxis: x[1] as number,
}));
}
return pre;
}, {} as ChartStatsType);
};

export const useShowTokenEmptyError = () => {
const [messageApi, contextHolder] = message.useMessage();
const { t } = useTranslate('chat');

const showTokenEmptyError = useCallback(() => {
messageApi.error(t('tokenError'));
}, [messageApi, t]);
return { showTokenEmptyError, contextHolder };
};

const getUrlWithToken = (token: string) => {
const { protocol, host } = window.location;
return `${protocol}//${host}/chat/share?shared_id=${token}`;
};

const useFetchTokenListBeforeOtherStep = (dialogId: string, idKey: string) => {
const { showTokenEmptyError, contextHolder } = useShowTokenEmptyError();

const { data: tokenList, refetch } = useFetchTokenList({ [idKey]: dialogId });

const token =
Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';

const handleOperate = useCallback(async () => {
const ret = await refetch();
const list = ret.data;
if (Array.isArray(list) && list.length > 0) {
return list[0]?.token;
} else {
showTokenEmptyError();
return false;
}
}, [showTokenEmptyError, refetch]);

return {
token,
contextHolder,
handleOperate,
};
};

export const useShowEmbedModal = (dialogId: string, idKey: string) => {
const {
visible: embedVisible,
hideModal: hideEmbedModal,
showModal: showEmbedModal,
} = useSetModalState();

const { handleOperate, token, contextHolder } =
useFetchTokenListBeforeOtherStep(dialogId, idKey);

const handleShowEmbedModal = useCallback(async () => {
const succeed = await handleOperate();
if (succeed) {
showEmbedModal();
}
}, [handleOperate, showEmbedModal]);

return {
showEmbedModal: handleShowEmbedModal,
hideEmbedModal,
embedVisible,
embedToken: token,
errorContextHolder: contextHolder,
};
};

export const usePreviewChat = (dialogId: string, idKey: string) => {
const { handleOperate, contextHolder } = useFetchTokenListBeforeOtherStep(
dialogId,
idKey,
);

const open = useCallback((t: string) => {
window.open(getUrlWithToken(t), '_blank');
}, []);

const handlePreview = useCallback(async () => {
const token = await handleOperate();
if (token) {
open(token);
}
}, [handleOperate, open]);

return {
handlePreview,
contextHolder,
};
};
Loading

0 comments on commit 827042f

Please sign in to comment.