mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
197 lines
5.5 KiB
TypeScript
197 lines
5.5 KiB
TypeScript
import React from "react";
|
||
import {
|
||
List,
|
||
Avatar,
|
||
Tag,
|
||
Button,
|
||
Select,
|
||
Popconfirm,
|
||
message,
|
||
Spin,
|
||
Alert,
|
||
Typography,
|
||
} from "antd";
|
||
import { DeleteOutlined, UserOutlined } from "@ant-design/icons";
|
||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||
import { api } from "@/shared/api";
|
||
import type { ServerUser, UserRole } from "@/shared/types";
|
||
|
||
const { Text } = Typography;
|
||
|
||
interface Props {
|
||
currentUserRole: UserRole;
|
||
}
|
||
|
||
export const TeamList: React.FC<Props> = ({ currentUserRole }) => {
|
||
const queryClient = useQueryClient();
|
||
|
||
const {
|
||
data: users,
|
||
isLoading,
|
||
isError,
|
||
} = useQuery({
|
||
queryKey: ["serverUsers"],
|
||
queryFn: api.getUsers,
|
||
});
|
||
|
||
const updateRoleMutation = useMutation({
|
||
mutationFn: ({ userId, newRole }: { userId: string; newRole: UserRole }) =>
|
||
api.updateUserRole(userId, newRole),
|
||
onSuccess: () => {
|
||
message.success("Роль пользователя обновлена");
|
||
queryClient.invalidateQueries({ queryKey: ["serverUsers"] });
|
||
},
|
||
onError: () => {
|
||
message.error("Не удалось изменить роль");
|
||
},
|
||
});
|
||
|
||
const removeUserMutation = useMutation({
|
||
mutationFn: (userId: string) => api.removeUser(userId),
|
||
onSuccess: () => {
|
||
message.success("Пользователь удален из команды");
|
||
queryClient.invalidateQueries({ queryKey: ["serverUsers"] });
|
||
},
|
||
onError: () => {
|
||
message.error("Не удалось удалить пользователя");
|
||
},
|
||
});
|
||
|
||
const getRoleColor = (role: UserRole) => {
|
||
switch (role) {
|
||
case "OWNER":
|
||
return "gold";
|
||
case "ADMIN":
|
||
return "blue";
|
||
case "OPERATOR":
|
||
return "default";
|
||
default:
|
||
return "default";
|
||
}
|
||
};
|
||
|
||
const getRoleName = (role: UserRole) => {
|
||
switch (role) {
|
||
case "OWNER":
|
||
return "Владелец";
|
||
case "ADMIN":
|
||
return "Админ";
|
||
case "OPERATOR":
|
||
return "Оператор";
|
||
default:
|
||
return role;
|
||
}
|
||
};
|
||
|
||
const canDelete = (targetUser: ServerUser) => {
|
||
if (targetUser.is_me) return false;
|
||
if (targetUser.role === "OWNER") return false;
|
||
if (currentUserRole === "ADMIN" && targetUser.role === "ADMIN")
|
||
return false;
|
||
return true;
|
||
};
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<div style={{ textAlign: "center", padding: 20 }}>
|
||
<Spin />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (isError) {
|
||
return <Alert type="error" message="Не удалось загрузить список команды" />;
|
||
}
|
||
|
||
return (
|
||
<>
|
||
<Alert
|
||
type="info"
|
||
showIcon
|
||
style={{ marginBottom: 16 }}
|
||
message="Приглашение сотрудников"
|
||
description="Чтобы добавить сотрудника, отправьте ему ссылку-приглашение."
|
||
/>
|
||
<List
|
||
itemLayout="horizontal"
|
||
dataSource={users || []}
|
||
renderItem={(user) => (
|
||
<List.Item
|
||
actions={[
|
||
currentUserRole === "OWNER" && !user.is_me ? (
|
||
<Select
|
||
key="role-select"
|
||
defaultValue={user.role}
|
||
size="small"
|
||
style={{ width: 110 }}
|
||
disabled={updateRoleMutation.isPending}
|
||
onChange={(val) =>
|
||
updateRoleMutation.mutate({
|
||
userId: user.user_id,
|
||
newRole: val,
|
||
})
|
||
}
|
||
options={[
|
||
{ value: "ADMIN", label: "Админ" },
|
||
{ value: "OPERATOR", label: "Оператор" },
|
||
]}
|
||
/>
|
||
) : (
|
||
<Tag key="role-tag" color={getRoleColor(user.role)}>
|
||
{getRoleName(user.role)}
|
||
</Tag>
|
||
),
|
||
<Popconfirm
|
||
key="delete"
|
||
title="Закрыть доступ?"
|
||
description={`Вы уверены, что хотите удалить ${user.first_name}?`}
|
||
onConfirm={() => removeUserMutation.mutate(user.user_id)}
|
||
disabled={!canDelete(user)}
|
||
okText="Да"
|
||
cancelText="Нет"
|
||
>
|
||
<Button
|
||
danger
|
||
type="text"
|
||
icon={<DeleteOutlined />}
|
||
disabled={!canDelete(user) || removeUserMutation.isPending}
|
||
/>
|
||
</Popconfirm>,
|
||
]}
|
||
>
|
||
<List.Item.Meta
|
||
avatar={
|
||
<Avatar src={user.photo_url} icon={<UserOutlined />}>
|
||
{user.first_name?.[0]}
|
||
</Avatar>
|
||
}
|
||
title={
|
||
<span>
|
||
{user.first_name} {user.last_name}{" "}
|
||
{user.is_me && <Text type="secondary">(Вы)</Text>}
|
||
</span>
|
||
}
|
||
description={
|
||
user.username ? (
|
||
<a
|
||
href={`https://t.me/${user.username}`}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
style={{ fontSize: 12 }}
|
||
>
|
||
@{user.username}
|
||
</a>
|
||
) : (
|
||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||
Нет username
|
||
</Text>
|
||
)
|
||
}
|
||
/>
|
||
</List.Item>
|
||
)}
|
||
/>
|
||
</>
|
||
);
|
||
};
|