### What problem does this PR solve? change language Issue link: #245 - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@@ -3,6 +3,7 @@ import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import { Checkbox, Form, Input, Modal, Space } from 'antd';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'umi';
|
||||
import EditTag from '../edit-tag';
|
||||
|
||||
@@ -24,6 +25,7 @@ const ChunkCreatingModal: React.FC<kFProps> = ({ doc_id, chunkId }) => {
|
||||
const [keywords, setKeywords] = useState<string[]>([]);
|
||||
const loading = useOneNamespaceEffectsLoading('chunkModel', ['create_chunk']);
|
||||
const { removeChunk } = useDeleteChunkByIds();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleCancel = () => {
|
||||
dispatch({
|
||||
@@ -87,7 +89,7 @@ const ChunkCreatingModal: React.FC<kFProps> = ({ doc_id, chunkId }) => {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={`${chunkId ? 'Edit' : 'Create'} Chunk`}
|
||||
title={`${chunkId ? t('common.edit') : t('common.create')} ${t('chunk.chunk')}`}
|
||||
open={isShowCreateModal}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
@@ -100,27 +102,27 @@ const ChunkCreatingModal: React.FC<kFProps> = ({ doc_id, chunkId }) => {
|
||||
layout={'vertical'}
|
||||
>
|
||||
<Form.Item<FieldType>
|
||||
label="Chunk"
|
||||
label={t('chunk.chunk')}
|
||||
name="content"
|
||||
rules={[{ required: true, message: 'Please input value!' }]}
|
||||
rules={[{ required: true, message: t('chunk.chunkMessage') }]}
|
||||
>
|
||||
<Input.TextArea autoSize={{ minRows: 4, maxRows: 10 }} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<section>
|
||||
<p>Keyword*</p>
|
||||
<p>{t('chunk.keyword')} *</p>
|
||||
<EditTag tags={keywords} setTags={setKeywords} />
|
||||
</section>
|
||||
{chunkId && (
|
||||
<section>
|
||||
<p>Function*</p>
|
||||
<p>{t('chunk.function')} *</p>
|
||||
<Space size={'large'}>
|
||||
<Checkbox onChange={handleCheck} checked={checked}>
|
||||
Enabled
|
||||
{t('chunk.enabled')}
|
||||
</Checkbox>
|
||||
|
||||
<span onClick={handleRemove}>
|
||||
<DeleteOutlined /> Delete
|
||||
<DeleteOutlined /> {t('common.delete')}
|
||||
</span>
|
||||
</Space>
|
||||
</section>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ReactComponent as FilterIcon } from '@/assets/filter.svg';
|
||||
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
|
||||
import {
|
||||
ArrowLeftOutlined,
|
||||
@@ -49,6 +50,7 @@ const ChunkToolBar = ({
|
||||
const dispatch = useDispatch();
|
||||
const knowledgeBaseId = useKnowledgeBaseId();
|
||||
const [isShowSearchBox, setIsShowSearchBox] = useState(false);
|
||||
const { t } = useTranslate('chunk');
|
||||
|
||||
const handleSelectAllCheck = useCallback(
|
||||
(e: any) => {
|
||||
@@ -95,7 +97,7 @@ const ChunkToolBar = ({
|
||||
label: (
|
||||
<>
|
||||
<Checkbox onChange={handleSelectAllCheck} checked={checked}>
|
||||
<b>Select All</b>
|
||||
<b>{t('selectAll')}</b>
|
||||
</Checkbox>
|
||||
</>
|
||||
),
|
||||
@@ -106,7 +108,7 @@ const ChunkToolBar = ({
|
||||
label: (
|
||||
<Space onClick={handleEnabledClick}>
|
||||
<CheckCircleOutlined />
|
||||
<b>Enabled Selected</b>
|
||||
<b>{t('enabledSelected')}</b>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
@@ -115,7 +117,7 @@ const ChunkToolBar = ({
|
||||
label: (
|
||||
<Space onClick={handleDisabledClick}>
|
||||
<CloseCircleOutlined />
|
||||
<b>Disabled Selected</b>
|
||||
<b>{t('disabledSelected')}</b>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
@@ -125,7 +127,7 @@ const ChunkToolBar = ({
|
||||
label: (
|
||||
<Space onClick={handleDelete}>
|
||||
<DeleteOutlined />
|
||||
<b>Delete Selected</b>
|
||||
<b>{t('deleteSelected')}</b>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
@@ -136,6 +138,7 @@ const ChunkToolBar = ({
|
||||
handleDelete,
|
||||
handleEnabledClick,
|
||||
handleDisabledClick,
|
||||
t,
|
||||
]);
|
||||
|
||||
const content = (
|
||||
@@ -151,9 +154,9 @@ const ChunkToolBar = ({
|
||||
const filterContent = (
|
||||
<Radio.Group onChange={handleFilterChange} value={available}>
|
||||
<Space direction="vertical">
|
||||
<Radio value={undefined}>All</Radio>
|
||||
<Radio value={1}>Enabled</Radio>
|
||||
<Radio value={0}>Disabled</Radio>
|
||||
<Radio value={undefined}>{t('all')}</Radio>
|
||||
<Radio value={1}>{t('enabled')}</Radio>
|
||||
<Radio value={0}>{t('disabled')}</Radio>
|
||||
</Space>
|
||||
</Radio.Group>
|
||||
);
|
||||
@@ -172,14 +175,14 @@ const ChunkToolBar = ({
|
||||
<Space>
|
||||
<Popover content={content} placement="bottom" arrow={false}>
|
||||
<Button>
|
||||
Bulk
|
||||
{t('bulk')}
|
||||
<DownOutlined />
|
||||
</Button>
|
||||
</Popover>
|
||||
{isShowSearchBox ? (
|
||||
<Input
|
||||
size="middle"
|
||||
placeholder="Search"
|
||||
placeholder={t('search')}
|
||||
prefix={<SearchOutlined />}
|
||||
allowClear
|
||||
onChange={handleSearchChange}
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from './hooks';
|
||||
import { ChunkModelState } from './model';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './index.less';
|
||||
|
||||
const Chunk = () => {
|
||||
@@ -33,6 +34,7 @@ const Chunk = () => {
|
||||
const documentInfo = useSelectDocumentInfo();
|
||||
const { handleChunkCardClick, selectedChunkId } = useHandleChunkCardClick();
|
||||
const isPdf = documentInfo.type === 'pdf';
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getChunkList = useFetchChunkList();
|
||||
|
||||
@@ -86,7 +88,7 @@ const Chunk = () => {
|
||||
[],
|
||||
);
|
||||
const showSelectedChunkWarning = () => {
|
||||
message.warning('Please select chunk!');
|
||||
message.warning(t('message.pleaseSelectChunk'));
|
||||
};
|
||||
|
||||
const handleRemoveChunk = useCallback(async () => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import kbService from '@/services/kbService';
|
||||
import { message } from 'antd';
|
||||
import { pick } from 'lodash';
|
||||
// import { delay } from '@/utils/storeUtil';
|
||||
import i18n from '@/locales/config';
|
||||
import { DvaModel } from 'umi';
|
||||
|
||||
export interface ChunkModelState extends BaseState {
|
||||
@@ -102,7 +103,7 @@ const model: DvaModel<ChunkModelState> = {
|
||||
const { data } = yield call(kbService.switch_chunk, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('Modified successfully !');
|
||||
message.success(i18n.t('message.modified'));
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
|
||||
@@ -40,6 +40,7 @@ import classNames from 'classnames';
|
||||
import { ReactElement, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { Link, useNavigate } from 'umi';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import styles from './index.less';
|
||||
|
||||
const { Dragger } = Upload;
|
||||
@@ -135,6 +136,7 @@ const KnowledgeUploadFile = () => {
|
||||
const documentList = useSelectDocumentList();
|
||||
const runDocumentByIds = useRunDocument();
|
||||
const uploadDocument = useUploadDocument();
|
||||
const { t } = useTranslate('knowledgeDetails');
|
||||
|
||||
const enabled = useMemo(() => {
|
||||
if (isUpload) {
|
||||
@@ -257,10 +259,10 @@ const KnowledgeUploadFile = () => {
|
||||
</Flex>
|
||||
<Flex justify="space-around">
|
||||
<p className={styles.selectFilesText}>
|
||||
<b>Select files</b>
|
||||
<b>{t('selectFiles')}</b>
|
||||
</p>
|
||||
<p className={styles.changeSpecificCategoryText}>
|
||||
<b>Change specific category</b>
|
||||
<b>{t('changeSpecificCategory')}</b>
|
||||
</p>
|
||||
</Flex>
|
||||
</div>
|
||||
@@ -275,13 +277,8 @@ const KnowledgeUploadFile = () => {
|
||||
<Button className={styles.uploaderButton}>
|
||||
<CloudUploadOutlined className={styles.uploaderIcon} />
|
||||
</Button>
|
||||
<p className="ant-upload-text">
|
||||
Click or drag file to this area to upload
|
||||
</p>
|
||||
<p className="ant-upload-hint">
|
||||
Support for a single or bulk upload. Strictly prohibited from
|
||||
uploading company data or other banned files.
|
||||
</p>
|
||||
<p className="ant-upload-text">{t('uploadTitle')}</p>
|
||||
<p className="ant-upload-hint">{t('uploadDescription')}</p>
|
||||
</Dragger>
|
||||
</section>
|
||||
<section className={styles.footer}>
|
||||
@@ -292,7 +289,7 @@ const KnowledgeUploadFile = () => {
|
||||
disabled={!enabled}
|
||||
size="large"
|
||||
>
|
||||
Next
|
||||
{t('next', { keyPrefix: 'common' })}
|
||||
</Button>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -204,7 +204,9 @@ const KnowledgeFile = () => {
|
||||
<div className={styles.filter}>
|
||||
<Space>
|
||||
<h3>{t('total', { keyPrefix: 'common' })}</h3>
|
||||
<Tag color="purple">{total} files</Tag>
|
||||
<Tag color="purple">
|
||||
{total} {t('files')}
|
||||
</Tag>
|
||||
</Space>
|
||||
<Space>
|
||||
<Input
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BaseState } from '@/interfaces/common';
|
||||
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||
import i18n from '@/locales/config';
|
||||
import kbService, { getDocumentFile } from '@/services/kbService';
|
||||
import { message } from 'antd';
|
||||
import omit from 'lodash/omit';
|
||||
@@ -54,14 +55,14 @@ const model: DvaModel<KFModelState> = {
|
||||
const { data } = yield call(kbService.createKb, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('Created!');
|
||||
message.success(i18n.t('message.created'));
|
||||
}
|
||||
},
|
||||
*updateKf({ payload = {} }, { call }) {
|
||||
const { data } = yield call(kbService.updateKb, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('Modified!');
|
||||
message.success(i18n.t('message.modified'));
|
||||
}
|
||||
},
|
||||
*getKfDetail({ payload = {} }, { call }) {
|
||||
@@ -109,7 +110,7 @@ const model: DvaModel<KFModelState> = {
|
||||
);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('Modified!');
|
||||
message.success(i18n.t('message.modified'));
|
||||
yield put({
|
||||
type: 'getKfList',
|
||||
payload: { kb_id: payload.kb_id },
|
||||
@@ -122,7 +123,7 @@ const model: DvaModel<KFModelState> = {
|
||||
});
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('Deleted!');
|
||||
message.success(i18n.t('message.deleted'));
|
||||
yield put({
|
||||
type: 'getKfList',
|
||||
payload: { kb_id: payload.kb_id },
|
||||
@@ -137,7 +138,7 @@ const model: DvaModel<KFModelState> = {
|
||||
);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('rename success!');
|
||||
message.success(i18n.t('message.renamed'));
|
||||
|
||||
yield put({
|
||||
type: 'getKfList',
|
||||
@@ -156,7 +157,7 @@ const model: DvaModel<KFModelState> = {
|
||||
payload: { kb_id: payload.kb_id },
|
||||
});
|
||||
|
||||
message.success('Created!');
|
||||
message.success(i18n.t('message.created'));
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
@@ -173,7 +174,7 @@ const model: DvaModel<KFModelState> = {
|
||||
payload: { kb_id: payload.knowledgeBaseId },
|
||||
});
|
||||
}
|
||||
message.success('Operation successfully !');
|
||||
message.success(i18n.t('message.operated'));
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
@@ -189,7 +190,7 @@ const model: DvaModel<KFModelState> = {
|
||||
payload: { kb_id: payload.kb_id },
|
||||
});
|
||||
|
||||
message.success('Modified!');
|
||||
message.success(i18n.t('message.modified'));
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||
import { Badge, DescriptionsProps, Flex, Popover, Space, Tag } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import reactStringReplace from 'react-string-replace';
|
||||
import { useDispatch } from 'umi';
|
||||
import { RunningStatus, RunningStatusMap } from '../constant';
|
||||
@@ -80,11 +81,14 @@ export const ParsingStatusCell = ({ record }: IProps) => {
|
||||
const dispatch = useDispatch();
|
||||
const text = record.run;
|
||||
const runningStatus = RunningStatusMap[text];
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isRunning = isParserRunning(text);
|
||||
|
||||
const OperationIcon = iconMap[text];
|
||||
|
||||
const label = t(`knowledgeDetails.runningStatus${text}`);
|
||||
|
||||
const handleOperationIconClick = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/document_run',
|
||||
@@ -103,11 +107,11 @@ export const ParsingStatusCell = ({ record }: IProps) => {
|
||||
{isRunning ? (
|
||||
<Space>
|
||||
<Badge color={runningStatus.color} />
|
||||
{runningStatus.label}
|
||||
{label}
|
||||
<span>{(record.progress * 100).toFixed(2)}%</span>
|
||||
</Space>
|
||||
) : (
|
||||
runningStatus.label
|
||||
label
|
||||
)}
|
||||
</Tag>
|
||||
</Popover>
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
import SvgIcon from '@/components/svg-icon';
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { useSelectParserList } from '@/hooks/userSettingHook';
|
||||
import { Col, Divider, Empty, Row, Typography } from 'antd';
|
||||
import { useMemo } from 'react';
|
||||
import styles from './index.less';
|
||||
import { ImageMap, TextMap } from './utils';
|
||||
import { ImageMap } from './utils';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => {
|
||||
const parserList = useSelectParserList();
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
|
||||
const item = useMemo(() => {
|
||||
const item = parserList.find((x) => x.value === chunkMethod);
|
||||
if (item) {
|
||||
return {
|
||||
title: item.label,
|
||||
description: TextMap[item.value as keyof typeof TextMap]?.description,
|
||||
description: t(item.value),
|
||||
};
|
||||
}
|
||||
return { title: '', description: '' };
|
||||
}, [parserList, chunkMethod]);
|
||||
}, [parserList, chunkMethod, t]);
|
||||
|
||||
const imageList = useMemo(() => {
|
||||
if (chunkMethod in ImageMap) {
|
||||
@@ -33,18 +35,17 @@ const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => {
|
||||
{imageList.length > 0 ? (
|
||||
<>
|
||||
<Title level={5} className={styles.topTitle}>
|
||||
"{item.title}" Chunking Method Description
|
||||
"{item.title}" {t('methodTitle')}
|
||||
</Title>
|
||||
<p
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: item.description,
|
||||
}}
|
||||
></p>
|
||||
<Title level={5}>"{item.title}" Examples</Title>
|
||||
<Text>
|
||||
This visual guides is in order to make understanding easier
|
||||
for you.
|
||||
</Text>
|
||||
<Title level={5}>
|
||||
"{item.title}" {t('methodExamples')}
|
||||
</Title>
|
||||
<Text>{t('methodExamplesDescription')}</Text>
|
||||
<Row gutter={[10, 10]} className={styles.imageRow}>
|
||||
{imageList.map((x) => (
|
||||
<Col span={12} key={x}>
|
||||
@@ -56,15 +57,14 @@ const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => {
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
<Title level={5}>{item.title} Dialogue Examples</Title>
|
||||
<Title level={5}>
|
||||
{item.title} {t('dialogueExamplesTitle')}
|
||||
</Title>
|
||||
<Divider></Divider>
|
||||
</>
|
||||
) : (
|
||||
<Empty description={''} image={null}>
|
||||
<p>
|
||||
This will display a visual explanation of the knowledge base
|
||||
categories
|
||||
</p>
|
||||
<p>{t('methodEmpty')}</p>
|
||||
<SvgIcon name={'chunk-method/chunk-empty'} width={'100%'}></SvgIcon>
|
||||
</Empty>
|
||||
)}
|
||||
|
||||
@@ -59,7 +59,7 @@ const ConfigurationForm = ({ form }: { form: FormInstance }) => {
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="permission"
|
||||
label="Permissions"
|
||||
label={t('permissions')}
|
||||
tooltip={t('permissionsTip')}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
@@ -70,7 +70,7 @@ const ConfigurationForm = ({ form }: { form: FormInstance }) => {
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="embd_id"
|
||||
label="Embedding model"
|
||||
label={t('embeddingModel')}
|
||||
rules={[{ required: true }]}
|
||||
tooltip={t('embeddingModelTip')}
|
||||
>
|
||||
@@ -82,7 +82,7 @@ const ConfigurationForm = ({ form }: { form: FormInstance }) => {
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="parser_id"
|
||||
label="Chunk method"
|
||||
label={t('chunkMethod')}
|
||||
tooltip={t('chunkMethodTip')}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||
import i18n from '@/locales/config';
|
||||
import kbService from '@/services/kbService';
|
||||
import { message } from 'antd';
|
||||
import { DvaModel } from 'umi';
|
||||
@@ -32,7 +33,7 @@ const model: DvaModel<KSModelState> = {
|
||||
const { data } = yield call(kbService.createKb, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('Created!');
|
||||
message.success(i18n.t('message.created'));
|
||||
}
|
||||
return data;
|
||||
},
|
||||
@@ -41,7 +42,7 @@ const model: DvaModel<KSModelState> = {
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
yield put({ type: 'getKbDetail', payload: { kb_id: payload.kb_id } });
|
||||
message.success('Updated!');
|
||||
message.success(i18n.t('message.updated'));
|
||||
}
|
||||
},
|
||||
*getKbDetail({ payload = {} }, { call, put }) {
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
KnowledgeDatasetRouteKey,
|
||||
KnowledgeRouteKey,
|
||||
datasetRouteMap,
|
||||
routeMap,
|
||||
} from './constant';
|
||||
import styles from './index.less';
|
||||
|
||||
@@ -49,7 +48,7 @@ const KnowledgeAdding = () => {
|
||||
{t(`knowledgeDetails.${activeKey}`)}
|
||||
</Link>
|
||||
) : (
|
||||
routeMap[activeKey]
|
||||
t(`knowledgeDetails.${activeKey}`)
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Form, Input, Select, Upload } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { ISegmentedContentProps } from '../interface';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import styles from './index.less';
|
||||
|
||||
const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
||||
@@ -12,6 +13,7 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
||||
label: x.name,
|
||||
value: x.id,
|
||||
}));
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
const normFile = (e: any) => {
|
||||
if (Array.isArray(e)) {
|
||||
@@ -28,14 +30,14 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
||||
>
|
||||
<Form.Item
|
||||
name={'name'}
|
||||
label="Assistant name"
|
||||
label={t('assistantName')}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input placeholder="e.g. Resume Jarvis" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="icon"
|
||||
label="Assistant avatar"
|
||||
label={t('assistantAvatar')}
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={normFile}
|
||||
>
|
||||
@@ -46,44 +48,45 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
||||
>
|
||||
<button style={{ border: 0, background: 'none' }} type="button">
|
||||
<PlusOutlined />
|
||||
<div style={{ marginTop: 8 }}>Upload</div>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
{t('upload', { keyPrefix: 'common' })}
|
||||
</div>
|
||||
</button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={'language'}
|
||||
label="Language"
|
||||
label={t('language')}
|
||||
initialValue={'Chinese'}
|
||||
tooltip="coming soon"
|
||||
style={{display:'none'}}
|
||||
style={{ display: 'none' }}
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ value: 'Chinese', label: 'Chinese' },
|
||||
{ value: 'English', label: 'English' },
|
||||
{ value: 'Chinese', label: t('chinese', { keyPrefix: 'common' }) },
|
||||
{ value: 'English', label: t('english', { keyPrefix: 'common' }) },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={['prompt_config', 'empty_response']}
|
||||
label="Empty response"
|
||||
tooltip="If nothing is retrieved with user's question in the knowledgebase, it will use this as an answer.
|
||||
If you want LLM comes up with its own opinion when nothing is retrieved, leave this blank."
|
||||
label={t('emptyResponse')}
|
||||
tooltip={t('emptyResponseTip')}
|
||||
>
|
||||
<Input placeholder="" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={['prompt_config', 'prologue']}
|
||||
label="Set an opener"
|
||||
tooltip="How do you want to welcome your clients?"
|
||||
initialValue={"Hi! I'm your assistant, what can I do for you?"}
|
||||
label={t('setAnOpener')}
|
||||
tooltip={t('setAnOpenerTip')}
|
||||
initialValue={t('setAnOpenerInitial')}
|
||||
>
|
||||
<Input.TextArea autoSize={{ minRows: 5 }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Knowledgebases"
|
||||
label={t('knowledgeBases')}
|
||||
name="kb_ids"
|
||||
tooltip="Select knowledgebases associated."
|
||||
tooltip={t('knowledgeBasesTip')}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -95,7 +98,7 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
||||
<Select
|
||||
mode="multiple"
|
||||
options={knowledgeOptions}
|
||||
placeholder="Please select"
|
||||
placeholder={t('knowledgeBasesMessage')}
|
||||
></Select>
|
||||
</Form.Item>
|
||||
</section>
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import { IDialog } from '@/interfaces/database/chat';
|
||||
import { Divider, Flex, Form, Modal, Segmented, UploadFile } from 'antd';
|
||||
import { SegmentedValue } from 'antd/es/segmented';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import omit from 'lodash/omit';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { variableEnabledFieldMap } from '../constants';
|
||||
@@ -17,20 +18,9 @@ import { useFetchModelId } from './hooks';
|
||||
import ModelSetting from './model-setting';
|
||||
import PromptEngine from './prompt-engine';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import styles from './index.less';
|
||||
|
||||
enum ConfigurationSegmented {
|
||||
AssistantSetting = 'Assistant Setting',
|
||||
PromptEngine = 'Prompt Engine',
|
||||
ModelSetting = 'Model Setting',
|
||||
}
|
||||
|
||||
const segmentedMap = {
|
||||
[ConfigurationSegmented.AssistantSetting]: AssistantSetting,
|
||||
[ConfigurationSegmented.ModelSetting]: ModelSetting,
|
||||
[ConfigurationSegmented.PromptEngine]: PromptEngine,
|
||||
};
|
||||
|
||||
const layout = {
|
||||
labelCol: { span: 7 },
|
||||
wrapperCol: { span: 17 },
|
||||
@@ -47,6 +37,18 @@ const validateMessages = {
|
||||
},
|
||||
};
|
||||
|
||||
enum ConfigurationSegmented {
|
||||
AssistantSetting = 'Assistant Setting',
|
||||
PromptEngine = 'Prompt Engine',
|
||||
ModelSetting = 'Model Setting',
|
||||
}
|
||||
|
||||
const segmentedMap = {
|
||||
[ConfigurationSegmented.AssistantSetting]: AssistantSetting,
|
||||
[ConfigurationSegmented.ModelSetting]: ModelSetting,
|
||||
[ConfigurationSegmented.PromptEngine]: PromptEngine,
|
||||
};
|
||||
|
||||
interface IProps extends IModalManagerChildrenProps {
|
||||
initialDialog: IDialog;
|
||||
loading: boolean;
|
||||
@@ -63,11 +65,13 @@ const ChatConfigurationModal = ({
|
||||
clearDialog,
|
||||
}: IProps) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [value, setValue] = useState<ConfigurationSegmented>(
|
||||
ConfigurationSegmented.AssistantSetting,
|
||||
);
|
||||
const promptEngineRef = useRef<Array<IPromptConfigParameters>>([]);
|
||||
const modelId = useFetchModelId(visible);
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
const handleOk = async () => {
|
||||
const values = await form.validateFields();
|
||||
@@ -115,10 +119,9 @@ const ChatConfigurationModal = ({
|
||||
<Flex gap={16}>
|
||||
<ChatConfigurationAtom></ChatConfigurationAtom>
|
||||
<div>
|
||||
<b>Chat Configuration</b>
|
||||
<b>{t('chatConfiguration')}</b>
|
||||
<div className={styles.chatConfigurationDescription}>
|
||||
Here, dress up a dedicated assistant for your special knowledge bases!
|
||||
💕
|
||||
{t('chatConfigurationDescription')}
|
||||
</div>
|
||||
</div>
|
||||
</Flex>
|
||||
@@ -158,7 +161,10 @@ const ChatConfigurationModal = ({
|
||||
size={'large'}
|
||||
value={value}
|
||||
onChange={handleSegmentedChange}
|
||||
options={Object.values(ConfigurationSegmented)}
|
||||
options={Object.values(ConfigurationSegmented).map((x) => ({
|
||||
label: t(camelCase(x)),
|
||||
value: x,
|
||||
}))}
|
||||
block
|
||||
/>
|
||||
<Divider></Divider>
|
||||
|
||||
@@ -5,16 +5,19 @@ import {
|
||||
} from '@/constants/knowledge';
|
||||
import { Divider, Flex, Form, InputNumber, Select, Slider, Switch } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import { useEffect } from 'react';
|
||||
import { ISegmentedContentProps } from '../interface';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { useFetchLlmList, useSelectLlmOptions } from '@/hooks/llmHooks';
|
||||
import { variableEnabledFieldMap } from '../constants';
|
||||
import styles from './index.less';
|
||||
|
||||
const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
const { t } = useTranslate('chat');
|
||||
const parameterOptions = Object.values(ModelVariableType).map((x) => ({
|
||||
label: x,
|
||||
label: t(camelCase(x)),
|
||||
value: x,
|
||||
}));
|
||||
|
||||
@@ -44,18 +47,18 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
})}
|
||||
>
|
||||
<Form.Item
|
||||
label="Model"
|
||||
label={t('model')}
|
||||
name="llm_id"
|
||||
tooltip="Large language chat model"
|
||||
rules={[{ required: true, message: 'Please select!' }]}
|
||||
tooltip={t('modelTip')}
|
||||
rules={[{ required: true, message: t('modelMessage') }]}
|
||||
>
|
||||
<Select options={modelOptions} showSearch />
|
||||
</Form.Item>
|
||||
<Divider></Divider>
|
||||
<Form.Item
|
||||
label="Freedom"
|
||||
label={t('freedom')}
|
||||
name="parameters"
|
||||
tooltip="'Precise' means the LLM will be conservative and answer your question cautiously. 'Improvise' means the you want LLM talk much and freely. 'Balance' is between cautiously and freely."
|
||||
tooltip={t('freedomTip')}
|
||||
initialValue={ModelVariableType.Precise}
|
||||
// rules={[{ required: true, message: 'Please input!' }]}
|
||||
>
|
||||
@@ -64,7 +67,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
onChange={handleParametersChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="Temperature" tooltip={'This parameter controls the randomness of predictions by the model. A lower temperature makes the model more confident in its responses, while a higher temperature makes it more creative and diverse.'}>
|
||||
<Form.Item label={t('temperature')} tooltip={t('temperatureTip')}>
|
||||
<Flex gap={20} align="center">
|
||||
<Form.Item
|
||||
name={'temperatureEnabled'}
|
||||
@@ -77,7 +80,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
<Form.Item
|
||||
name={['llm_setting', 'temperature']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Temperature is required' }]}
|
||||
rules={[{ required: true, message: t('temperatureMessage') }]}
|
||||
>
|
||||
<Slider className={styles.variableSlider} max={1} step={0.01} />
|
||||
</Form.Item>
|
||||
@@ -85,7 +88,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
<Form.Item
|
||||
name={['llm_setting', 'temperature']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Temperature is required' }]}
|
||||
rules={[{ required: true, message: t('temperatureMessage') }]}
|
||||
>
|
||||
<InputNumber
|
||||
className={styles.sliderInputNumber}
|
||||
@@ -96,7 +99,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
<Form.Item label="Top P" tooltip={'Also known as “nucleus sampling,” this parameter sets a threshold to select a smaller set of words to sample from. It focuses on the most likely words, cutting off the less probable ones.'}>
|
||||
<Form.Item label={t('topP')} tooltip={t('topPTip')}>
|
||||
<Flex gap={20} align="center">
|
||||
<Form.Item name={'topPEnabled'} valuePropName="checked" noStyle>
|
||||
<Switch size="small" />
|
||||
@@ -105,7 +108,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
<Form.Item
|
||||
name={['llm_setting', 'top_p']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Top_p is required' }]}
|
||||
rules={[{ required: true, message: t('topPMessage') }]}
|
||||
>
|
||||
<Slider className={styles.variableSlider} max={1} step={0.01} />
|
||||
</Form.Item>
|
||||
@@ -113,7 +116,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
<Form.Item
|
||||
name={['llm_setting', 'top_p']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Top_p is required' }]}
|
||||
rules={[{ required: true, message: t('topPMessage') }]}
|
||||
>
|
||||
<InputNumber
|
||||
className={styles.sliderInputNumber}
|
||||
@@ -124,7 +127,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
<Form.Item label="Presence Penalty" tooltip={'This discourages the model from repeating the same information by penalizing words that have already appeared in the conversation.'}>
|
||||
<Form.Item label={t('presencePenalty')} tooltip={t('presencePenaltyTip')}>
|
||||
<Flex gap={20} align="center">
|
||||
<Form.Item
|
||||
name={'presencePenaltyEnabled'}
|
||||
@@ -137,9 +140,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
<Form.Item
|
||||
name={['llm_setting', 'presence_penalty']}
|
||||
noStyle
|
||||
rules={[
|
||||
{ required: true, message: 'Presence Penalty is required' },
|
||||
]}
|
||||
rules={[{ required: true, message: t('presencePenaltyMessage') }]}
|
||||
>
|
||||
<Slider className={styles.variableSlider} max={1} step={0.01} />
|
||||
</Form.Item>
|
||||
@@ -147,9 +148,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
<Form.Item
|
||||
name={['llm_setting', 'presence_penalty']}
|
||||
noStyle
|
||||
rules={[
|
||||
{ required: true, message: 'Presence Penalty is required' },
|
||||
]}
|
||||
rules={[{ required: true, message: t('presencePenaltyMessage') }]}
|
||||
>
|
||||
<InputNumber
|
||||
className={styles.sliderInputNumber}
|
||||
@@ -160,7 +159,10 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
<Form.Item label="Frequency Penalty" tooltip={'Similar to the presence penalty, this reduces the model’s tendency to repeat the same words frequently.'}>
|
||||
<Form.Item
|
||||
label={t('frequencyPenalty')}
|
||||
tooltip={t('frequencyPenaltyTip')}
|
||||
>
|
||||
<Flex gap={20} align="center">
|
||||
<Form.Item
|
||||
name={'frequencyPenaltyEnabled'}
|
||||
@@ -174,7 +176,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
name={['llm_setting', 'frequency_penalty']}
|
||||
noStyle
|
||||
rules={[
|
||||
{ required: true, message: 'Frequency Penalty is required' },
|
||||
{ required: true, message: t('frequencyPenaltyMessage') },
|
||||
]}
|
||||
>
|
||||
<Slider className={styles.variableSlider} max={1} step={0.01} />
|
||||
@@ -183,9 +185,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
<Form.Item
|
||||
name={['llm_setting', 'frequency_penalty']}
|
||||
noStyle
|
||||
rules={[
|
||||
{ required: true, message: 'Frequency Penalty is required' },
|
||||
]}
|
||||
rules={[{ required: true, message: t('frequencyPenaltyMessage') }]}
|
||||
>
|
||||
<InputNumber
|
||||
className={styles.sliderInputNumber}
|
||||
@@ -196,7 +196,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
<Form.Item label="Max Tokens" tooltip={'This sets the maximum length of the model’s output, measured in the number of tokens (words or pieces of words).'}>
|
||||
<Form.Item label={t('maxTokens')} tooltip={t('maxTokensTip')}>
|
||||
<Flex gap={20} align="center">
|
||||
<Form.Item name={'maxTokensEnabled'} valuePropName="checked" noStyle>
|
||||
<Switch size="small" />
|
||||
@@ -205,7 +205,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
<Form.Item
|
||||
name={['llm_setting', 'max_tokens']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Max Tokens is required' }]}
|
||||
rules={[{ required: true, message: t('maxTokensMessage') }]}
|
||||
>
|
||||
<Slider className={styles.variableSlider} max={2048} />
|
||||
</Form.Item>
|
||||
@@ -213,7 +213,7 @@ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
|
||||
<Form.Item
|
||||
name={['llm_setting', 'max_tokens']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Max Tokens is required' }]}
|
||||
rules={[{ required: true, message: t('maxTokensMessage') }]}
|
||||
>
|
||||
<InputNumber
|
||||
className={styles.sliderInputNumber}
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
} from '../interface';
|
||||
import { EditableCell, EditableRow } from './editable-cell';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { useSelectPromptConfigParameters } from '../hooks';
|
||||
import styles from './index.less';
|
||||
|
||||
@@ -44,6 +45,7 @@ const PromptEngine = (
|
||||
) => {
|
||||
const [dataSource, setDataSource] = useState<DataType[]>([]);
|
||||
const parameters = useSelectPromptConfigParameters();
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
const components = {
|
||||
body: {
|
||||
@@ -102,7 +104,7 @@ const PromptEngine = (
|
||||
|
||||
const columns: TableProps<DataType>['columns'] = [
|
||||
{
|
||||
title: 'key',
|
||||
title: t('key'),
|
||||
dataIndex: 'variable',
|
||||
key: 'variable',
|
||||
onCell: (record: DataType) => ({
|
||||
@@ -114,7 +116,7 @@ const PromptEngine = (
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: 'optional',
|
||||
title: t('optional'),
|
||||
dataIndex: 'optional',
|
||||
key: 'optional',
|
||||
width: 40,
|
||||
@@ -130,7 +132,7 @@ const PromptEngine = (
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'operation',
|
||||
title: t('operation'),
|
||||
dataIndex: 'operation',
|
||||
width: 30,
|
||||
key: 'operation',
|
||||
@@ -152,24 +154,21 @@ const PromptEngine = (
|
||||
})}
|
||||
>
|
||||
<Form.Item
|
||||
label="System"
|
||||
rules={[{ required: true, message: 'Please input!' }]}
|
||||
tooltip="Instructions you need LLM to follow when LLM answers questions, like charactor design, answer length and answer language etc."
|
||||
label={t('system')}
|
||||
rules={[{ required: true, message: t('systemMessage') }]}
|
||||
tooltip={t('systemTip')}
|
||||
name={['prompt_config', 'system']}
|
||||
initialValue={`你是一个智能助手,请总结知识库的内容来回答问题,请列举知识库中的数据详细回答。当所有知识库内容都与问题无关时,你的回答必须包括“知识库中未找到您要的答案!”这句话。回答需要考虑聊天历史。
|
||||
以下是知识库:
|
||||
{knowledge}
|
||||
以上是知识库。`}
|
||||
initialValue={t('systemInitialValue')}
|
||||
>
|
||||
<Input.TextArea autoSize={{ maxRows: 8, minRows: 5 }} />
|
||||
</Form.Item>
|
||||
<Divider></Divider>
|
||||
<SimilaritySlider isTooltipShown></SimilaritySlider>
|
||||
<Form.Item<FieldType>
|
||||
label="Top N"
|
||||
label={t('topN')}
|
||||
name={'top_n'}
|
||||
initialValue={8}
|
||||
tooltip={`Not all the chunks whose similarity score is above the 'simialrity threashold' will be feed to LLMs. LLM can only see these 'Top N' chunks.`}
|
||||
tooltip={t('topNTip')}
|
||||
>
|
||||
<Slider max={30} />
|
||||
</Form.Item>
|
||||
@@ -177,18 +176,15 @@ const PromptEngine = (
|
||||
<Row align={'middle'} justify="end">
|
||||
<Col span={7} className={styles.variableAlign}>
|
||||
<label className={styles.variableLabel}>
|
||||
Variables
|
||||
<Tooltip title="If you use dialog APIs, the varialbes might help you chat with your clients with different strategies.
|
||||
The variables are used to fill-in the 'System' part in prompt in order to give LLM a hint.
|
||||
The 'knowledge' is a very special variable which will be filled-in with the retrieved chunks.
|
||||
All the variables in 'System' should be curly bracketed.">
|
||||
{t('variable')}
|
||||
<Tooltip title={t('variableTip')}>
|
||||
<QuestionCircleOutlined className={styles.variableIcon} />
|
||||
</Tooltip>
|
||||
</label>
|
||||
</Col>
|
||||
<Col span={17} className={styles.variableAlign}>
|
||||
<Button size="small" onClick={handleAdd}>
|
||||
Add
|
||||
{t('add')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
} from '../hooks';
|
||||
|
||||
import SvgIcon from '@/components/svg-icon';
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { getExtension, isPdf } from '@/utils/documentUtils';
|
||||
import styles from './index.less';
|
||||
|
||||
@@ -298,6 +299,7 @@ const ChatContainer = () => {
|
||||
const disabled = useGetSendButtonDisabled();
|
||||
useGetFileIcon();
|
||||
const loading = useSelectConversationLoading();
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -328,7 +330,7 @@ const ChatContainer = () => {
|
||||
</Flex>
|
||||
<Input
|
||||
size="large"
|
||||
placeholder="Message Resume Assistant..."
|
||||
placeholder={t('sendPlaceholder')}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
suffix={
|
||||
@@ -338,7 +340,7 @@ const ChatContainer = () => {
|
||||
loading={sendLoading}
|
||||
disabled={disabled}
|
||||
>
|
||||
Send
|
||||
{t('send')}
|
||||
</Button>
|
||||
}
|
||||
onPressEnter={handlePressEnter}
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
useSelectFirstDialogOnMount,
|
||||
} from './hooks';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import styles from './index.less';
|
||||
|
||||
const Chat = () => {
|
||||
@@ -71,6 +72,7 @@ const Chat = () => {
|
||||
} = useEditDialog();
|
||||
const dialogLoading = useSelectDialogListLoading();
|
||||
const conversationLoading = useSelectConversationListLoading();
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
useFetchDialogOnMount(dialogId, true);
|
||||
|
||||
@@ -132,7 +134,8 @@ const Chat = () => {
|
||||
onClick: handleCreateTemporaryConversation,
|
||||
label: (
|
||||
<Space>
|
||||
<EditOutlined /> New chat
|
||||
<EditOutlined />
|
||||
{t('newChat')}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
@@ -146,7 +149,7 @@ const Chat = () => {
|
||||
label: (
|
||||
<Space>
|
||||
<EditOutlined />
|
||||
Edit
|
||||
{t('edit', { keyPrefix: 'common' })}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
@@ -157,7 +160,7 @@ const Chat = () => {
|
||||
label: (
|
||||
<Space>
|
||||
<DeleteOutlined />
|
||||
Delete chat
|
||||
{t('delete', { keyPrefix: 'common' })}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
@@ -250,7 +253,7 @@ const Chat = () => {
|
||||
className={styles.chatTitle}
|
||||
>
|
||||
<Space>
|
||||
<b>Chat</b>
|
||||
<b>{t('chat')}</b>
|
||||
<Tag>{conversationList.length}</Tag>
|
||||
</Space>
|
||||
<Dropdown menu={{ items }}>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IConversation, IDialog, Message } from '@/interfaces/database/chat';
|
||||
import i18n from '@/locales/config';
|
||||
import chatService from '@/services/chatService';
|
||||
import { message } from 'antd';
|
||||
import { DvaModel } from 'umi';
|
||||
@@ -77,7 +78,9 @@ const model: DvaModel<ChatModelState> = {
|
||||
const { data } = yield call(chatService.setDialog, payload);
|
||||
if (data.retcode === 0) {
|
||||
yield put({ type: 'listDialog' });
|
||||
message.success(payload.dialog_id ? 'Modified!' : 'Created!');
|
||||
message.success(
|
||||
i18n.t(`message.${payload.dialog_id ? 'modified' : 'created'}`),
|
||||
);
|
||||
}
|
||||
return data.retcode;
|
||||
},
|
||||
@@ -85,7 +88,7 @@ const model: DvaModel<ChatModelState> = {
|
||||
const { data } = yield call(chatService.removeDialog, payload);
|
||||
if (data.retcode === 0) {
|
||||
yield put({ type: 'listDialog' });
|
||||
message.success('Deleted successfully !');
|
||||
message.success(i18n.t('message.deleted'));
|
||||
}
|
||||
return data.retcode;
|
||||
},
|
||||
@@ -152,7 +155,7 @@ const model: DvaModel<ChatModelState> = {
|
||||
type: 'listConversation',
|
||||
payload: { dialog_id: payload.dialog_id },
|
||||
});
|
||||
message.success('Deleted successfully !');
|
||||
message.success(i18n.t('message.deleted'));
|
||||
}
|
||||
return data.retcode;
|
||||
},
|
||||
|
||||
@@ -75,7 +75,7 @@ const Login = () => {
|
||||
<div className={styles.loginLeft}>
|
||||
<div className={styles.leftContainer}>
|
||||
<div className={styles.loginTitle}>
|
||||
<div>{title === 'login' ? t('login') : 'Create an account'}</div>
|
||||
<div>{title === 'login' ? t('login') : t('register')}</div>
|
||||
<span>
|
||||
{title === 'login'
|
||||
? t('loginDescription')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Authorization } from '@/constants/authorization';
|
||||
import i18n from '@/locales/config';
|
||||
import userService from '@/services/userService';
|
||||
import authorizationUtil from '@/utils/authorizationUtil';
|
||||
import { message } from 'antd';
|
||||
@@ -31,7 +32,7 @@ const model: DvaModel<LoginModelState> = {
|
||||
const { retcode, data: res } = data;
|
||||
const authorization = response.headers.get(Authorization);
|
||||
if (retcode === 0) {
|
||||
message.success('logged!');
|
||||
message.success(i18n.t('message.logged'));
|
||||
const token = res.access_token;
|
||||
const userInfo = {
|
||||
avatar: res.avatar,
|
||||
@@ -51,7 +52,7 @@ const model: DvaModel<LoginModelState> = {
|
||||
console.log();
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('Registered!');
|
||||
message.success(i18n.t('message.registered'));
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
@@ -59,7 +60,7 @@ const model: DvaModel<LoginModelState> = {
|
||||
const { data } = yield call(userService.logout, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('logout');
|
||||
message.success(i18n.t('message.logout'));
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
|
||||
@@ -3,11 +3,13 @@ import SvgIcon from '@/components/svg-icon';
|
||||
import { Flex, Rate, Space, Typography } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import styles from './index.less';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
const LoginRightPanel = () => {
|
||||
const { t } = useTranslate('login');
|
||||
return (
|
||||
<section className={styles.rightPanel}>
|
||||
<SvgIcon name="login-star" width={80}></SvgIcon>
|
||||
@@ -16,11 +18,10 @@ const LoginRightPanel = () => {
|
||||
level={1}
|
||||
className={classNames(styles.white, styles.loginTitle)}
|
||||
>
|
||||
Start building your smart assisstants.
|
||||
{t('title')}
|
||||
</Title>
|
||||
<Text className={classNames(styles.pink, styles.loginDescription)}>
|
||||
Sign up for free to explore top RAG technology. Create knowledge bases
|
||||
and AIs to empower your business.
|
||||
{t('description')}
|
||||
</Text>
|
||||
<Flex align="center" gap={16}>
|
||||
<Avatars></Avatars>
|
||||
@@ -34,7 +35,7 @@ const LoginRightPanel = () => {
|
||||
</span>
|
||||
</Space>
|
||||
<span className={classNames(styles.pink, styles.loginRateReviews)}>
|
||||
from 500+ reviews
|
||||
{t('review')}
|
||||
</span>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { SettingOutlined } from '@ant-design/icons';
|
||||
import { Button, Flex, Typography } from 'antd';
|
||||
|
||||
@@ -16,6 +17,8 @@ const SettingTitle = ({
|
||||
clickButton,
|
||||
showRightButton = false,
|
||||
}: IProps) => {
|
||||
const { t } = useTranslate('setting');
|
||||
|
||||
return (
|
||||
<Flex align="center" justify={'space-between'}>
|
||||
<div>
|
||||
@@ -24,7 +27,7 @@ const SettingTitle = ({
|
||||
</div>
|
||||
{showRightButton && (
|
||||
<Button type={'primary'} onClick={clickButton}>
|
||||
<SettingOutlined></SettingOutlined> System Model Settings
|
||||
<SettingOutlined></SettingOutlined> {t('systemModelSettings')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
IThirdOAIModelCollection as IThirdAiModelCollection,
|
||||
} from '@/interfaces/database/llm';
|
||||
import { IUserInfo } from '@/interfaces/database/userSetting';
|
||||
import i18n from '@/locales/config';
|
||||
import userService from '@/services/userService';
|
||||
import { message } from 'antd';
|
||||
import { DvaModel } from 'umi';
|
||||
@@ -47,7 +48,8 @@ const model: DvaModel<SettingModelState> = {
|
||||
const { data } = yield call(userService.setting, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('Modified!');
|
||||
message.success(i18n.t('message.modified'));
|
||||
|
||||
yield put({
|
||||
type: 'getUserInfo',
|
||||
});
|
||||
@@ -89,7 +91,8 @@ const model: DvaModel<SettingModelState> = {
|
||||
const { data } = yield call(userService.set_tenant_info, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('Modified!');
|
||||
message.success(i18n.t('message.modified'));
|
||||
|
||||
yield put({
|
||||
type: 'getTenantInfo',
|
||||
});
|
||||
@@ -137,7 +140,8 @@ const model: DvaModel<SettingModelState> = {
|
||||
const { data } = yield call(userService.set_api_key, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('Modified!');
|
||||
message.success(i18n.t('message.modified'));
|
||||
|
||||
yield put({ type: 'my_llm' });
|
||||
yield put({ type: 'factories_list' });
|
||||
yield put({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IModalManagerChildrenProps } from '@/components/modal-manager';
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { Form, Input, Modal } from 'antd';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
@@ -24,6 +25,7 @@ const ApiKeyModal = ({
|
||||
onOk,
|
||||
}: IProps) => {
|
||||
const [form] = Form.useForm();
|
||||
const { t } = useTranslate('setting');
|
||||
|
||||
const handleOk = async () => {
|
||||
const ret = await form.validateFields();
|
||||
@@ -49,7 +51,7 @@ const ApiKeyModal = ({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Modify"
|
||||
title={t('modify')}
|
||||
open={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
@@ -67,18 +69,18 @@ const ApiKeyModal = ({
|
||||
form={form}
|
||||
>
|
||||
<Form.Item<FieldType>
|
||||
label="Api-Key"
|
||||
label={t('apiKey')}
|
||||
name="api_key"
|
||||
tooltip="The API key can be obtained by registering the corresponding LLM supplier."
|
||||
rules={[{ required: true, message: 'Please input api key!' }]}
|
||||
tooltip={t('apiKeyTip')}
|
||||
rules={[{ required: true, message: t('apiKeyMessage') }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
{llmFactory === 'OpenAI' && (
|
||||
<Form.Item<FieldType>
|
||||
label="Base-Url"
|
||||
label={t('baseUrl')}
|
||||
name="base_url"
|
||||
tooltip="If your API key is from OpenAI, just ignore it. Any other intermediate providers will give this base url with the API key."
|
||||
tooltip={t('baseUrlTip')}
|
||||
>
|
||||
<Input placeholder="https://api.openai.com/v1" />
|
||||
</Form.Item>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ReactComponent as MoreModelIcon } from '@/assets/svg/more-model.svg';
|
||||
import { useSetModalState } from '@/hooks/commonHooks';
|
||||
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
|
||||
import {
|
||||
LlmItem,
|
||||
useFetchLlmFactoryListOnMount,
|
||||
@@ -64,6 +64,7 @@ interface IModelCardProps {
|
||||
|
||||
const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
|
||||
const { visible, switchVisible } = useSetModalState();
|
||||
const { t } = useTranslate('setting');
|
||||
|
||||
const handleApiKeyClick = () => {
|
||||
clickApiKey(item.name);
|
||||
@@ -94,7 +95,7 @@ const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
|
||||
</Button>
|
||||
<Button onClick={handleShowMoreClick}>
|
||||
<Flex gap={'small'}>
|
||||
Show more models
|
||||
{t('showMoreModels')}
|
||||
<MoreModelIcon />
|
||||
</Flex>
|
||||
</Button>
|
||||
@@ -140,6 +141,7 @@ const UserSettingModel = () => {
|
||||
hideSystemSettingModal,
|
||||
showSystemSettingModal,
|
||||
} = useSubmitSystemModelSetting();
|
||||
const { t } = useTranslate('setting');
|
||||
|
||||
const handleApiKeyClick = useCallback(
|
||||
(llmFactory: string) => {
|
||||
@@ -155,7 +157,7 @@ const UserSettingModel = () => {
|
||||
const items: CollapseProps['items'] = [
|
||||
{
|
||||
key: '1',
|
||||
label: 'Added models',
|
||||
label: t('addedModels'),
|
||||
children: (
|
||||
<List
|
||||
grid={{ gutter: 16, column: 1 }}
|
||||
@@ -168,7 +170,7 @@ const UserSettingModel = () => {
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: 'Models to be added',
|
||||
label: t('modelsToBeAdded'),
|
||||
children: (
|
||||
<List
|
||||
grid={{
|
||||
@@ -193,7 +195,7 @@ const UserSettingModel = () => {
|
||||
</Flex>
|
||||
<Divider></Divider>
|
||||
<Button type="link" onClick={handleAddModel(item.name)}>
|
||||
Add the model
|
||||
{t('addTheModel')}
|
||||
</Button>
|
||||
</Card>
|
||||
</List.Item>
|
||||
@@ -208,8 +210,8 @@ const UserSettingModel = () => {
|
||||
<Spin spinning={loading}>
|
||||
<section className={styles.modelWrapper}>
|
||||
<SettingTitle
|
||||
title="Model Setting"
|
||||
description="Manage your account settings and preferences here."
|
||||
title={t('model')}
|
||||
description={t('profileDescription')}
|
||||
showRightButton
|
||||
clickButton={showSystemSettingModal}
|
||||
></SettingTitle>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { IModalManagerChildrenProps } from '@/components/modal-manager';
|
||||
import { LlmModelType } from '@/constants/knowledge';
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { ISystemModelSettingSavingParams } from '@/hooks/llmHooks';
|
||||
import { Form, Modal, Select } from 'antd';
|
||||
import { useEffect } from 'react';
|
||||
@@ -21,6 +22,7 @@ const SystemModelSettingModal = ({
|
||||
const [form] = Form.useForm();
|
||||
const { systemSetting: initialValues, allOptions } =
|
||||
useFetchSystemModelSettingOnMount(visible);
|
||||
const { t } = useTranslate('setting');
|
||||
|
||||
const handleOk = async () => {
|
||||
const values = await form.validateFields();
|
||||
@@ -35,7 +37,7 @@ const SystemModelSettingModal = ({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="System Model Settings"
|
||||
title={t('systemModelSettings')}
|
||||
open={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={hideModal}
|
||||
@@ -43,25 +45,32 @@ const SystemModelSettingModal = ({
|
||||
confirmLoading={loading}
|
||||
>
|
||||
<Form form={form} onValuesChange={onFormLayoutChange} layout={'vertical'}>
|
||||
|
||||
<Form.Item label="Chat model" name="llm_id" tooltip="The default chat LLM all the newly created knowledgebase will use.">
|
||||
<Form.Item
|
||||
label={t('chatModel')}
|
||||
name="llm_id"
|
||||
tooltip={t('chatModelTip')}
|
||||
>
|
||||
<Select options={allOptions[LlmModelType.Chat]} />
|
||||
</Form.Item>
|
||||
<Form.Item label="Embedding model" name="embd_id" tooltip="The default embedding model all the newly created knowledgebase will use.">
|
||||
<Form.Item
|
||||
label={t('embeddingModel')}
|
||||
name="embd_id"
|
||||
tooltip={t('embeddingModelTip')}
|
||||
>
|
||||
<Select options={allOptions[LlmModelType.Embedding]} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Img2txt model"
|
||||
label={t('img2txtModel')}
|
||||
name="img2txt_id"
|
||||
tooltip="The default multi-module model all the newly created knowledgebase will use. It can describe a picture or video."
|
||||
tooltip={t('img2txtModelTip')}
|
||||
>
|
||||
<Select options={allOptions[LlmModelType.Image2text]} />
|
||||
</Form.Item>
|
||||
|
||||
|
||||
<Form.Item
|
||||
label="Sequence2txt model"
|
||||
label={t('sequence2txtModel')}
|
||||
name="asr_id"
|
||||
tooltip="The default ASR model all the newly created knowledgebase will use. Use this model to translate voices to corresponding text."
|
||||
tooltip={t('sequence2txtModelTip')}
|
||||
>
|
||||
<Select options={allOptions[LlmModelType.Speech2text]} />
|
||||
</Form.Item>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Button, Divider, Form, Input, Space } from 'antd';
|
||||
import SettingTitle from '../components/setting-title';
|
||||
import { useValidateSubmittable } from '../hooks';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import parentStyles from '../index.less';
|
||||
import styles from './index.less';
|
||||
|
||||
@@ -22,6 +23,7 @@ const UserSettingPassword = () => {
|
||||
const loading = useOneNamespaceEffectsLoading('settingModel', ['setting']);
|
||||
const { form, submittable } = useValidateSubmittable();
|
||||
const saveSetting = useSaveSetting();
|
||||
const { t } = useTranslate('setting');
|
||||
|
||||
const onFinish = (values: any) => {
|
||||
const password = rsaPsw(values.password) as string;
|
||||
@@ -37,8 +39,8 @@ const UserSettingPassword = () => {
|
||||
return (
|
||||
<section className={styles.passwordWrapper}>
|
||||
<SettingTitle
|
||||
title="Password"
|
||||
description="Please enter your current password to change your password."
|
||||
title={t('password')}
|
||||
description={t('passwordDescription')}
|
||||
></SettingTitle>
|
||||
<Divider />
|
||||
<Form
|
||||
@@ -56,12 +58,12 @@ const UserSettingPassword = () => {
|
||||
// requiredMark={'optional'}
|
||||
>
|
||||
<Form.Item<FieldType>
|
||||
label="Current password"
|
||||
label={t('currentPassword')}
|
||||
name="password"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input your password!',
|
||||
message: t('currentPasswordMessage'),
|
||||
whitespace: true,
|
||||
},
|
||||
]}
|
||||
@@ -69,14 +71,14 @@ const UserSettingPassword = () => {
|
||||
<Input.Password />
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item label="New password" required>
|
||||
<Form.Item label={t('newPassword')} required>
|
||||
<Form.Item<FieldType>
|
||||
noStyle
|
||||
name="new_password"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input your password!',
|
||||
message: t('newPasswordMessage'),
|
||||
whitespace: true,
|
||||
},
|
||||
]}
|
||||
@@ -84,18 +86,18 @@ const UserSettingPassword = () => {
|
||||
<Input.Password />
|
||||
</Form.Item>
|
||||
<p className={parentStyles.itemDescription}>
|
||||
Your new password must be more than 8 characters.
|
||||
{t('newPasswordDescription')}
|
||||
</p>
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item<FieldType>
|
||||
label="Confirm new password"
|
||||
label={t('confirmPassword')}
|
||||
name="confirm_password"
|
||||
dependencies={['new_password']}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please confirm your password!',
|
||||
message: t('confirmPasswordMessage'),
|
||||
whitespace: true,
|
||||
},
|
||||
({ getFieldValue }) => ({
|
||||
@@ -104,7 +106,7 @@ const UserSettingPassword = () => {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(
|
||||
new Error('The new password that you entered do not match!'),
|
||||
new Error(t('confirmPasswordNonMatchMessage')),
|
||||
);
|
||||
},
|
||||
}),
|
||||
@@ -120,14 +122,14 @@ const UserSettingPassword = () => {
|
||||
}
|
||||
>
|
||||
<Space>
|
||||
<Button htmlType="button">Cancel</Button>
|
||||
<Button htmlType="button">{t('cancel')}</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
disabled={!submittable}
|
||||
loading={loading}
|
||||
>
|
||||
Save
|
||||
{t('save', { keyPrefix: 'common' })}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
|
||||
@@ -29,6 +29,8 @@ import {
|
||||
useValidateSubmittable,
|
||||
} from '../hooks';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { useChangeLanguage } from '@/hooks/logicHooks';
|
||||
import parentStyles from '../index.less';
|
||||
import styles from './index.less';
|
||||
|
||||
@@ -54,6 +56,8 @@ const UserSettingProfile = () => {
|
||||
const { form, submittable } = useValidateSubmittable();
|
||||
const loading = useSelectUserInfoLoading();
|
||||
useFetchUserInfo();
|
||||
const { t } = useTranslate('setting');
|
||||
const changeLanguage = useChangeLanguage();
|
||||
|
||||
const onFinish = async (values: any) => {
|
||||
const avatar = await getBase64FromUploadFileList(values.avatar);
|
||||
@@ -72,8 +76,8 @@ const UserSettingProfile = () => {
|
||||
return (
|
||||
<section className={styles.profileWrapper}>
|
||||
<SettingTitle
|
||||
title="Profile"
|
||||
description="Update your photo and personal details here."
|
||||
title={t('profile')}
|
||||
description={t('profileDescription')}
|
||||
></SettingTitle>
|
||||
<Divider />
|
||||
<Spin spinning={loading}>
|
||||
@@ -91,12 +95,12 @@ const UserSettingProfile = () => {
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item<FieldType>
|
||||
label="Username"
|
||||
label={t('username')}
|
||||
name="nickname"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input your username!',
|
||||
message: t('usernameMessage'),
|
||||
whitespace: true,
|
||||
},
|
||||
]}
|
||||
@@ -107,8 +111,8 @@ const UserSettingProfile = () => {
|
||||
<Form.Item<FieldType>
|
||||
label={
|
||||
<div>
|
||||
<Space>Your photo</Space>
|
||||
<div>This will be displayed on your profile.</div>
|
||||
<Space>{t('photo')}</Space>
|
||||
<div>{t('photoDescription')}</div>
|
||||
</div>
|
||||
}
|
||||
name="avatar"
|
||||
@@ -126,41 +130,53 @@ const UserSettingProfile = () => {
|
||||
>
|
||||
<button style={{ border: 0, background: 'none' }} type="button">
|
||||
<PlusOutlined />
|
||||
<div style={{ marginTop: 8 }}>Upload</div>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
{t('upload', { keyPrefix: 'common' })}
|
||||
</div>
|
||||
</button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item<FieldType>
|
||||
label="Color schema"
|
||||
label={t('colorSchema')}
|
||||
name="color_schema"
|
||||
rules={[{ required: true, message: t('colorSchemaMessage') }]}
|
||||
>
|
||||
<Select placeholder={t('colorSchemaPlaceholder')}>
|
||||
<Option value="Bright">{t('bright')}</Option>
|
||||
<Option value="Dark">{t('dark')}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item<FieldType>
|
||||
label={t('language', { keyPrefix: 'common' })}
|
||||
name="language"
|
||||
rules={[
|
||||
{ required: true, message: 'Please select your color schema!' },
|
||||
{
|
||||
required: true,
|
||||
message: t('languageMessage', { keyPrefix: 'common' }),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select placeholder="select your color schema">
|
||||
<Option value="Bright">Bright</Option>
|
||||
<Option value="Dark">Dark</Option>
|
||||
<Select
|
||||
placeholder={t('languagePlaceholder', { keyPrefix: 'common' })}
|
||||
onChange={changeLanguage}
|
||||
>
|
||||
<Option value="English">
|
||||
{t('english', { keyPrefix: 'common' })}
|
||||
</Option>
|
||||
<Option value="Chinese">
|
||||
{t('chinese', { keyPrefix: 'common' })}
|
||||
</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item<FieldType>
|
||||
label="Language"
|
||||
name="language"
|
||||
rules={[{ required: true, message: 'Please input your language!' }]}
|
||||
>
|
||||
<Select placeholder="select your language">
|
||||
<Option value="English">English</Option>
|
||||
<Option value="Chinese">Chinese</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item<FieldType>
|
||||
label="Timezone"
|
||||
label={t('timezone')}
|
||||
name="timezone"
|
||||
rules={[{ required: true, message: 'Please input your timezone!' }]}
|
||||
rules={[{ required: true, message: t('timezoneMessage') }]}
|
||||
>
|
||||
<Select placeholder="select your timezone" showSearch>
|
||||
<Select placeholder={t('timezonePlaceholder')} showSearch>
|
||||
{TimezoneList.map((x) => (
|
||||
<Option value={x} key={x}>
|
||||
{x}
|
||||
@@ -169,12 +185,12 @@ const UserSettingProfile = () => {
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item label="Email address">
|
||||
<Form.Item label={t('email')}>
|
||||
<Form.Item<FieldType> name="email" noStyle>
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
<p className={parentStyles.itemDescription}>
|
||||
Once registered, E-mail cannot be changed.
|
||||
{t('emailDescription')}
|
||||
</p>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
@@ -184,14 +200,14 @@ const UserSettingProfile = () => {
|
||||
}
|
||||
>
|
||||
<Space>
|
||||
<Button htmlType="button">Cancel</Button>
|
||||
<Button htmlType="button">{t('cancel')}</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
disabled={!submittable}
|
||||
loading={submitLoading}
|
||||
>
|
||||
Save
|
||||
{t('save', { keyPrefix: 'common' })}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import { Button, Card, Flex } from 'antd';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { useSelectUserInfo } from '@/hooks/userSettingHook';
|
||||
import styles from './index.less';
|
||||
|
||||
const UserSettingTeam = () => {
|
||||
const userInfo = useSelectUserInfo();
|
||||
const { t } = useTranslate('setting');
|
||||
|
||||
return (
|
||||
<div className={styles.teamWrapper}>
|
||||
<Card className={styles.teamCard}>
|
||||
<Flex align="center" justify={'space-between'}>
|
||||
<span>{userInfo.nickname} Workspace</span>
|
||||
<Button type="primary">Upgrade</Button>
|
||||
<span>
|
||||
{userInfo.nickname} {t('workspace')}
|
||||
</span>
|
||||
<Button type="primary">{t('upgrade')}</Button>
|
||||
</Flex>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -7,38 +7,39 @@ import {
|
||||
UserSettingBaseKey,
|
||||
UserSettingIconMap,
|
||||
UserSettingRouteKey,
|
||||
UserSettingRouteMap,
|
||||
} from '../constants';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { useLogout } from '@/hooks/userSettingHook';
|
||||
import styles from './index.less';
|
||||
|
||||
type MenuItem = Required<MenuProps>['items'][number];
|
||||
|
||||
function getItem(
|
||||
label: React.ReactNode,
|
||||
key: React.Key,
|
||||
icon?: React.ReactNode,
|
||||
children?: MenuItem[],
|
||||
type?: 'group',
|
||||
): MenuItem {
|
||||
return {
|
||||
key,
|
||||
icon,
|
||||
children,
|
||||
label,
|
||||
type,
|
||||
} as MenuItem;
|
||||
}
|
||||
|
||||
const items: MenuItem[] = Object.values(UserSettingRouteKey).map((value) =>
|
||||
getItem(UserSettingRouteMap[value], value, UserSettingIconMap[value]),
|
||||
);
|
||||
|
||||
const SideBar = () => {
|
||||
const navigate = useNavigate();
|
||||
const pathName = useSecondPathName();
|
||||
const logout = useLogout();
|
||||
const { t } = useTranslate('setting');
|
||||
|
||||
function getItem(
|
||||
label: string,
|
||||
key: React.Key,
|
||||
icon?: React.ReactNode,
|
||||
children?: MenuItem[],
|
||||
type?: 'group',
|
||||
): MenuItem {
|
||||
return {
|
||||
key,
|
||||
icon,
|
||||
children,
|
||||
label: t(label),
|
||||
type,
|
||||
} as MenuItem;
|
||||
}
|
||||
|
||||
const items: MenuItem[] = Object.values(UserSettingRouteKey).map((value) =>
|
||||
getItem(value, value, UserSettingIconMap[value]),
|
||||
);
|
||||
|
||||
const handleMenuClick: MenuProps['onClick'] = ({ key }) => {
|
||||
if (key === UserSettingRouteKey.Logout) {
|
||||
|
||||
Reference in New Issue
Block a user