mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-04-23 22:50:17 +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:
parent
791afbba15
commit
cf3106040a
@ -2,8 +2,19 @@ import { LanguageTranslationMap } from '@/constants/common';
|
|||||||
import { ResponseGetType } from '@/interfaces/database/base';
|
import { ResponseGetType } from '@/interfaces/database/base';
|
||||||
import { IToken } from '@/interfaces/database/chat';
|
import { IToken } from '@/interfaces/database/chat';
|
||||||
import { ITenantInfo } from '@/interfaces/database/knowledge';
|
import { ITenantInfo } from '@/interfaces/database/knowledge';
|
||||||
import { ISystemStatus, IUserInfo } from '@/interfaces/database/user-setting';
|
import {
|
||||||
import userService from '@/services/user-service';
|
ISystemStatus,
|
||||||
|
ITenant,
|
||||||
|
ITenantUser,
|
||||||
|
IUserInfo,
|
||||||
|
} from '@/interfaces/database/user-setting';
|
||||||
|
import userService, {
|
||||||
|
addTenantUser,
|
||||||
|
agreeTenant,
|
||||||
|
deleteTenantUser,
|
||||||
|
listTenant,
|
||||||
|
listTenantUser,
|
||||||
|
} from '@/services/user-service';
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { Modal, message } from 'antd';
|
import { Modal, message } from 'antd';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
@ -215,3 +226,125 @@ export const useCreateSystemToken = () => {
|
|||||||
|
|
||||||
return { data, loading, createToken: mutateAsync };
|
return { data, loading, createToken: mutateAsync };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useListTenantUser = () => {
|
||||||
|
const { data: tenantInfo } = useFetchTenantInfo();
|
||||||
|
const tenantId = tenantInfo.tenant_id;
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isFetching: loading,
|
||||||
|
refetch,
|
||||||
|
} = useQuery<ITenantUser[]>({
|
||||||
|
queryKey: ['listTenantUser', tenantId],
|
||||||
|
initialData: [],
|
||||||
|
gcTime: 0,
|
||||||
|
enabled: !!tenantId,
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await listTenantUser(tenantId);
|
||||||
|
|
||||||
|
return data?.data ?? [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, refetch };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAddTenantUser = () => {
|
||||||
|
const { data: tenantInfo } = useFetchTenantInfo();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: ['addTenantUser'],
|
||||||
|
mutationFn: async (email: string) => {
|
||||||
|
const { data } = await addTenantUser(tenantInfo.tenant_id, email);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['listTenantUser'] });
|
||||||
|
}
|
||||||
|
return data?.retcode;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, addTenantUser: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteTenantUser = () => {
|
||||||
|
const { data: tenantInfo } = useFetchTenantInfo();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: ['deleteTenantUser'],
|
||||||
|
mutationFn: async ({
|
||||||
|
userId,
|
||||||
|
tenantId,
|
||||||
|
}: {
|
||||||
|
userId: string;
|
||||||
|
tenantId?: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await deleteTenantUser({
|
||||||
|
tenantId: tenantId ?? tenantInfo.tenant_id,
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
message.success(t('message.deleted'));
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['listTenantUser'] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['listTenant'] });
|
||||||
|
}
|
||||||
|
return data?.data ?? [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, deleteTenantUser: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useListTenant = () => {
|
||||||
|
const { data: tenantInfo } = useFetchTenantInfo();
|
||||||
|
const tenantId = tenantInfo.tenant_id;
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isFetching: loading,
|
||||||
|
refetch,
|
||||||
|
} = useQuery<ITenant[]>({
|
||||||
|
queryKey: ['listTenant', tenantId],
|
||||||
|
initialData: [],
|
||||||
|
gcTime: 0,
|
||||||
|
enabled: !!tenantId,
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await listTenant();
|
||||||
|
|
||||||
|
return data?.data ?? [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, refetch };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAgreeTenant = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: ['agreeTenant'],
|
||||||
|
mutationFn: async (tenantId: string) => {
|
||||||
|
const { data } = await agreeTenant(tenantId);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
message.success(t('message.operated'));
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['listTenant'] });
|
||||||
|
}
|
||||||
|
return data?.data ?? [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, agreeTenant: mutateAsync };
|
||||||
|
};
|
||||||
|
@ -60,3 +60,28 @@ interface Es {
|
|||||||
number_of_nodes: number;
|
number_of_nodes: number;
|
||||||
active_shards: number;
|
active_shards: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ITenantUser {
|
||||||
|
avatar: null;
|
||||||
|
delta_seconds: number;
|
||||||
|
email: string;
|
||||||
|
is_active: string;
|
||||||
|
is_anonymous: string;
|
||||||
|
is_authenticated: string;
|
||||||
|
is_superuser: boolean;
|
||||||
|
nickname: string;
|
||||||
|
role: string;
|
||||||
|
status: string;
|
||||||
|
update_date: string;
|
||||||
|
user_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITenant {
|
||||||
|
avatar: string;
|
||||||
|
delta_seconds: number;
|
||||||
|
email: string;
|
||||||
|
nickname: string;
|
||||||
|
role: string;
|
||||||
|
tenant_id: string;
|
||||||
|
update_date: string;
|
||||||
|
}
|
||||||
|
@ -27,7 +27,8 @@ export default {
|
|||||||
close: 'Close',
|
close: 'Close',
|
||||||
preview: 'Preview',
|
preview: 'Preview',
|
||||||
move: 'Move',
|
move: 'Move',
|
||||||
warn: '提醒',
|
warn: 'Warn',
|
||||||
|
action: 'Action',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
login: 'Sign in',
|
login: 'Sign in',
|
||||||
@ -584,6 +585,14 @@ The above is the content you need to summarize.`,
|
|||||||
'Please add both embedding model and LLM in <b>Settings > Model providers</b> firstly.',
|
'Please add both embedding model and LLM in <b>Settings > Model providers</b> firstly.',
|
||||||
apiVersion: 'API-Version',
|
apiVersion: 'API-Version',
|
||||||
apiVersionMessage: 'Please input API version',
|
apiVersionMessage: 'Please input API version',
|
||||||
|
add: 'Add',
|
||||||
|
updateDate: 'Update Date',
|
||||||
|
role: 'Role',
|
||||||
|
invite: 'Invite',
|
||||||
|
agree: 'Agree',
|
||||||
|
refuse: 'Refuse',
|
||||||
|
teamMembers: 'Team Members',
|
||||||
|
joinedTeams: 'Joined Teams',
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
registered: 'Registered!',
|
registered: 'Registered!',
|
||||||
|
@ -28,6 +28,7 @@ export default {
|
|||||||
preview: '預覽',
|
preview: '預覽',
|
||||||
move: '移動',
|
move: '移動',
|
||||||
warn: '提醒',
|
warn: '提醒',
|
||||||
|
action: '操作',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
login: '登入',
|
login: '登入',
|
||||||
@ -540,6 +541,14 @@ export default {
|
|||||||
GoogleRegionMessage: '請輸入 Google Cloud 區域',
|
GoogleRegionMessage: '請輸入 Google Cloud 區域',
|
||||||
modelProvidersWarn:
|
modelProvidersWarn:
|
||||||
'請先在 <b>「設定」>「模型提供者」</b> 中新增嵌入模型和LLM。',
|
'請先在 <b>「設定」>「模型提供者」</b> 中新增嵌入模型和LLM。',
|
||||||
|
add: '添加',
|
||||||
|
updateDate: '更新日期',
|
||||||
|
role: '角色',
|
||||||
|
invite: '邀請',
|
||||||
|
agree: '同意',
|
||||||
|
refuse: '拒絕',
|
||||||
|
teamMembers: '團隊成員',
|
||||||
|
joinedTeams: '加入的團隊',
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
registered: '註冊成功',
|
registered: '註冊成功',
|
||||||
|
@ -28,6 +28,7 @@ export default {
|
|||||||
preview: '预览',
|
preview: '预览',
|
||||||
move: '移动',
|
move: '移动',
|
||||||
warn: '提醒',
|
warn: '提醒',
|
||||||
|
action: '操作',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
login: '登录',
|
login: '登录',
|
||||||
@ -559,6 +560,14 @@ export default {
|
|||||||
'请首先在 <b>设置 > 模型提供商</b> 中添加嵌入模型和 LLM。',
|
'请首先在 <b>设置 > 模型提供商</b> 中添加嵌入模型和 LLM。',
|
||||||
apiVersion: 'API版本',
|
apiVersion: 'API版本',
|
||||||
apiVersionMessage: '请输入API版本!',
|
apiVersionMessage: '请输入API版本!',
|
||||||
|
add: '添加',
|
||||||
|
updateDate: '更新日期',
|
||||||
|
role: '角色',
|
||||||
|
invite: '邀请',
|
||||||
|
agree: '同意',
|
||||||
|
refuse: '拒绝',
|
||||||
|
teamMembers: '团队成员',
|
||||||
|
joinedTeams: '加入的团队',
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
registered: '注册成功',
|
registered: '注册成功',
|
||||||
|
@ -30,3 +30,9 @@ export const LocalLlmFactories = [
|
|||||||
'OpenRouter',
|
'OpenRouter',
|
||||||
'HuggingFace',
|
'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 {
|
.teamWrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
.teamCard {
|
.teamCard {
|
||||||
// width: 100%;
|
// 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 { TeamOutlined, UserAddOutlined, UserOutlined } from '@ant-design/icons';
|
||||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
import AddingUserModal from './add-user-modal';
|
||||||
|
import { useAddUser } from './hooks';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
import TenantTable from './tenant-table';
|
||||||
|
import UserTable from './user-table';
|
||||||
|
|
||||||
|
const iconStyle = { fontSize: 20, color: '#1677ff' };
|
||||||
|
|
||||||
const UserSettingTeam = () => {
|
const UserSettingTeam = () => {
|
||||||
const { data: userInfo } = useFetchUserInfo();
|
const { data: userInfo } = useFetchUserInfo();
|
||||||
const { t } = useTranslate('setting');
|
const { t } = useTranslation();
|
||||||
|
useListTenantUser();
|
||||||
|
const {
|
||||||
|
addingTenantModalVisible,
|
||||||
|
hideAddingTenantModal,
|
||||||
|
showAddingTenantModal,
|
||||||
|
handleAddUserOk,
|
||||||
|
} = useAddUser();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.teamWrapper}>
|
<div className={styles.teamWrapper}>
|
||||||
<Card className={styles.teamCard}>
|
<Card className={styles.teamCard}>
|
||||||
<Flex align="center" justify={'space-between'}>
|
<Flex align="center" justify={'space-between'}>
|
||||||
<span>
|
<span>
|
||||||
{userInfo.nickname} {t('workspace')}
|
{userInfo.nickname} {t('setting.workspace')}
|
||||||
</span>
|
</span>
|
||||||
<Button type="primary" disabled>
|
<Button type="primary" onClick={showAddingTenantModal}>
|
||||||
{t('upgrade')}
|
<UserAddOutlined />
|
||||||
|
{t('setting.invite')}
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Card>
|
</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>
|
</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;
|
@ -1,6 +1,6 @@
|
|||||||
import api from '@/utils/api';
|
import api from '@/utils/api';
|
||||||
import registerServer from '@/utils/register-server';
|
import registerServer from '@/utils/register-server';
|
||||||
import request from '@/utils/request';
|
import request, { post } from '@/utils/request';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
login,
|
login,
|
||||||
@ -105,4 +105,23 @@ const methods = {
|
|||||||
|
|
||||||
const userService = registerServer<keyof typeof methods>(methods, request);
|
const userService = registerServer<keyof typeof methods>(methods, request);
|
||||||
|
|
||||||
|
export const listTenantUser = (tenantId: string) =>
|
||||||
|
request.get(api.listTenantUser(tenantId));
|
||||||
|
|
||||||
|
export const addTenantUser = (tenantId: string, email: string) =>
|
||||||
|
post(api.addTenantUser(tenantId), { email });
|
||||||
|
|
||||||
|
export const deleteTenantUser = ({
|
||||||
|
tenantId,
|
||||||
|
userId,
|
||||||
|
}: {
|
||||||
|
tenantId: string;
|
||||||
|
userId: string;
|
||||||
|
}) => request.delete(api.deleteTenantUser(tenantId, userId));
|
||||||
|
|
||||||
|
export const listTenant = () => request.get(api.listTenant);
|
||||||
|
|
||||||
|
export const agreeTenant = (tenantId: string) =>
|
||||||
|
request.put(api.agreeTenant(tenantId));
|
||||||
|
|
||||||
export default userService;
|
export default userService;
|
||||||
|
@ -12,6 +12,15 @@ export default {
|
|||||||
tenant_info: `${api_host}/user/tenant_info`,
|
tenant_info: `${api_host}/user/tenant_info`,
|
||||||
set_tenant_info: `${api_host}/user/set_tenant_info`,
|
set_tenant_info: `${api_host}/user/set_tenant_info`,
|
||||||
|
|
||||||
|
// team
|
||||||
|
addTenantUser: (tenantId: string) => `${api_host}/tenant/${tenantId}/user`,
|
||||||
|
listTenantUser: (tenantId: string) =>
|
||||||
|
`${api_host}/tenant/${tenantId}/user/list`,
|
||||||
|
deleteTenantUser: (tenantId: string, userId: string) =>
|
||||||
|
`${api_host}/tenant/${tenantId}/user/${userId}`,
|
||||||
|
listTenant: `${api_host}/tenant/list`,
|
||||||
|
agreeTenant: (tenantId: string) => `${api_host}/tenant/agree/${tenantId}`,
|
||||||
|
|
||||||
// llm model
|
// llm model
|
||||||
factories_list: `${api_host}/llm/factories`,
|
factories_list: `${api_host}/llm/factories`,
|
||||||
llm_list: `${api_host}/llm/list`,
|
llm_list: `${api_host}/llm/list`,
|
||||||
|
@ -135,3 +135,15 @@ request.interceptors.response.use(async (response: any, options) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default request;
|
export default request;
|
||||||
|
|
||||||
|
export const get = (url: string) => {
|
||||||
|
return request.get(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const post = (url: string, body: any) => {
|
||||||
|
return request.post(url, { data: body });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const drop = () => {};
|
||||||
|
|
||||||
|
export const put = () => {};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user