feat: remove showDeleteConfirm function feat: center the Empty of knowledge list feat: extract the text of the login page to en.json #204tags/v0.1.0
| @@ -1,16 +1,16 @@ | |||
| import { ConfigProvider } from 'antd'; | |||
| import React, { ReactNode } from 'react'; | |||
| import { App, ConfigProvider } from 'antd'; | |||
| import { ReactNode } from 'react'; | |||
| export function rootContainer(container: ReactNode) { | |||
| return React.createElement( | |||
| ConfigProvider, | |||
| { | |||
| theme: { | |||
| return ( | |||
| <ConfigProvider | |||
| theme={{ | |||
| token: { | |||
| fontFamily: 'Inter', | |||
| }, | |||
| }, | |||
| }, | |||
| container, | |||
| }} | |||
| > | |||
| <App> {container}</App> | |||
| </ConfigProvider> | |||
| ); | |||
| } | |||
| @@ -1,39 +0,0 @@ | |||
| import { ExclamationCircleFilled } from '@ant-design/icons'; | |||
| import { Modal } from 'antd'; | |||
| const { confirm } = Modal; | |||
| interface IProps { | |||
| onOk?: (...args: any[]) => any; | |||
| onCancel?: (...args: any[]) => any; | |||
| } | |||
| export const showDeleteConfirm = ({ | |||
| onOk, | |||
| onCancel, | |||
| }: IProps): Promise<number> => { | |||
| return new Promise((resolve, reject) => { | |||
| confirm({ | |||
| title: 'Are you sure delete this item?', | |||
| icon: <ExclamationCircleFilled />, | |||
| content: 'Some descriptions', | |||
| okText: 'Yes', | |||
| okType: 'danger', | |||
| cancelText: 'No', | |||
| async onOk() { | |||
| try { | |||
| const ret = await onOk?.(); | |||
| resolve(ret); | |||
| console.info(ret); | |||
| } catch (error) { | |||
| reject(error); | |||
| } | |||
| }, | |||
| onCancel() { | |||
| onCancel?.(); | |||
| }, | |||
| }); | |||
| }); | |||
| }; | |||
| export default showDeleteConfirm; | |||
| @@ -4,7 +4,6 @@ import { IconComponentProps } from '@ant-design/icons/lib/components/Icon'; | |||
| const importAll = (requireContext: __WebpackModuleApi.RequireContext) => { | |||
| const list = requireContext.keys().map((key) => { | |||
| const name = key.replace(/\.\/(.*)\.\w+$/, '$1'); | |||
| console.log(name, requireContext(key)); | |||
| return { name, value: requireContext(key) }; | |||
| }); | |||
| return list; | |||
| @@ -1,5 +1,8 @@ | |||
| import { ExclamationCircleFilled } from '@ant-design/icons'; | |||
| import { App } from 'antd'; | |||
| import isEqual from 'lodash/isEqual'; | |||
| import { useEffect, useRef, useState } from 'react'; | |||
| import { useCallback, useEffect, useRef, useState } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| export const useSetModalState = () => { | |||
| const [visible, setVisible] = useState(false); | |||
| @@ -72,3 +75,43 @@ export function useDynamicSVGImport( | |||
| return { error, loading, SvgIcon: ImportedIconRef.current }; | |||
| } | |||
| interface IProps { | |||
| onOk?: (...args: any[]) => any; | |||
| onCancel?: (...args: any[]) => any; | |||
| } | |||
| export const useShowDeleteConfirm = () => { | |||
| const { modal } = App.useApp(); | |||
| const { t } = useTranslation(); | |||
| const showDeleteConfirm = useCallback( | |||
| ({ onOk, onCancel }: IProps): Promise<number> => { | |||
| return new Promise((resolve, reject) => { | |||
| modal.confirm({ | |||
| title: t('common.deleteModalTitle'), | |||
| icon: <ExclamationCircleFilled />, | |||
| // content: 'Some descriptions', | |||
| okText: 'Yes', | |||
| okType: 'danger', | |||
| cancelText: 'No', | |||
| async onOk() { | |||
| try { | |||
| const ret = await onOk?.(); | |||
| resolve(ret); | |||
| console.info(ret); | |||
| } catch (error) { | |||
| reject(error); | |||
| } | |||
| }, | |||
| onCancel() { | |||
| onCancel?.(); | |||
| }, | |||
| }); | |||
| }); | |||
| }, | |||
| [t, modal], | |||
| ); | |||
| return showDeleteConfirm; | |||
| }; | |||
| @@ -1,4 +1,4 @@ | |||
| import showDeleteConfirm from '@/components/deleting-confirm'; | |||
| import { useShowDeleteConfirm } from '@/hooks/commonHooks'; | |||
| import { IKnowledge } from '@/interfaces/database/knowledge'; | |||
| import { useCallback, useEffect, useMemo } from 'react'; | |||
| import { useDispatch, useSearchParams, useSelector } from 'umi'; | |||
| @@ -17,6 +17,7 @@ export const useDeleteDocumentById = (): { | |||
| } => { | |||
| const dispatch = useDispatch(); | |||
| const knowledgeBaseId = useKnowledgeBaseId(); | |||
| const showDeleteConfirm = useShowDeleteConfirm(); | |||
| const removeDocument = (documentId: string) => () => { | |||
| return dispatch({ | |||
| @@ -79,6 +80,7 @@ export const useDeleteChunkByIds = (): { | |||
| removeChunk: (chunkIds: string[], documentId: string) => Promise<number>; | |||
| } => { | |||
| const dispatch = useDispatch(); | |||
| const showDeleteConfirm = useShowDeleteConfirm(); | |||
| const removeChunk = useCallback( | |||
| (chunkIds: string[], documentId: string) => () => { | |||
| @@ -97,7 +99,7 @@ export const useDeleteChunkByIds = (): { | |||
| (chunkIds: string[], documentId: string): Promise<number> => { | |||
| return showDeleteConfirm({ onOk: removeChunk(chunkIds, documentId) }); | |||
| }, | |||
| [removeChunk], | |||
| [removeChunk, showDeleteConfirm], | |||
| ); | |||
| return { | |||
| @@ -8,6 +8,7 @@ import styles from './index.less'; | |||
| import { useNavigateWithFromState } from '@/hooks/routeHook'; | |||
| import { useCallback, useMemo } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { useLocation } from 'umi'; | |||
| const { Header } = Layout; | |||
| @@ -18,14 +19,15 @@ const RagHeader = () => { | |||
| } = theme.useToken(); | |||
| const navigate = useNavigateWithFromState(); | |||
| const { pathname } = useLocation(); | |||
| const { t } = useTranslation('translation', { keyPrefix: 'header' }); | |||
| const tagsData = useMemo( | |||
| () => [ | |||
| { path: '/knowledge', name: 'Knowledge Base', icon: KnowledgeBaseIcon }, | |||
| { path: '/chat', name: 'Chat', icon: StarIon }, | |||
| { path: '/knowledge', name: t('knowledgeBase'), icon: KnowledgeBaseIcon }, | |||
| { path: '/chat', name: t('chat'), icon: StarIon }, | |||
| // { path: '/file', name: 'File Management', icon: FileIcon }, | |||
| ], | |||
| [], | |||
| [t], | |||
| ); | |||
| const currentPath = useMemo(() => { | |||
| @@ -1,12 +1,41 @@ | |||
| { | |||
| "login": { "login": "Sign in" }, | |||
| "common": { | |||
| "delete": "Delete", | |||
| "deleteModalTitle": "Are you sure delete this item?" | |||
| }, | |||
| "login": { | |||
| "login": "Sign in", | |||
| "signUp": "Sign up", | |||
| "loginDescription": "We’re so excited to see you again!", | |||
| "registerDescription": "Glad to have you on board!", | |||
| "emailLabel": "Email", | |||
| "emailPlaceholder": "Please input email", | |||
| "passwordLabel": "Password", | |||
| "passwordPlaceholder": "Please input password", | |||
| "rememberMe": "Remember me", | |||
| "signInTip": "Don’t have an account?", | |||
| "signUpTip": "Already have an account?", | |||
| "nicknameLabel": "Nickname", | |||
| "nicknamePlaceholder": "Please input nickname", | |||
| "register": "Create an account", | |||
| "continue": "Continue" | |||
| }, | |||
| "header": { | |||
| "knowledgeBase": "Knowledge Base", | |||
| "chat": "Chat", | |||
| "register": "Register", | |||
| "signin": "Sign in", | |||
| "home": "Home", | |||
| "setting": "用户设置", | |||
| "logout": "登出" | |||
| }, | |||
| "knowledgeList": { | |||
| "welcome": "Welcome back", | |||
| "description": "Which database are we going to use today?", | |||
| "createKnowledgeBase": "Create knowledge base", | |||
| "name": "Name", | |||
| "namePlaceholder": "Please input name!" | |||
| }, | |||
| "footer": { | |||
| "detail": "All rights reserved @ React" | |||
| }, | |||
| @@ -203,7 +203,6 @@ const model: DvaModel<KFModelState> = { | |||
| const documentId = payload; | |||
| try { | |||
| const ret = yield call(getDocumentFile, documentId); | |||
| console.info('fetch_document_file:', ret); | |||
| return ret; | |||
| } catch (error) { | |||
| console.warn(error); | |||
| @@ -238,7 +237,6 @@ const model: DvaModel<KFModelState> = { | |||
| payload: { current: 1, pageSize: 10 }, | |||
| }); | |||
| } | |||
| console.info(location); | |||
| }); | |||
| }, | |||
| }, | |||
| @@ -1,4 +1,4 @@ | |||
| import showDeleteConfirm from '@/components/deleting-confirm'; | |||
| import { useShowDeleteConfirm } from '@/hooks/commonHooks'; | |||
| import { useRemoveDocument } from '@/hooks/documentHooks'; | |||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import { api_host } from '@/utils/api'; | |||
| @@ -31,6 +31,7 @@ const ParsingActionCell = ({ | |||
| const isRunning = isParserRunning(record.run); | |||
| const removeDocument = useRemoveDocument(documentId); | |||
| const showDeleteConfirm = useShowDeleteConfirm(); | |||
| const onRmDocument = () => { | |||
| if (!isRunning) { | |||
| @@ -1,7 +1,6 @@ | |||
| import showDeleteConfirm from '@/components/deleting-confirm'; | |||
| import { MessageType } from '@/constants/chat'; | |||
| import { fileIconMap } from '@/constants/common'; | |||
| import { useSetModalState } from '@/hooks/commonHooks'; | |||
| import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks'; | |||
| import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; | |||
| import { IConversation, IDialog } from '@/interfaces/database/chat'; | |||
| import { IChunk } from '@/interfaces/database/knowledge'; | |||
| @@ -150,6 +149,7 @@ export const useSelectPromptConfigParameters = (): VariableTableDataType[] => { | |||
| export const useRemoveDialog = () => { | |||
| const dispatch = useDispatch(); | |||
| const showDeleteConfirm = useShowDeleteConfirm(); | |||
| const removeDocument = (dialogIds: Array<string>) => () => { | |||
| return dispatch({ | |||
| @@ -668,6 +668,7 @@ export const useRemoveConversation = () => { | |||
| const dispatch = useDispatch(); | |||
| const { dialogId } = useGetChatSearchParams(); | |||
| const { handleClickConversation } = useClickConversationCard(); | |||
| const showDeleteConfirm = useShowDeleteConfirm(); | |||
| const removeConversation = (conversationIds: Array<string>) => async () => { | |||
| const ret = await dispatch<any>({ | |||
| @@ -44,4 +44,7 @@ | |||
| .knowledgeCardContainer { | |||
| padding: 0 60px; | |||
| overflow: auto; | |||
| .knowledgeEmpty { | |||
| width: 100%; | |||
| } | |||
| } | |||
| @@ -6,22 +6,22 @@ import { Button, Empty, Flex, Space, Spin } from 'antd'; | |||
| import KnowledgeCard from './knowledge-card'; | |||
| import KnowledgeCreatingModal from './knowledge-creating-modal'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import styles from './index.less'; | |||
| const Knowledge = () => { | |||
| const { list, loading } = useFetchKnowledgeList(); | |||
| const userInfo = useSelectUserInfo(); | |||
| const { t } = useTranslation('translation', { keyPrefix: 'knowledgeList' }); | |||
| return ( | |||
| <Flex className={styles.knowledge} vertical flex={1}> | |||
| <div className={styles.topWrapper}> | |||
| <div> | |||
| <span className={styles.title}> | |||
| Welcome back, {userInfo.nickname} | |||
| {t('welcome')}, {userInfo.nickname} | |||
| </span> | |||
| <p className={styles.description}> | |||
| Which database are we going to use today? | |||
| </p> | |||
| <p className={styles.description}>{t('description')}</p> | |||
| </div> | |||
| <Space size={'large'}> | |||
| {/* <Button icon={<FilterIcon />} className={styles.filterButton}> | |||
| @@ -38,7 +38,7 @@ const Knowledge = () => { | |||
| }} | |||
| className={styles.topButton} | |||
| > | |||
| Create knowledge base | |||
| {t('createKnowledgeBase')} | |||
| </Button> | |||
| <KnowledgeCreatingModal | |||
| visible={visible} | |||
| @@ -62,7 +62,7 @@ const Knowledge = () => { | |||
| ); | |||
| }) | |||
| ) : ( | |||
| <Empty></Empty> | |||
| <Empty className={styles.knowledgeEmpty}></Empty> | |||
| )} | |||
| </Flex> | |||
| </Spin> | |||
| @@ -1,5 +1,6 @@ | |||
| import { ReactComponent as MoreIcon } from '@/assets/svg/more.svg'; | |||
| import { KnowledgeRouteKey } from '@/constants/knowledge'; | |||
| import { useShowDeleteConfirm } from '@/hooks/commonHooks'; | |||
| import { IKnowledge } from '@/interfaces/database/knowledge'; | |||
| import { formatDate } from '@/utils/date'; | |||
| import { | |||
| @@ -11,7 +12,6 @@ import { | |||
| import { Avatar, Card, Dropdown, MenuProps, Space } from 'antd'; | |||
| import { useDispatch, useNavigate } from 'umi'; | |||
| import showDeleteConfirm from '@/components/deleting-confirm'; | |||
| import styles from './index.less'; | |||
| interface IProps { | |||
| @@ -21,6 +21,7 @@ interface IProps { | |||
| const KnowledgeCard = ({ item }: IProps) => { | |||
| const navigate = useNavigate(); | |||
| const dispatch = useDispatch(); | |||
| const showDeleteConfirm = useShowDeleteConfirm(); | |||
| const removeKnowledge = () => { | |||
| return dispatch({ | |||
| @@ -1,6 +1,7 @@ | |||
| import { IModalManagerChildrenProps } from '@/components/modal-manager'; | |||
| import { KnowledgeRouteKey } from '@/constants/knowledge'; | |||
| import { Form, Input, Modal } from 'antd'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { useDispatch, useNavigate, useSelector } from 'umi'; | |||
| type FieldType = { | |||
| @@ -17,6 +18,7 @@ const KnowledgeCreatingModal = ({ | |||
| (state: any) => state.loading.effects['kSModel/createKb'], | |||
| ); | |||
| const navigate = useNavigate(); | |||
| const { t } = useTranslation('translation', { keyPrefix: 'knowledgeList' }); | |||
| const handleOk = async () => { | |||
| const ret = await form.validateFields(); | |||
| @@ -50,7 +52,7 @@ const KnowledgeCreatingModal = ({ | |||
| return ( | |||
| <Modal | |||
| title="Create knowledge base" | |||
| title={t('createKnowledgeBase')} | |||
| open={visible} | |||
| onOk={handleOk} | |||
| onCancel={handleCancel} | |||
| @@ -67,11 +69,11 @@ const KnowledgeCreatingModal = ({ | |||
| form={form} | |||
| > | |||
| <Form.Item<FieldType> | |||
| label="Name" | |||
| label={t('name')} | |||
| name="name" | |||
| rules={[{ required: true, message: 'Please input name!' }]} | |||
| rules={[{ required: true, message: t('namePlaceholder') }]} | |||
| > | |||
| <Input /> | |||
| <Input placeholder={t('namePlaceholder')} /> | |||
| </Form.Item> | |||
| </Form> | |||
| </Modal> | |||
| @@ -1,3 +1,4 @@ | |||
| import { useLogin, useRegister } from '@/hooks/loginHooks'; | |||
| import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; | |||
| import { rsaPsw } from '@/utils'; | |||
| import { Button, Checkbox, Form, Input } from 'antd'; | |||
| @@ -6,7 +7,6 @@ import { useTranslation } from 'react-i18next'; | |||
| import { Icon, useNavigate } from 'umi'; | |||
| import RightPanel from './right-panel'; | |||
| import { useLogin, useRegister } from '@/hooks/loginHooks'; | |||
| import styles from './index.less'; | |||
| const Login = () => { | |||
| @@ -14,7 +14,7 @@ const Login = () => { | |||
| const navigate = useNavigate(); | |||
| const login = useLogin(); | |||
| const register = useRegister(); | |||
| const { t } = useTranslation(); | |||
| const { t } = useTranslation('translation', { keyPrefix: 'login' }); | |||
| // TODO: When the server address request is not accessible, the value of dva-loading always remains true. | |||
| @@ -75,13 +75,11 @@ const Login = () => { | |||
| <div className={styles.loginLeft}> | |||
| <div className={styles.leftContainer}> | |||
| <div className={styles.loginTitle}> | |||
| <div> | |||
| {title === 'login' ? t('login.login') : 'Create an account'} | |||
| </div> | |||
| <div>{title === 'login' ? t('login') : 'Create an account'}</div> | |||
| <span> | |||
| {title === 'login' | |||
| ? 'We’re so excited to see you again!' | |||
| : 'Glad to have you on board!'} | |||
| ? t('loginDescription') | |||
| : t('registerDescription')} | |||
| </span> | |||
| </div> | |||
| @@ -94,55 +92,52 @@ const Login = () => { | |||
| <Form.Item | |||
| {...formItemLayout} | |||
| name="email" | |||
| label="Email" | |||
| rules={[{ required: true, message: 'Please input value' }]} | |||
| label={t('emailLabel')} | |||
| rules={[{ required: true, message: t('emailPlaceholder') }]} | |||
| > | |||
| <Input size="large" placeholder="Please input value" /> | |||
| <Input size="large" placeholder={t('emailPlaceholder')} /> | |||
| </Form.Item> | |||
| {title === 'register' && ( | |||
| <Form.Item | |||
| {...formItemLayout} | |||
| name="nickname" | |||
| label="Nickname" | |||
| rules={[ | |||
| { required: true, message: 'Please input your nickname' }, | |||
| ]} | |||
| label={t('nicknameLabel')} | |||
| rules={[{ required: true, message: t('nicknamePlaceholder') }]} | |||
| > | |||
| <Input size="large" placeholder="Please input your nickname" /> | |||
| <Input size="large" placeholder={t('nicknamePlaceholder')} /> | |||
| </Form.Item> | |||
| )} | |||
| <Form.Item | |||
| {...formItemLayout} | |||
| name="password" | |||
| label="Password" | |||
| rules={[{ required: true, message: 'Please input value' }]} | |||
| label={t('passwordLabel')} | |||
| rules={[{ required: true, message: t('passwordPlaceholder') }]} | |||
| > | |||
| <Input.Password | |||
| size="large" | |||
| placeholder="Please input value" | |||
| placeholder={t('passwordPlaceholder')} | |||
| onPressEnter={onCheck} | |||
| /> | |||
| </Form.Item> | |||
| {title === 'login' && ( | |||
| <Form.Item name="remember" valuePropName="checked"> | |||
| <Checkbox> Remember me</Checkbox> | |||
| <Checkbox> {t('rememberMe')}</Checkbox> | |||
| </Form.Item> | |||
| )} | |||
| <div> | |||
| {' '} | |||
| {title === 'login' && ( | |||
| <div> | |||
| Don’t have an account? | |||
| {t('signInTip')} | |||
| <Button type="link" onClick={changeTitle}> | |||
| Sign up | |||
| {t('signUp')} | |||
| </Button> | |||
| </div> | |||
| )} | |||
| {title === 'register' && ( | |||
| <div> | |||
| Already have an account? | |||
| {t('signUpTip')} | |||
| <Button type="link" onClick={changeTitle}> | |||
| Sign in | |||
| {t('login')} | |||
| </Button> | |||
| </div> | |||
| )} | |||
| @@ -154,7 +149,7 @@ const Login = () => { | |||
| onClick={onCheck} | |||
| loading={signLoading} | |||
| > | |||
| {title === 'login' ? 'Sign in' : 'Continue'} | |||
| {title === 'login' ? t('login') : t('continue')} | |||
| </Button> | |||
| {title === 'login' && ( | |||
| <> | |||