Added Team mamagement with backend integration

This commit is contained in:
2026-04-30 11:11:11 +03:00
parent 8449da8373
commit 45c157b910
35 changed files with 1570 additions and 4671 deletions

View File

@@ -0,0 +1,242 @@
import {
Avatar,
Card,
MultiSelect,
Pill,
Stack,
Text,
UnstyledButton,
} from "@mantine/core";
import {
IconCheck,
IconCrown,
IconForbid,
IconPencil,
IconTrash,
} from "@tabler/icons-react";
import CustomButton from "Components/CustomButton/CustomButton";
import type { Team, TeamMember } from "Types/Team/Team";
import "./TeamMemberCard.css";
import { useState } from "react";
import { addMember, deleteMember } from "Api/QuantumBackend/TeamManagement";
import { useTeamStore } from "Stores/TeamsStore";
import { notifications } from "@mantine/notifications";
function TeamMemberCard(props: { cur_team: Team; member: TeamMember }) {
const { updateTeam } = useTeamStore();
const [is_editing, set_is_editing] = useState<boolean>(false);
const [selected_permissions, set_selected_permissions] = useState<string[]>(
props.member.permissions,
);
return (
<Card padding="sm" withBorder orientation="horizontal">
<Card.Section
inheritPadding
px="xs"
style={{
display: "flex",
alignItems: "center",
flexDirection: "column",
justifyContent:
props.member.user.keycloak_id !=
props.cur_team.creator.user.keycloak_id
? "center"
: "",
}}
>
{props.member.user.keycloak_id ==
props.cur_team.creator.user.keycloak_id && <IconCrown />}
<Avatar radius="xl" src={props.member.user.profile_picture_path} />
</Card.Section>
<Card.Section
withBorder
inheritPadding
px="xs"
style={{ width: "250px" }}
>
<Stack>
<Text size="xl">{props.member.user.username}</Text>
<Text size="md">{props.member.user.email}</Text>
</Stack>
</Card.Section>
<Card.Section
inheritPadding
px="md"
withBorder
style={{
display: "flex",
flexGrow: 1,
alignItems: "center",
}}
>
<Stack>
<div style={{ display: "flex", gap: "20px" }}>
<Text size="md">Разрешения: </Text>
{props.member.user.keycloak_id !=
props.cur_team.creator.user.keycloak_id &&
(is_editing ? (
<div style={{ display: "flex", gap: "10px" }}>
<CustomButton
style="outline"
text="Сохранить"
textSize="sm"
icon={<IconPencil size={15} />}
onClick={() => {
addMember({
team_id: props.cur_team.id,
user_id: props.member.user.keycloak_id,
permissions: selected_permissions,
})
.then((member) => {
if (member) {
updateTeam(props.cur_team.id, {
members: [
...props.cur_team.members.filter(
(member) =>
member.user.keycloak_id !=
props.member.user.keycloak_id,
),
member,
],
});
set_is_editing(false);
notifications.show({
radius: "md",
title: "Права изменены успешно",
message: "",
icon: <IconCheck />,
style: { paddingLeft: "5px" },
});
}
})
.catch(() => {
notifications.show({
radius: "md",
title: "Ошибка изменения прав",
message: "",
color: "red",
icon: <IconForbid />,
style: { paddingLeft: "5px" },
});
});
}}
/>
<CustomButton
style="outline"
text="Отменить"
color="red"
textSize="sm"
icon={<IconPencil size={15} />}
onClick={() => {
set_is_editing(false);
set_selected_permissions(props.member.permissions);
}}
/>
</div>
) : (
<div>
<CustomButton
style="outline"
text="Изменить"
textSize="sm"
icon={<IconPencil size={15} />}
onClick={() => {
set_is_editing(true);
}}
/>
</div>
))}
</div>
<div
style={{
gap: "10px",
display: "flex",
flexDirection: "row",
}}
>
{!is_editing ? (
props.member.permissions.map((perm) => (
<Pill className="TeamPill">{perm}</Pill>
))
) : (
<MultiSelect
value={selected_permissions}
label=""
placeholder="Выберите разрешения"
searchable
data={props.cur_team.creator.permissions}
classNames={{ input: "MantineInput" }}
onChange={(value) => {
set_selected_permissions(value);
}}
/>
)}
</div>
</Stack>
</Card.Section>
<Card.Section
inheritPadding
px="md"
style={{
display: "flex",
flexDirection: "column",
}}
>
<UnstyledButton
onClick={(e) => {
e.stopPropagation();
deleteMember({
team_id: props.cur_team.id,
user_id: props.member.user.keycloak_id,
}).then(() => {
notifications.show({
radius: "md",
title: "Пользователь удален успешно",
message: "",
icon: <IconCheck />,
style: { paddingLeft: "5px" },
});
updateTeam(props.cur_team.id, {
members: [
...props.cur_team.members.filter(
(member) =>
member.user.keycloak_id != props.member.user.keycloak_id,
),
],
});
});
}}
style={{
cursor: "pointer",
width: "20px",
alignSelf: "flex-end",
}}
>
<IconTrash size={20} />
</UnstyledButton>
<Stack gap={"5px"}>
<Text style={{ alignSelf: "flex-end" }}>Добавлен:</Text>
<div
style={{
display: "flex",
flexDirection: "row",
gap: "5px",
}}
>
<Text size="sm" style={{ textAlign: "right" }}>
{new Date(props.member.joined_at).toLocaleDateString("ru")}
</Text>
<Text size="sm" style={{ textAlign: "right" }}>
{new Date(props.member.joined_at).toLocaleTimeString("ru")}
</Text>
</div>
</Stack>
</Card.Section>
</Card>
);
}
export default TeamMemberCard;