Merge branch 'frontend' into gruppDM

This commit is contained in:
Davenludd 2024-04-02 11:49:46 +02:00
commit 7df1654bdc
42 changed files with 942 additions and 483 deletions

View file

@ -1,5 +1,10 @@
import { APIResponse, api } from "../API/API";
import { NewProjMember } from "../Types/goTypes";
export interface NewProjMember {
username: string;
role: string;
projectname: string;
}
/**
* Tries to add a member to a project
@ -21,7 +26,7 @@ function AddMember(props: { memberToAdd: NewProjMember }): boolean {
props.memberToAdd,
localStorage.getItem("accessToken") ?? "",
)
.then((response: APIResponse<NewProjMember>) => {
.then((response: APIResponse<void>) => {
if (response.success) {
alert("Member added");
added = true;

View file

@ -1,6 +1,6 @@
import { useState } from "react";
import { APIResponse, api } from "../API/API";
import { NewProject, Project } from "../Types/goTypes";
import { NewProject } from "../Types/goTypes";
import InputField from "./InputField";
import Logo from "../assets/Logo.svg";
import Button from "./Button";
@ -10,27 +10,26 @@ import Button from "./Button";
* @param {Object} props - Project name and description
* @returns {boolean} True if created, false if not
*/
function CreateProject(props: { name: string; description: string }): boolean {
function CreateProject(props: { name: string; description: string }): void {
const project: NewProject = {
name: props.name,
description: props.description,
};
let created = false;
api
.createProject(project, localStorage.getItem("accessToken") ?? "")
.then((response: APIResponse<Project>) => {
.then((response: APIResponse<void>) => {
if (response.success) {
created = true;
alert("Project added!");
} else {
alert("Project NOT added!");
console.error(response.message);
}
})
.catch((error) => {
alert("Project NOT added!");
console.error("An error occurred during creation:", error);
});
return created;
}
/**
@ -48,7 +47,10 @@ function AddProject(): JSX.Element {
className="bg-white rounded px-8 pt-6 pb-8 mb-4 items-center justify-center flex flex-col w-fit h-fit"
onSubmit={(e) => {
e.preventDefault();
CreateProject({ name: name, description: description });
CreateProject({
name: name,
description: description,
});
}}
>
<img
@ -64,6 +66,7 @@ function AddProject(): JSX.Element {
type="text"
value={name}
onChange={(e) => {
e.preventDefault();
setName(e.target.value);
}}
/>
@ -72,6 +75,7 @@ function AddProject(): JSX.Element {
type="text"
value={description}
onChange={(e) => {
e.preventDefault();
setDescription(e.target.value);
}}
/>

View file

@ -1,8 +1,7 @@
import { useState } from "react";
import { NewProjMember } from "../Types/goTypes";
import Button from "./Button";
import GetAllUsers from "./GetAllUsers";
import AddMember from "./AddMember";
import AddMember, { NewProjMember } from "./AddMember";
import BackButton from "./BackButton";
/**

View file

@ -0,0 +1,37 @@
import { APIResponse, api } from "../API/API";
export interface ProjectRoleChange {
username: string;
role: "project_manager" | "member" | "";
projectname: string;
}
export default function ChangeRole(roleChangeInfo: ProjectRoleChange): void {
if (
roleChangeInfo.username === "" ||
roleChangeInfo.role === "" ||
roleChangeInfo.projectname === ""
) {
// FOR DEBUG
// console.log(roleChangeInfo.role + ": Role");
// console.log(roleChangeInfo.projectname + ": P-Name");
// console.log(roleChangeInfo.username + ": U-name");
alert("You have to select a role");
return;
}
api
.changeUserRole(roleChangeInfo, localStorage.getItem("accessToken") ?? "")
.then((response: APIResponse<void>) => {
if (response.success) {
alert("Role changed successfully");
location.reload();
} else {
alert(response.message);
console.error(response.message);
}
})
.catch((error) => {
alert(error);
console.error("An error occurred during change:", error);
});
}

View file

@ -0,0 +1,77 @@
import { useState } from "react";
import Button from "./Button";
import ChangeRole, { ProjectRoleChange } from "./ChangeRole";
export default function ChangeRoles(props: {
projectName: string;
username: string;
}): JSX.Element {
const [selectedRole, setSelectedRole] = useState<
"project_manager" | "member" | ""
>("");
const handleRoleChange = (
event: React.ChangeEvent<HTMLInputElement>,
): void => {
if (event.target.value === "member") {
setSelectedRole(event.target.value);
} else if (event.target.value === "project_manager") {
setSelectedRole(event.target.value);
}
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
event.preventDefault();
const roleChangeInfo: ProjectRoleChange = {
username: props.username,
projectname: props.projectName,
role: selectedRole,
};
ChangeRole(roleChangeInfo);
};
return (
<div>
<h1 className="font-bold text-[22px] mb-[15px]">Change role:</h1>
<form
className="border-2 border-black rounded-lg p-2"
onSubmit={handleSubmit}
>
<div className="self-start text-left font-medium">
<div className="hover:font-bold">
<label>
<input
type="radio"
value="project_manager"
checked={selectedRole === "project_manager"}
onChange={handleRoleChange}
className="ml-2 mr-2 mb-5"
/>
Project manager
</label>
</div>
<div className="hover:font-bold">
<label>
<input
type="radio"
value="member"
checked={selectedRole === "member"}
onChange={handleRoleChange}
className="ml-2 mr-2"
/>
Member
</label>
</div>
</div>
<Button
text="Change"
onClick={(): void => {
return;
}}
type="submit"
/>
</form>
</div>
);
}

View file

@ -1,61 +1,26 @@
import React, { useState } from "react";
import InputField from "./InputField";
import { api } from "../API/API";
function ChangeUsername(): JSX.Element {
const [newUsername, setNewUsername] = useState("");
const [errorMessage, setErrorMessage] = useState("");
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
setNewUsername(e.target.value);
};
const handleSubmit = async (): Promise<void> => {
try {
// Call the API function to change the username
const token = localStorage.getItem("accessToken");
if (!token) {
throw new Error("Access token not found");
}
const response = await api.changeUserName(
{ prevName: "currentName", newName: newUsername },
token,
);
import { APIResponse, api } from "../API/API";
import { StrNameChange } from "../Types/goTypes";
function ChangeUsername(props: { nameChange: StrNameChange }): void {
if (props.nameChange.newName === "") {
alert("You have to select a new name");
return;
}
api
.changeUserName(props.nameChange, localStorage.getItem("accessToken") ?? "")
.then((response: APIResponse<void>) => {
if (response.success) {
// Optionally, add a success message or redirect the user
console.log("Username changed successfully");
alert("Name changed successfully");
location.reload();
} else {
// Handle the error message
console.error("Failed to change username:", response.message);
setErrorMessage(response.message ?? "Failed to change username");
alert("Name not changed");
console.error(response.message);
}
} catch (error) {
console.error("Error changing username:", error);
// Optionally, handle the error
setErrorMessage("Failed to change username");
}
};
const handleButtonClick = (): void => {
handleSubmit().catch((error) => {
console.error("Error in handleSubmit:", error);
})
.catch((error) => {
alert("Name not changed");
console.error("An error occurred during change:", error);
});
};
return (
<div>
<InputField
label="New Username"
type="text"
value={newUsername}
onChange={handleChange}
/>
{errorMessage && <div>{errorMessage}</div>}
<button onClick={handleButtonClick}>Update Username</button>
</div>
);
}
export default ChangeUsername;

View file

@ -1,6 +1,7 @@
import { useState, useEffect } from "react";
import { useState } from "react";
import { Project } from "../Types/goTypes";
import { useNavigate } from "react-router-dom";
import GetProjects from "./GetProjects";
import { api } from "../API/API";
/**
@ -11,16 +12,10 @@ function DisplayUserProject(): JSX.Element {
const [projects, setProjects] = useState<Project[]>([]);
const navigate = useNavigate();
const getProjects = async (): Promise<void> => {
const token = localStorage.getItem("accessToken") ?? "";
const response = await api.getUserProjects(token);
console.log(response);
if (response.success) {
setProjects(response.data ?? []);
} else {
console.error(response.message);
}
};
GetProjects({
setProjectsProp: setProjects,
username: localStorage.getItem("username") ?? "",
});
const handleProjectClick = async (projectName: string): Promise<void> => {
const token = localStorage.getItem("accessToken") ?? "";
@ -41,11 +36,6 @@ function DisplayUserProject(): JSX.Element {
}
};
// Call getProjects when the component mounts
useEffect(() => {
void getProjects();
}, []);
return (
<>
<h1 className="font-bold text-[30px] mb-[20px]">Your Projects</h1>

View file

@ -12,6 +12,7 @@ import { api } from "../API/API";
*/
function GetProjects(props: {
setProjectsProp: Dispatch<React.SetStateAction<Project[]>>;
username: string;
}): void {
const setProjects: Dispatch<React.SetStateAction<Project[]>> =
props.setProjectsProp;
@ -19,7 +20,7 @@ function GetProjects(props: {
const fetchUsers = async (): Promise<void> => {
try {
const token = localStorage.getItem("accessToken") ?? "";
const response = await api.getUserProjects(token);
const response = await api.getUserProjects(props.username, token);
if (response.success) {
setProjects(response.data ?? []);
} else {
@ -31,7 +32,7 @@ function GetProjects(props: {
};
void fetchUsers();
}, [setProjects]);
}, [props.username, setProjects]);
}
export default GetProjects;

View file

@ -1,7 +1,11 @@
import { Dispatch, useEffect } from "react";
import { UserProjectMember } from "../Types/goTypes";
import { api } from "../API/API";
export interface ProjectMember {
Username: string;
UserRole: string;
}
/**
* Gets all projects that user is a member of
* @param props - A setStateAction for the array you want to put projects in
@ -12,9 +16,9 @@ import { api } from "../API/API";
*/
function GetUsersInProject(props: {
projectName: string;
setUsersProp: Dispatch<React.SetStateAction<UserProjectMember[]>>;
setUsersProp: Dispatch<React.SetStateAction<ProjectMember[]>>;
}): void {
const setUsers: Dispatch<React.SetStateAction<UserProjectMember[]>> =
const setUsers: Dispatch<React.SetStateAction<ProjectMember[]>> =
props.setUsersProp;
useEffect(() => {
const fetchUsers = async (): Promise<void> => {

View file

@ -0,0 +1,77 @@
import Button from "./Button";
import DeleteUser from "./DeleteUser";
import UserProjectListAdmin from "./UserProjectListAdmin";
import { useState } from "react";
import ChangeRoleView from "./ChangeRoleView";
function MemberInfoModal(props: {
isVisible: boolean;
username: string;
onClose: () => void;
}): JSX.Element {
const [showRoles, setShowRoles] = useState(false);
if (!props.isVisible) return <></>;
const handleChangeRole = (): void => {
if (showRoles) {
setShowRoles(false);
} else {
setShowRoles(true);
}
};
return (
<div
className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm
flex justify-center items-center"
>
<div className="border-4 border-black bg-white p-2 rounded-lg text-center flex flex-col">
<p className="font-bold text-[30px]">{props.username}</p>
<p
className="mb-[20px] hover:font-bold hover:cursor-pointer underline"
onClick={handleChangeRole}
>
(Change Role)
</p>
{showRoles && (
<ChangeRoleView
projectName={localStorage.getItem("projectName") ?? ""}
username={props.username}
/>
)}
<div>
<h2 className="font-bold text-[22px] mb-[20px]">
Member of these projects:
</h2>
<div className="pr-6 pl-6">
<UserProjectListAdmin username={props.username} />
</div>
</div>
<div className="items-center space-x-6 pr-6 pl-6">
<Button
text={"Delete"}
onClick={function (): void {
if (
window.confirm("Are you sure you want to delete this user?")
) {
DeleteUser({
usernameToDelete: props.username,
});
}
}}
type="button"
/>
<Button
text={"Close"}
onClick={function (): void {
setShowRoles(false);
props.onClose();
}}
type="button"
/>
</div>
</div>
</div>
);
}
export default MemberInfoModal;

View file

@ -1,7 +1,6 @@
import { useState } from "react";
import Button from "./Button";
import { UserProjectMember } from "../Types/goTypes";
import GetUsersInProject from "./GetUsersInProject";
import GetUsersInProject, { ProjectMember } from "./GetUsersInProject";
import { Link } from "react-router-dom";
function ProjectInfoModal(props: {
@ -10,7 +9,7 @@ function ProjectInfoModal(props: {
onClose: () => void;
onClick: (username: string) => void;
}): JSX.Element {
const [users, setUsers] = useState<UserProjectMember[]>([]);
const [users, setUsers] = useState<ProjectMember[]>([]);
GetUsersInProject({ projectName: props.projectname, setUsersProp: setUsers });
if (!props.isVisible) return <></>;

View file

@ -1,7 +1,7 @@
import { useState } from "react";
import { NewProject } from "../Types/goTypes";
import ProjectInfoModal from "./ProjectInfoModal";
import UserInfoModal from "./UserInfoModal";
import MemberInfoModal from "./MemberInfoModal";
/**
* A list of projects for admin manage projects page, that sets an onClick
@ -51,13 +51,8 @@ export function ProjectListAdmin(props: {
isVisible={projectModalVisible}
projectname={projectname}
/>
<UserInfoModal
manageMember={true}
<MemberInfoModal
onClose={handleCloseUser}
//TODO: CHANGE TO REMOVE USER FROM PROJECT
onDelete={() => {
return;
}}
isVisible={userModalVisible}
username={username}
/>

View file

@ -1,31 +1,15 @@
import { useEffect, useState } from "react";
import { useState } from "react";
import { Link, useParams } from "react-router-dom";
import { api } from "../API/API";
import { UserProjectMember } from "../Types/goTypes";
import GetUsersInProject, { ProjectMember } from "./GetUsersInProject";
function ProjectMembers(): JSX.Element {
const { projectName } = useParams();
const [projectMembers, setProjectMembers] = useState<UserProjectMember[]>([]);
const [projectMembers, setProjectMembers] = useState<ProjectMember[]>([]);
useEffect(() => {
const getProjectMembers = async (): Promise<void> => {
const token = localStorage.getItem("accessToken") ?? "";
const response = await api.getAllUsersProject(projectName ?? "", token);
console.log(response);
if (response.success) {
setProjectMembers(response.data ?? []);
} else {
console.error(response.message);
}
};
void getProjectMembers();
}, [projectName]);
interface ProjectMember {
Username: string;
UserRole: string;
}
GetUsersInProject({
projectName: projectName ?? "",
setUsersProp: setProjectMembers,
});
return (
<>

View file

@ -22,6 +22,8 @@ export default function Register(): JSX.Element {
const response = await api.registerUser(newUser);
if (response.success) {
alert("User added!");
setPassword("");
setUsername("");
} else {
alert("User not added");
setErrMessage(response.message ?? "Unknown error");

View file

@ -1,34 +1,38 @@
import { Link } from "react-router-dom";
import Button from "./Button";
import DeleteUser from "./DeleteUser";
import UserProjectListAdmin from "./UserProjectListAdmin";
import { useState } from "react";
import InputField from "./InputField";
import ChangeUsername from "./ChangeUsername";
import { StrNameChange } from "../Types/goTypes";
function UserInfoModal(props: {
isVisible: boolean;
manageMember: boolean;
username: string;
onClose: () => void;
onDelete: (username: string) => void;
}): JSX.Element {
if (!props.isVisible) return <></>;
const ManageUserOrMember = (check: boolean): JSX.Element => {
if (check) {
return (
<Link to="/AdminChangeRole">
<p className="mb-[20px] hover:font-bold hover:cursor-pointer underline">
(Change Role)
</p>
</Link>
);
const [showInput, setShowInput] = useState(false);
const [newUsername, setNewUsername] = useState("");
if (!props.isVisible) {
return <></>;
}
const handleChangeNameView = (): void => {
if (showInput) {
setShowInput(false);
} else {
setShowInput(true);
}
return (
<Link to="/AdminChangeUserName">
<p className="mb-[20px] hover:font-bold hover:cursor-pointer underline">
(Change Username)
</p>
</Link>
);
};
const handleClickChangeName = (): void => {
const nameChange: StrNameChange = {
prevName: props.username,
newName: newUsername,
};
ChangeUsername({ nameChange: nameChange });
};
return (
<div
className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm
@ -36,13 +40,38 @@ function UserInfoModal(props: {
>
<div className="border-4 border-black bg-white p-2 rounded-lg text-center flex flex-col">
<p className="font-bold text-[30px]">{props.username}</p>
{ManageUserOrMember(props.manageMember)}
<p
className="mb-[20px] hover:font-bold hover:cursor-pointer underline"
onClick={handleChangeNameView}
>
(Change Username)
</p>
{showInput && (
<div className="border-2 border-black rounded-lg p-2">
<InputField
label={"New username"}
type={"text"}
value={newUsername}
onChange={function (e): void {
e.defaultPrevented;
setNewUsername(e.target.value);
}}
/>
<Button
text={"Change"}
onClick={function (): void {
handleClickChangeName();
}}
type={"submit"}
/>
</div>
)}
<div>
<h2 className="font-bold text-[22px] mb-[20px]">
Member of these projects:
</h2>
<div className="pr-6 pl-6">
<UserProjectListAdmin />
<UserProjectListAdmin username={props.username} />
</div>
</div>
<div className="items-center space-x-6 pr-6 pl-6">
@ -62,6 +91,8 @@ function UserInfoModal(props: {
<Button
text={"Close"}
onClick={function (): void {
setNewUsername("");
setShowInput(false);
props.onClose();
}}
type="button"

View file

@ -1,6 +1,5 @@
import { useState } from "react";
import UserInfoModal from "./UserInfoModal";
import DeleteUser from "./DeleteUser";
/**
* A list of users for admin manage users page, that sets an onClick
@ -30,9 +29,7 @@ export function UserListAdmin(props: { users: string[] }): JSX.Element {
return (
<>
<UserInfoModal
manageMember={false}
onClose={handleClose}
onDelete={() => DeleteUser}
isVisible={modalVisible}
username={username}
/>

View file

@ -1,29 +1,11 @@
import { useEffect, useState } from "react";
import { api } from "../API/API";
import { useState } from "react";
import { Project } from "../Types/goTypes";
import GetProjects from "./GetProjects";
function UserProjectListAdmin(): JSX.Element {
function UserProjectListAdmin(props: { username: string }): JSX.Element {
const [projects, setProjects] = useState<Project[]>([]);
useEffect(() => {
const fetchProjects = async (): Promise<void> => {
try {
const token = localStorage.getItem("accessToken") ?? "";
// const username = props.username;
const response = await api.getUserProjects(token);
if (response.success) {
setProjects(response.data ?? []);
} else {
console.error("Failed to fetch projects:", response.message);
}
} catch (error) {
console.error("Error fetching projects:", error);
}
};
void fetchProjects();
}, []);
GetProjects({ setProjectsProp: setProjects, username: props.username });
return (
<div className="border-2 border-black bg-white p-2 rounded-lg text-center">