mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-18 03:26:42 +08:00
### What problem does this PR solve? feat: Bind data to TenantTable #2846 feat: Add TenantTable ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -30,3 +30,9 @@ export const LocalLlmFactories = [
|
||||
'OpenRouter',
|
||||
'HuggingFace',
|
||||
];
|
||||
|
||||
export enum TenantRole {
|
||||
Owner = 'owner',
|
||||
Invite = 'invite',
|
||||
Normal = 'normal',
|
||||
}
|
||||
|
||||
52
web/src/pages/user-setting/setting-team/add-user-modal.tsx
Normal file
52
web/src/pages/user-setting/setting-team/add-user-modal.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { Form, Input, Modal } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const AddingUserModal = ({
|
||||
visible,
|
||||
hideModal,
|
||||
loading,
|
||||
onOk,
|
||||
}: IModalProps<string>) => {
|
||||
const [form] = Form.useForm();
|
||||
const { t } = useTranslation();
|
||||
|
||||
type FieldType = {
|
||||
email?: string;
|
||||
};
|
||||
|
||||
const handleOk = async () => {
|
||||
const ret = await form.validateFields();
|
||||
|
||||
return onOk?.(ret.email);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t('setting.add')}
|
||||
open={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={hideModal}
|
||||
okButtonProps={{ loading }}
|
||||
confirmLoading={loading}
|
||||
>
|
||||
<Form
|
||||
name="basic"
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 18 }}
|
||||
autoComplete="off"
|
||||
form={form}
|
||||
>
|
||||
<Form.Item<FieldType>
|
||||
label={t('setting.email')}
|
||||
name="email"
|
||||
rules={[{ required: true, message: t('namePlaceholder') }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddingUserModal;
|
||||
68
web/src/pages/user-setting/setting-team/hooks.ts
Normal file
68
web/src/pages/user-setting/setting-team/hooks.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks';
|
||||
import {
|
||||
useAddTenantUser,
|
||||
useAgreeTenant,
|
||||
useDeleteTenantUser,
|
||||
useFetchUserInfo,
|
||||
} from '@/hooks/user-setting-hooks';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useAddUser = () => {
|
||||
const { addTenantUser } = useAddTenantUser();
|
||||
const {
|
||||
visible: addingTenantModalVisible,
|
||||
hideModal: hideAddingTenantModal,
|
||||
showModal: showAddingTenantModal,
|
||||
} = useSetModalState();
|
||||
|
||||
const handleAddUserOk = useCallback(
|
||||
async (email: string) => {
|
||||
const retcode = await addTenantUser(email);
|
||||
if (retcode === 0) {
|
||||
hideAddingTenantModal();
|
||||
}
|
||||
},
|
||||
[addTenantUser, hideAddingTenantModal],
|
||||
);
|
||||
|
||||
return {
|
||||
addingTenantModalVisible,
|
||||
hideAddingTenantModal,
|
||||
showAddingTenantModal,
|
||||
handleAddUserOk,
|
||||
};
|
||||
};
|
||||
|
||||
export const useHandleDeleteUser = () => {
|
||||
const { deleteTenantUser, loading } = useDeleteTenantUser();
|
||||
const showDeleteConfirm = useShowDeleteConfirm();
|
||||
|
||||
const handleDeleteTenantUser = (userId: string) => () => {
|
||||
showDeleteConfirm({
|
||||
onOk: async () => {
|
||||
const retcode = await deleteTenantUser({ userId });
|
||||
if (retcode === 0) {
|
||||
}
|
||||
return;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return { handleDeleteTenantUser, loading };
|
||||
};
|
||||
|
||||
export const useHandleAgreeTenant = () => {
|
||||
const { agreeTenant } = useAgreeTenant();
|
||||
const { deleteTenantUser } = useDeleteTenantUser();
|
||||
const { data: user } = useFetchUserInfo();
|
||||
|
||||
const handleAgree = (tenantId: string, isAgree: boolean) => () => {
|
||||
if (isAgree) {
|
||||
agreeTenant(tenantId);
|
||||
} else {
|
||||
deleteTenantUser({ tenantId, userId: user.id });
|
||||
}
|
||||
};
|
||||
|
||||
return { handleAgree };
|
||||
};
|
||||
@ -1,5 +1,8 @@
|
||||
.teamWrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
.teamCard {
|
||||
// width: 100%;
|
||||
}
|
||||
|
||||
@ -1,25 +1,70 @@
|
||||
import { Button, Card, Flex } from 'antd';
|
||||
import {
|
||||
useFetchUserInfo,
|
||||
useListTenantUser,
|
||||
} from '@/hooks/user-setting-hooks';
|
||||
import { Button, Card, Flex, Space } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||
import { TeamOutlined, UserAddOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import AddingUserModal from './add-user-modal';
|
||||
import { useAddUser } from './hooks';
|
||||
import styles from './index.less';
|
||||
import TenantTable from './tenant-table';
|
||||
import UserTable from './user-table';
|
||||
|
||||
const iconStyle = { fontSize: 20, color: '#1677ff' };
|
||||
|
||||
const UserSettingTeam = () => {
|
||||
const { data: userInfo } = useFetchUserInfo();
|
||||
const { t } = useTranslate('setting');
|
||||
const { t } = useTranslation();
|
||||
useListTenantUser();
|
||||
const {
|
||||
addingTenantModalVisible,
|
||||
hideAddingTenantModal,
|
||||
showAddingTenantModal,
|
||||
handleAddUserOk,
|
||||
} = useAddUser();
|
||||
|
||||
return (
|
||||
<div className={styles.teamWrapper}>
|
||||
<Card className={styles.teamCard}>
|
||||
<Flex align="center" justify={'space-between'}>
|
||||
<span>
|
||||
{userInfo.nickname} {t('workspace')}
|
||||
{userInfo.nickname} {t('setting.workspace')}
|
||||
</span>
|
||||
<Button type="primary" disabled>
|
||||
{t('upgrade')}
|
||||
<Button type="primary" onClick={showAddingTenantModal}>
|
||||
<UserAddOutlined />
|
||||
{t('setting.invite')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</Card>
|
||||
<Card
|
||||
title={
|
||||
<Space>
|
||||
<UserOutlined style={iconStyle} /> {t('setting.teamMembers')}
|
||||
</Space>
|
||||
}
|
||||
bordered={false}
|
||||
>
|
||||
<UserTable></UserTable>
|
||||
</Card>
|
||||
<Card
|
||||
title={
|
||||
<Space>
|
||||
<TeamOutlined style={iconStyle} /> {t('setting.joinedTeams')}
|
||||
</Space>
|
||||
}
|
||||
bordered={false}
|
||||
>
|
||||
<TenantTable></TenantTable>
|
||||
</Card>
|
||||
{addingTenantModalVisible && (
|
||||
<AddingUserModal
|
||||
visible
|
||||
hideModal={hideAddingTenantModal}
|
||||
onOk={handleAddUserOk}
|
||||
></AddingUserModal>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
65
web/src/pages/user-setting/setting-team/tenant-table.tsx
Normal file
65
web/src/pages/user-setting/setting-team/tenant-table.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import { useListTenant } from '@/hooks/user-setting-hooks';
|
||||
import { ITenant } from '@/interfaces/database/user-setting';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import type { TableProps } from 'antd';
|
||||
import { Button, Space, Table } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TenantRole } from '../constants';
|
||||
import { useHandleAgreeTenant } from './hooks';
|
||||
|
||||
const TenantTable = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data, loading } = useListTenant();
|
||||
const { handleAgree } = useHandleAgreeTenant();
|
||||
|
||||
const columns: TableProps<ITenant>['columns'] = [
|
||||
{
|
||||
title: t('common.name'),
|
||||
dataIndex: 'nickname',
|
||||
key: 'nickname',
|
||||
},
|
||||
{
|
||||
title: t('setting.email'),
|
||||
dataIndex: 'email',
|
||||
key: 'email',
|
||||
},
|
||||
{
|
||||
title: t('setting.updateDate'),
|
||||
dataIndex: 'update_date',
|
||||
key: 'update_date',
|
||||
render(value) {
|
||||
return formatDate(value);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('common.action'),
|
||||
key: 'action',
|
||||
render: (_, { role, tenant_id }) => {
|
||||
if (role === TenantRole.Invite) {
|
||||
return (
|
||||
<Space>
|
||||
<Button type="link" onClick={handleAgree(tenant_id, true)}>
|
||||
{t(`setting.agree`)}
|
||||
</Button>
|
||||
<Button type="link" onClick={handleAgree(tenant_id, false)}>
|
||||
{t(`setting.refuse`)}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Table<ITenant>
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
rowKey={'tenant_id'}
|
||||
loading={loading}
|
||||
pagination={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default TenantTable;
|
||||
73
web/src/pages/user-setting/setting-team/user-table.tsx
Normal file
73
web/src/pages/user-setting/setting-team/user-table.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import { useListTenantUser } from '@/hooks/user-setting-hooks';
|
||||
import { ITenantUser } from '@/interfaces/database/user-setting';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import type { TableProps } from 'antd';
|
||||
import { Button, Table, Tag } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TenantRole } from '../constants';
|
||||
import { useHandleDeleteUser } from './hooks';
|
||||
|
||||
const ColorMap = {
|
||||
[TenantRole.Normal]: 'green',
|
||||
[TenantRole.Invite]: 'orange',
|
||||
[TenantRole.Owner]: 'red',
|
||||
};
|
||||
|
||||
const UserTable = () => {
|
||||
const { data, loading } = useListTenantUser();
|
||||
const { handleDeleteTenantUser } = useHandleDeleteUser();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const columns: TableProps<ITenantUser>['columns'] = [
|
||||
{
|
||||
title: t('common.name'),
|
||||
dataIndex: 'nickname',
|
||||
key: 'nickname',
|
||||
},
|
||||
{
|
||||
title: t('setting.email'),
|
||||
dataIndex: 'email',
|
||||
key: 'email',
|
||||
},
|
||||
{
|
||||
title: t('setting.role'),
|
||||
dataIndex: 'role',
|
||||
key: 'role',
|
||||
render(value, { role }) {
|
||||
return (
|
||||
<Tag color={ColorMap[role as keyof typeof ColorMap]}>{role}</Tag>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('setting.updateDate'),
|
||||
dataIndex: 'update_date',
|
||||
key: 'update_date',
|
||||
render(value) {
|
||||
return formatDate(value);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('common.action'),
|
||||
key: 'action',
|
||||
render: (_, record) => (
|
||||
<Button type="text" onClick={handleDeleteTenantUser(record.user_id)}>
|
||||
<DeleteOutlined size={20} />
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Table<ITenantUser>
|
||||
rowKey={'user_id'}
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
loading={loading}
|
||||
pagination={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserTable;
|
||||
Reference in New Issue
Block a user