feat: Bind data to TenantTable #2846 (#2883)

### 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:
balibabu
2024-10-18 09:21:01 +08:00
committed by GitHub
parent 791afbba15
commit cf3106040a
15 changed files with 548 additions and 11 deletions

View File

@ -30,3 +30,9 @@ export const LocalLlmFactories = [
'OpenRouter',
'HuggingFace',
];
export enum TenantRole {
Owner = 'owner',
Invite = 'invite',
Normal = 'normal',
}

View 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;

View 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 };
};

View File

@ -1,5 +1,8 @@
.teamWrapper {
width: 100%;
display: flex;
flex-direction: column;
gap: 20px;
.teamCard {
// width: 100%;
}

View File

@ -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>
);
};

View 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;

View 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;