Added Team mamagement with backend integration
This commit is contained in:
@@ -15,7 +15,12 @@ function DevicesPage() {
|
||||
/>
|
||||
</Helmet>
|
||||
<div className="DevicesPage">
|
||||
<PaginationContainer numberOfPages={1}>
|
||||
<PaginationContainer
|
||||
numberOfPages={1}
|
||||
isLoading={false}
|
||||
activePage={1}
|
||||
setPage={() => {}}
|
||||
>
|
||||
<Alert title="Подключенные устройства не найдены" color="blue">
|
||||
Для того, чтобы добавить устройство, простмотрите{" "}
|
||||
<Link to={"documentaion"} className="invisible_link">
|
||||
|
||||
@@ -6,25 +6,17 @@ import { useState } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { IconPlus, IconSettings } from "@tabler/icons-react";
|
||||
import { useParams } from "react-router";
|
||||
import {
|
||||
useExperimentStore,
|
||||
useSelectedExperiment,
|
||||
} from "Stores/ExperimentStore";
|
||||
import CustomButton from "Components/CustomButton/CustomButton";
|
||||
import { useExperimentStore } from "Stores/ExperimentStore";
|
||||
|
||||
function ExperimentPage() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { experiment_id } = useParams();
|
||||
const { selectedExperimentId, selectExperiment } = useExperimentStore();
|
||||
const { experiments } = useExperimentStore();
|
||||
|
||||
if (
|
||||
!selectedExperimentId ||
|
||||
(Number(experiment_id) != selectedExperimentId && experiment_id)
|
||||
) {
|
||||
console.log(Number(experiment_id));
|
||||
selectExperiment(Number(experiment_id));
|
||||
}
|
||||
|
||||
const experiment = useSelectedExperiment();
|
||||
const experiment = experiments.find((exp) => {
|
||||
return exp.id == Number(experiment_id);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -74,7 +66,12 @@ function ExperimentPage() {
|
||||
</div>
|
||||
</div>
|
||||
{experiment.tasks_ids.length > 0 && (
|
||||
<PaginationContainer numberOfPages={1}>
|
||||
<PaginationContainer
|
||||
numberOfPages={1}
|
||||
isLoading={false}
|
||||
activePage={1}
|
||||
setPage={() => {}}
|
||||
>
|
||||
{experiment.tasks_ids.map((task: number) => {
|
||||
return <div>{task}</div>;
|
||||
})}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Helmet } from "react-helmet";
|
||||
import "./ExperimentsPage.css";
|
||||
import { PaginationContainer } from "Components/PaginationContainer/PaginationContainer";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { IconMicroscope } from "@tabler/icons-react";
|
||||
import { NewExperimentModal } from "Modals/NewExperiment/NewExperiment";
|
||||
import ExperimentsListCard from "Components/ListCard/ExperimentsListCard";
|
||||
@@ -9,10 +9,29 @@ import { useExperimentStore } from "Stores/ExperimentStore";
|
||||
import type { Experiment } from "Types/Experiment/Experiment";
|
||||
import CustomButton from "Components/CustomButton/CustomButton";
|
||||
import { Alert } from "@mantine/core";
|
||||
import { getShortTeamsList } from "Api/QuantumBackend/TeamManagement";
|
||||
import { useAuthenticationStore } from "Stores/AuthenticationStore";
|
||||
|
||||
function ExperimentsPage() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { experiments, teams } = useExperimentStore();
|
||||
const { experiments } = useExperimentStore();
|
||||
const [teams, setTeams] =
|
||||
useState<{ team_id: number; team_name: string }[]>();
|
||||
|
||||
const { profile, is_loading } = useAuthenticationStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (profile || !is_loading)
|
||||
getShortTeamsList()
|
||||
.then((teams) => {
|
||||
if (teams) {
|
||||
setTeams(teams);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setTeams([]);
|
||||
});
|
||||
}, [profile, is_loading]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -23,7 +42,11 @@ function ExperimentsPage() {
|
||||
content="See the list of all of users experiments"
|
||||
/>
|
||||
</Helmet>
|
||||
<NewExperimentModal isOpened={isOpen} setIsOpened={setIsOpen} />
|
||||
<NewExperimentModal
|
||||
isOpened={isOpen}
|
||||
setIsOpened={setIsOpen}
|
||||
teams={teams}
|
||||
/>
|
||||
<div className="ExperimentsPage">
|
||||
<div
|
||||
style={{
|
||||
@@ -33,7 +56,7 @@ function ExperimentsPage() {
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
{teams.length > 0 && (
|
||||
{teams && teams.length > 0 && (
|
||||
<div className="experimentsButtons">
|
||||
<CustomButton
|
||||
color="contrast"
|
||||
@@ -46,27 +69,40 @@ function ExperimentsPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<PaginationContainer numberOfPages={1}>
|
||||
<PaginationContainer
|
||||
numberOfPages={1}
|
||||
isLoading={teams == undefined}
|
||||
activePage={1}
|
||||
setPage={() => {}}
|
||||
>
|
||||
{experiments.map((exp: Experiment) => {
|
||||
return (
|
||||
<ExperimentsListCard
|
||||
id={exp.id}
|
||||
name={exp.name}
|
||||
description={exp.description}
|
||||
team_id={exp.team_id}
|
||||
tasks_ids={exp.tasks_ids}
|
||||
date_created={exp.date_created}
|
||||
experiment_status={exp.experiment_status}
|
||||
experiment_type="A"
|
||||
experiment={{
|
||||
id: exp.id,
|
||||
name: exp.name,
|
||||
description: exp.description,
|
||||
team_id: exp.team_id,
|
||||
tasks_ids: exp.tasks_ids,
|
||||
date_created: exp.date_created,
|
||||
experiment_status: exp.experiment_status,
|
||||
experiment_type: "A",
|
||||
}}
|
||||
team={teams?.find((team) => team.team_id == exp.team_id)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{teams.length == 0 && (
|
||||
{teams && teams.length == 0 && (
|
||||
<Alert title="Команды не найдены" color="red">
|
||||
Создайте или войдите в команду чтобы начать работу с
|
||||
экспериментами
|
||||
</Alert>
|
||||
)}
|
||||
{teams && teams.length != 0 && experiments.length == 0 && (
|
||||
<Alert title="Экспериментов нету" color="blue">
|
||||
Вы еще не создали не один эксперимент
|
||||
</Alert>
|
||||
)}
|
||||
</PaginationContainer>
|
||||
</div>
|
||||
</>
|
||||
|
||||
11
src/Pages/TeamsPage/TeamPage/TeamPage.css
Normal file
11
src/Pages/TeamsPage/TeamPage/TeamPage.css
Normal file
@@ -0,0 +1,11 @@
|
||||
.TeamPage {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.TeamData {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
228
src/Pages/TeamsPage/TeamPage/TeamPage.tsx
Normal file
228
src/Pages/TeamsPage/TeamPage/TeamPage.tsx
Normal file
@@ -0,0 +1,228 @@
|
||||
import {
|
||||
Alert,
|
||||
Avatar,
|
||||
Card,
|
||||
Center,
|
||||
Grid,
|
||||
LoadingOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
TextInput,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import "./TeamPage.css";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { IconCheck, IconPlus } from "@tabler/icons-react";
|
||||
import { useParams } from "react-router";
|
||||
import CustomButton from "Components/CustomButton/CustomButton";
|
||||
import { useTeamStore } from "Stores/TeamsStore";
|
||||
import { getTeam, updateTeamRequest } from "Api/QuantumBackend/TeamManagement";
|
||||
import type { Team, TeamMember } from "Types/Team/Team";
|
||||
import { useAuthenticationStore } from "Stores/AuthenticationStore";
|
||||
import TeamMemberCard from "Components/ListCard/TeamListCard/TeamMemberCard/TeamMemberCard";
|
||||
import { AddMember } from "Modals/AddMember/AddMember";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
|
||||
function TeamPage() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { team_id } = useParams();
|
||||
const { addTeam, teams, updateTeam } = useTeamStore();
|
||||
const { profile } = useAuthenticationStore();
|
||||
const [is_loading, set_is_loading] = useState(true);
|
||||
|
||||
const [cur_team, set_cur_team] = useState<Team>();
|
||||
|
||||
const [team_name, set_team_name] = useState(cur_team?.name);
|
||||
const [team_descr, set_team_descr] = useState(cur_team?.description);
|
||||
|
||||
useEffect(() => {
|
||||
if (!cur_team && profile) {
|
||||
getTeam(Number(team_id))
|
||||
.then((team) => {
|
||||
if (team) {
|
||||
addTeam(team);
|
||||
set_cur_team(team);
|
||||
set_team_name(team.name);
|
||||
set_team_descr(team.description);
|
||||
set_is_loading(false);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
set_is_loading(false);
|
||||
});
|
||||
} else {
|
||||
if (profile) {
|
||||
set_is_loading(false);
|
||||
}
|
||||
}
|
||||
if (teams.find((e) => e.id === Number(team_id)) != cur_team) {
|
||||
set_cur_team(teams.find((e) => e.id === Number(team_id)));
|
||||
}
|
||||
}, [cur_team, profile, teams]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{cur_team ? "Team " + cur_team.id + " | QMolSim" : "Error | QmolSim"}
|
||||
</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="See the information on the currently selected team"
|
||||
/>
|
||||
</Helmet>
|
||||
<AddMember
|
||||
isOpened={isOpen}
|
||||
setIsOpened={setIsOpen}
|
||||
permissions={cur_team?.creator.permissions}
|
||||
team={cur_team}
|
||||
/>
|
||||
<div className="TeamPage">
|
||||
<LoadingOverlay
|
||||
visible={is_loading}
|
||||
zIndex={1000}
|
||||
overlayProps={{ radius: "sm", blur: 2 }}
|
||||
loaderProps={{ size: 50, type: "dots" }}
|
||||
/>
|
||||
|
||||
<Card className="TeamData">
|
||||
<Grid>
|
||||
<Grid.Col span={3}>
|
||||
<Title size={"xl"}>Имя команды:</Title>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={9}>
|
||||
<TextInput
|
||||
size="md"
|
||||
value={team_name}
|
||||
onChange={(val) => {
|
||||
set_team_name(val.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={3}>
|
||||
<Title size={"xl"}>Описание:</Title>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={9}>
|
||||
<Textarea
|
||||
size="md"
|
||||
value={team_descr ? team_descr : ""}
|
||||
onChange={(val) => {
|
||||
set_team_descr(val.target.value);
|
||||
}}
|
||||
minRows={2}
|
||||
maxRows={4}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={3}>
|
||||
<Title size={"xl"}>Создатель:</Title>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={9}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
radius="xl"
|
||||
src={cur_team?.creator.user.profile_picture_path}
|
||||
/>
|
||||
<Text size="md" style={{ textDecoration: "underline" }}>
|
||||
{cur_team?.creator.user.username} (
|
||||
{cur_team?.creator.user.email})
|
||||
</Text>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<CustomButton
|
||||
color="accent"
|
||||
text="Сохранить"
|
||||
disabled={
|
||||
cur_team?.name == team_name &&
|
||||
cur_team?.description == team_descr
|
||||
}
|
||||
onClick={() => {
|
||||
if (cur_team && team_name)
|
||||
updateTeamRequest({
|
||||
team_id: cur_team.id,
|
||||
name: team_name,
|
||||
description: team_descr,
|
||||
}).then((team) => {
|
||||
updateTeam(team.team_id, {
|
||||
name: team.name,
|
||||
description: team.description,
|
||||
});
|
||||
notifications.show({
|
||||
radius: "md",
|
||||
title: "Информация команды обновлена успешно",
|
||||
message: "",
|
||||
icon: <IconCheck />,
|
||||
style: { paddingLeft: "5px" },
|
||||
});
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<CustomButton
|
||||
color="error"
|
||||
text="Отменить"
|
||||
disabled={
|
||||
cur_team?.name == team_name &&
|
||||
cur_team?.description == team_descr
|
||||
}
|
||||
onClick={() => {
|
||||
set_team_name(cur_team?.name);
|
||||
set_team_descr(cur_team?.description);
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Card>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexWrap: "nowrap",
|
||||
gap: "20px",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Title order={2} style={{ textWrap: "nowrap" }}>
|
||||
Члены команды:{" "}
|
||||
</Title>
|
||||
<div>
|
||||
<CustomButton
|
||||
color="contrast"
|
||||
onClick={() => {
|
||||
setIsOpen(true);
|
||||
}}
|
||||
icon={<IconPlus />}
|
||||
text="Добавить члена"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{cur_team && cur_team.members.length > 0 && (
|
||||
<div className="TeamMembers">
|
||||
{cur_team.members.map((member: TeamMember) => {
|
||||
return <TeamMemberCard cur_team={cur_team} member={member} />;
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{!cur_team && !is_loading && (
|
||||
<Alert color="red">
|
||||
<Center>
|
||||
{" "}
|
||||
<Text c="contrast" size={"xl"}>
|
||||
Ошибка. Команда не найдена
|
||||
</Text>{" "}
|
||||
</Center>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default TeamPage;
|
||||
@@ -3,14 +3,79 @@ import "./TeamsPage.css";
|
||||
import { PaginationContainer } from "Components/PaginationContainer/PaginationContainer";
|
||||
import CustomButton from "Components/CustomButton/CustomButton";
|
||||
import { IconUsersGroup } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Alert } from "@mantine/core";
|
||||
import { NewTeamModal } from "Modals/NewTeam/NewTeam";
|
||||
import { useTeamStore } from "Stores/TeamsStore";
|
||||
import { getTeams } from "Api/QuantumBackend/TeamManagement";
|
||||
import { useAuthenticationStore as useAuthenticationStore } from "Stores/AuthenticationStore";
|
||||
import TeamsListCard from "Components/ListCard/TeamListCard/TeamsListCard";
|
||||
|
||||
function TeamsPage() {
|
||||
const [, setIsOpen] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { teams, setTeams, setTotalTeams, totalTeams, removeTeam } =
|
||||
useTeamStore();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const { profile } = useAuthenticationStore();
|
||||
const [cur_page, set_cur_page] = useState<number>(1);
|
||||
const [page_size, set_page_size] = useState<number>(256);
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
if (profile) {
|
||||
getTeams({ page_num: cur_page })
|
||||
.then((teams) => {
|
||||
setTeams(teams.teams);
|
||||
set_cur_page(teams.cur_page);
|
||||
setTotalTeams(teams.total_teams);
|
||||
set_page_size(teams.page_size);
|
||||
setIsLoading(false);
|
||||
})
|
||||
.catch(() => setIsLoading(false));
|
||||
}
|
||||
}, [profile]);
|
||||
|
||||
useEffect(() => {
|
||||
if (profile) {
|
||||
if (teams && teams.length > page_size) {
|
||||
removeTeam(teams[teams.length - 1].id);
|
||||
}
|
||||
if (teams && cur_page > Math.ceil(totalTeams / page_size)) {
|
||||
setIsLoading(true);
|
||||
getTeams({ page_num: Math.max(1, cur_page - 1) })
|
||||
.then((teams) => {
|
||||
setTeams(teams.teams);
|
||||
set_cur_page(teams.cur_page);
|
||||
setTotalTeams(teams.total_teams);
|
||||
set_page_size(teams.page_size);
|
||||
setIsLoading(false);
|
||||
scroll({ top: 0 });
|
||||
})
|
||||
.catch(() => setIsLoading(false));
|
||||
set_cur_page(Math.max(1, cur_page - 1));
|
||||
}
|
||||
if (
|
||||
teams &&
|
||||
teams.length < Math.min(page_size, totalTeams) &&
|
||||
cur_page != Math.ceil(totalTeams / page_size)
|
||||
) {
|
||||
getTeams({ page_num: cur_page })
|
||||
.then((teams) => {
|
||||
setTeams(teams.teams);
|
||||
set_cur_page(teams.cur_page);
|
||||
setTotalTeams(teams.total_teams);
|
||||
set_page_size(teams.page_size);
|
||||
setIsLoading(false);
|
||||
scroll({ top: 0 });
|
||||
})
|
||||
.catch(() => setIsLoading(false));
|
||||
}
|
||||
}
|
||||
}, [profile, teams]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<NewTeamModal isOpened={isOpen} setIsOpened={setIsOpen} />
|
||||
<Helmet>
|
||||
<title>Teams Page | QMolSim</title>
|
||||
<meta
|
||||
@@ -26,13 +91,35 @@ function TeamsPage() {
|
||||
setIsOpen(true);
|
||||
}}
|
||||
icon={<IconUsersGroup />}
|
||||
text="Создать эксперимент"
|
||||
text="Создать Команду"
|
||||
/>
|
||||
</div>
|
||||
<PaginationContainer numberOfPages={1}>
|
||||
<Alert title="Команды не найдены" color="blue">
|
||||
Создайте или войдите в команду чтобы начать работу с экспериментами
|
||||
</Alert>
|
||||
<PaginationContainer
|
||||
numberOfPages={Math.ceil(totalTeams / page_size)}
|
||||
isLoading={isLoading}
|
||||
activePage={cur_page}
|
||||
setPage={(page) => {
|
||||
getTeams({ page_num: page })
|
||||
.then((teams) => {
|
||||
setTeams(teams.teams);
|
||||
set_cur_page(teams.cur_page);
|
||||
setTotalTeams(teams.total_teams);
|
||||
set_page_size(teams.page_size);
|
||||
setIsLoading(false);
|
||||
scroll({ top: 0 });
|
||||
})
|
||||
.catch(() => setIsLoading(false));
|
||||
}}
|
||||
>
|
||||
{teams.length == 0 && !isLoading && (
|
||||
<Alert title="Команды не найдены" color="blue">
|
||||
Создайте или войдите в команду чтобы начать работу с
|
||||
экспериментами
|
||||
</Alert>
|
||||
)}
|
||||
{teams.map((team) => {
|
||||
return <TeamsListCard {...team} />;
|
||||
})}
|
||||
</PaginationContainer>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -23,7 +23,7 @@ import keycloak, {
|
||||
} from "Api/Keycloak/Keycloak";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { AuthenticationStore } from "Stores/AuthenticationStore";
|
||||
import { useAuthenticationStore } from "Stores/AuthenticationStore";
|
||||
import "./UserPage.css";
|
||||
import { useUserPreferencesStore } from "Stores/PreferencesStore";
|
||||
import CustomButton from "Components/CustomButton/CustomButton";
|
||||
@@ -35,7 +35,8 @@ import {
|
||||
} from "Api/QuantumBackend/UserManagement";
|
||||
import type { UserData } from "Types/User/User";
|
||||
function UserPage() {
|
||||
const { profile, is_loading, profile_picture_path } = AuthenticationStore();
|
||||
const { profile, is_loading, profile_picture_path } =
|
||||
useAuthenticationStore();
|
||||
const { theme, set_theme } = useUserPreferencesStore();
|
||||
const [username, set_username] = useState<string>("");
|
||||
const [email, set_email] = useState<string>("");
|
||||
@@ -49,15 +50,13 @@ function UserPage() {
|
||||
((profile.username && profile.username != username) ||
|
||||
(profile.email && profile.email != email))
|
||||
) {
|
||||
console.log("A");
|
||||
const prof_1 = profile;
|
||||
prof_1.email = email;
|
||||
prof_1.username = username;
|
||||
AuthenticationStore.setState({
|
||||
useAuthenticationStore.setState({
|
||||
is_loading: true,
|
||||
});
|
||||
updateUserData(prof_1).then((data) => {
|
||||
console.log(data);
|
||||
if (data) {
|
||||
notifications.show({
|
||||
radius: "md",
|
||||
@@ -67,7 +66,7 @@ function UserPage() {
|
||||
style: { paddingLeft: "5px" },
|
||||
});
|
||||
keycloak.loadUserProfile().then((profile) => {
|
||||
AuthenticationStore.setState({
|
||||
useAuthenticationStore.setState({
|
||||
is_loading: false,
|
||||
profile: profile,
|
||||
});
|
||||
@@ -81,7 +80,7 @@ function UserPage() {
|
||||
UpdateCurrentUserInfo(pfp_path).then(() => {
|
||||
GetCurrentUserInfo().then((info: UserData | undefined) => {
|
||||
if (info && info.profile_picture_path)
|
||||
AuthenticationStore.setState({
|
||||
useAuthenticationStore.setState({
|
||||
profile_picture_path: info.profile_picture_path,
|
||||
});
|
||||
});
|
||||
@@ -184,6 +183,12 @@ function UserPage() {
|
||||
<CustomButton
|
||||
color="error"
|
||||
text="Отменить"
|
||||
onClick={() => {
|
||||
if (profile && profile.email && profile.username) {
|
||||
set_username(profile.username);
|
||||
set_email(profile.email);
|
||||
}
|
||||
}}
|
||||
disabled={
|
||||
!(
|
||||
profile != null &&
|
||||
|
||||
Reference in New Issue
Block a user