Merge branch 'frontend' into gruppDM
This commit is contained in:
		
						commit
						544383809b
					
				
					 14 changed files with 346 additions and 138 deletions
				
			
		|  | @ -17,6 +17,7 @@ type Database interface { | |||
| 	AddUser(username string, password string) error | ||||
| 	CheckUser(username string, password string) bool | ||||
| 	RemoveUser(username string) error | ||||
| 	RemoveUserFromProject(username string, projectname string) error | ||||
| 	PromoteToAdmin(username string) error | ||||
| 	GetUserId(username string) (int, error) | ||||
| 	AddProject(name string, description string, username string) error | ||||
|  | @ -43,6 +44,7 @@ type Database interface { | |||
| 	GetProjectTimes(projectName string) (map[string]int, error) | ||||
| 	UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error | ||||
| 	RemoveProject(projectname string) error | ||||
| 	GetUserName(id int) (string, error) | ||||
| } | ||||
| 
 | ||||
| // This struct is a wrapper type that holds the database connection | ||||
|  | @ -86,6 +88,10 @@ const isProjectManagerQuery = `SELECT COUNT(*) > 0 FROM user_roles | |||
| 								JOIN projects ON user_roles.project_id = projects.id | ||||
| 								WHERE users.username = ? AND projects.name = ? AND user_roles.p_role = 'project_manager'` | ||||
| 
 | ||||
| const removeUserFromProjectQuery = `DELETE FROM user_roles  | ||||
| 									WHERE user_id = (SELECT id FROM users WHERE username = ?)  | ||||
| 									AND project_id = (SELECT id FROM projects WHERE name = ?)` | ||||
| 
 | ||||
| // DbConnect connects to the database | ||||
| func DbConnect(dbpath string) Database { | ||||
| 	// Open the database | ||||
|  | @ -147,6 +153,11 @@ func (d *Db) AddUserToProject(username string, projectname string, role string) | |||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (d *Db) RemoveUserFromProject(username string, projectname string) error { | ||||
| 	_, err := d.Exec(removeUserFromProjectQuery, username, projectname) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // ChangeUserRole changes the role of a user within a project. | ||||
| func (d *Db) ChangeUserRole(username string, projectname string, role string) error { | ||||
| 	// Execute the SQL query to change the user's role | ||||
|  | @ -601,3 +612,9 @@ func (d *Db) RemoveProject(projectname string) error { | |||
| 	_, err := d.Exec("DELETE FROM projects WHERE name = ?", projectname) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (d *Db) GetUserName(id int) (string, error) { | ||||
| 	var username string | ||||
| 	err := d.Get(&username, "SELECT username FROM users WHERE id = ?", id) | ||||
| 	return username, err | ||||
| } | ||||
|  |  | |||
							
								
								
									
										40
									
								
								backend/internal/handlers/projects/RemoveUserFromProject.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								backend/internal/handlers/projects/RemoveUserFromProject.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | |||
| package projects | ||||
| 
 | ||||
| import ( | ||||
| 	db "ttime/internal/database" | ||||
| 
 | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/gofiber/fiber/v2/log" | ||||
| 	"github.com/golang-jwt/jwt/v5" | ||||
| ) | ||||
| 
 | ||||
| func RemoveUserFromProject(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("user").(*jwt.Token) | ||||
| 	claims := user.Claims.(jwt.MapClaims) | ||||
| 	pm_name := claims["name"].(string) | ||||
| 
 | ||||
| 	project := c.Params("projectName") | ||||
| 	username := c.Query("userName") | ||||
| 
 | ||||
| 	// Check if the user is a project manager | ||||
| 	isPM, err := db.GetDb(c).IsProjectManager(pm_name, project) | ||||
| 	if err != nil { | ||||
| 		log.Info("Error checking if user is project manager:", err) | ||||
| 		return c.Status(500).SendString(err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	if !isPM { | ||||
| 		log.Info("User: ", pm_name, " is not a project manager in project: ", project) | ||||
| 		return c.Status(403).SendString("User is not a project manager") | ||||
| 	} | ||||
| 
 | ||||
| 	// Remove the user from the project | ||||
| 	if err = db.GetDb(c).RemoveUserFromProject(username, project); err != nil { | ||||
| 		log.Info("Error removing user from project:", err) | ||||
| 		return c.Status(500).SendString(err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	// Return success message | ||||
| 	log.Info("User : ", username, " removed from project: ", project) | ||||
| 	return c.SendStatus(fiber.StatusOK) | ||||
| } | ||||
							
								
								
									
										32
									
								
								backend/internal/handlers/users/GetUserName.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								backend/internal/handlers/users/GetUserName.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| package users | ||||
| 
 | ||||
| import ( | ||||
| 	"strconv" | ||||
| 	db "ttime/internal/database" | ||||
| 
 | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
| 
 | ||||
| // Return the username of a user given their user id | ||||
| func GetUserName(c *fiber.Ctx) error { | ||||
| 	// Check the query params for userId | ||||
| 	user_id_string := c.Query("userId") | ||||
| 	if user_id_string == "" { | ||||
| 		return c.Status(400).SendString("Missing user id") | ||||
| 	} | ||||
| 
 | ||||
| 	// Convert to int | ||||
| 	user_id, err := strconv.Atoi(user_id_string) | ||||
| 	if err != nil { | ||||
| 		return c.Status(400).SendString("Invalid user id") | ||||
| 	} | ||||
| 
 | ||||
| 	// Get the username from the database | ||||
| 	username, err := db.GetDb(c).GetUserName(user_id) | ||||
| 	if err != nil { | ||||
| 		return c.Status(500).SendString(err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	// Send the nuclear launch codes to north korea | ||||
| 	return c.JSON(fiber.Map{"username": username}) | ||||
| } | ||||
|  | @ -103,6 +103,7 @@ func main() { | |||
| 	// userGroup := api.Group("/user") // Not currently in use | ||||
| 	api.Get("/users/all", users.ListAllUsers) | ||||
| 	api.Get("/project/getAllUsers", users.GetAllUsersProject) | ||||
| 	api.Get("/username", users.GetUserName) | ||||
| 	api.Post("/login", users.Login) | ||||
| 	api.Post("/register", users.Register) | ||||
| 	api.Post("/loginrenew", users.LoginRenew) | ||||
|  | @ -121,6 +122,7 @@ func main() { | |||
| 	api.Post("/ProjectRoleChange", projects.ProjectRoleChange) | ||||
| 	api.Put("/promoteToPm/:projectName", projects.PromoteToPm) | ||||
| 	api.Put("/addUserToProject/:projectName", projects.AddUserToProjectHandler) | ||||
| 	api.Delete("/removeUserFromProject/:projectName", projects.RemoveUserFromProject) | ||||
| 	api.Delete("/removeProject/:projectName", projects.RemoveProject) | ||||
| 	api.Delete("/project/:projectID", projects.DeleteProject) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import { NewProjMember } from "../Components/AddMember"; | ||||
| import { AddMemberInfo } from "../Components/AddMember"; | ||||
| import { ProjectRoleChange } from "../Components/ChangeRole"; | ||||
| import { projectTimes } from "../Components/GetProjectTimes"; | ||||
| import { ProjectMember } from "../Components/GetUsersInProject"; | ||||
|  | @ -197,7 +197,13 @@ interface API { | |||
|   ): Promise<APIResponse<void>>; | ||||
| 
 | ||||
|   addUserToProject( | ||||
|     user: NewProjMember, | ||||
|     addMemberInfo: AddMemberInfo, | ||||
|     token: string, | ||||
|   ): Promise<APIResponse<void>>; | ||||
| 
 | ||||
|   removeUserFromProject( | ||||
|     user: string, | ||||
|     project: string, | ||||
|     token: string, | ||||
|   ): Promise<APIResponse<void>>; | ||||
| 
 | ||||
|  | @ -227,6 +233,12 @@ interface API { | |||
|     projectName: string, | ||||
|     token: string, | ||||
|   ): Promise<APIResponse<string>>; | ||||
|   /** | ||||
|    * Get the username from the id | ||||
|    * @param {number} id The id of the user | ||||
|    * @param {string} token Your token | ||||
|    */ | ||||
|   getUsername(id: number, token: string): Promise<APIResponse<string>>; | ||||
| } | ||||
| 
 | ||||
| /** An instance of the API */ | ||||
|  | @ -336,18 +348,20 @@ export const api: API = { | |||
|   }, | ||||
| 
 | ||||
|   async addUserToProject( | ||||
|     user: NewProjMember, | ||||
|     addMemberInfo: AddMemberInfo, | ||||
|     token: string, | ||||
|   ): Promise<APIResponse<void>> { | ||||
|     try { | ||||
|       const response = await fetch("/api/addUserToProject", { | ||||
|         method: "PUT", | ||||
|         headers: { | ||||
|           "Content-Type": "application/json", | ||||
|           Authorization: "Bearer " + token, | ||||
|       const response = await fetch( | ||||
|         `/api/addUserToProject/${addMemberInfo.projectName}/?userName=${addMemberInfo.userName}`, | ||||
|         { | ||||
|           method: "PUT", | ||||
|           headers: { | ||||
|             "Content-Type": "application/json", | ||||
|             Authorization: "Bearer " + token, | ||||
|           }, | ||||
|         }, | ||||
|         body: JSON.stringify(user), | ||||
|       }); | ||||
|       ); | ||||
| 
 | ||||
|       if (!response.ok) { | ||||
|         return { success: false, message: "Failed to add member" }; | ||||
|  | @ -359,6 +373,31 @@ export const api: API = { | |||
|     } | ||||
|   }, | ||||
| 
 | ||||
|   async removeUserFromProject( | ||||
|     user: string, | ||||
|     project: string, | ||||
|     token: string, | ||||
|   ): Promise<APIResponse<void>> { | ||||
|     try { | ||||
|       const response = await fetch( | ||||
|         `/api/removeUserFromProject/${project}?userName=${user}`, | ||||
|         { | ||||
|           method: "DELETE", | ||||
|           headers: { | ||||
|             "Content-Type": "application/json", | ||||
|             Authorization: "Bearer " + token, | ||||
|           }, | ||||
|         }, | ||||
|       ); | ||||
|       if (!response.ok) { | ||||
|         return { success: false, message: "Failed to remove member" }; | ||||
|       } | ||||
|     } catch (e) { | ||||
|       return { success: false, message: "Failed to remove member" }; | ||||
|     } | ||||
|     return { success: true, message: "Removed member" }; | ||||
|   }, | ||||
| 
 | ||||
|   async renewToken(token: string): Promise<APIResponse<string>> { | ||||
|     try { | ||||
|       const response = await fetch("/api/loginrenew", { | ||||
|  | @ -837,4 +876,25 @@ export const api: API = { | |||
|     } | ||||
|     return { success: true, message: "User promoted to project manager" }; | ||||
|   }, | ||||
| 
 | ||||
|   async getUsername(id: number, token: string): Promise<APIResponse<string>> { | ||||
|     try { | ||||
|       const response = await fetch(`/api/username?userId=${id}`, { | ||||
|         method: "GET", | ||||
|         headers: { | ||||
|           "Content-Type": "application/json", | ||||
|           Authorization: "Bearer " + token, | ||||
|         }, | ||||
|       }); | ||||
| 
 | ||||
|       if (!response.ok) { | ||||
|         return { success: false, message: "Failed to get username" }; | ||||
|       } else { | ||||
|         const data = (await response.json()) as string; | ||||
|         return { success: true, data }; | ||||
|       } | ||||
|     } catch (e) { | ||||
|       return { success: false, message: "Failed to get username" }; | ||||
|     } | ||||
|   }, | ||||
| }; | ||||
|  |  | |||
|  | @ -1,44 +1,35 @@ | |||
| import { APIResponse, api } from "../API/API"; | ||||
| import { api } from "../API/API"; | ||||
| 
 | ||||
| export interface NewProjMember { | ||||
|   username: string; | ||||
|   role: string; | ||||
|   projectname: string; | ||||
| export interface AddMemberInfo { | ||||
|   userName: string; | ||||
|   projectName: string; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Tries to add a member to a project | ||||
|  * @param {Object} props - A NewProjMember | ||||
|  * @returns {boolean} True if added, false if not | ||||
|  * @param {AddMemberInfo} props.membertoAdd - Contains user's name and project's name | ||||
|  * @returns {Promise<void>} | ||||
|  */ | ||||
| function AddMember(props: { memberToAdd: NewProjMember }): boolean { | ||||
|   let added = false; | ||||
|   if ( | ||||
|     props.memberToAdd.username === "" || | ||||
|     props.memberToAdd.role === "" || | ||||
|     props.memberToAdd.projectname === "" | ||||
|   ) { | ||||
|     alert("All fields must be filled before adding"); | ||||
|     return added; | ||||
| async function AddMember(props: { memberToAdd: AddMemberInfo }): Promise<void> { | ||||
|   if (props.memberToAdd.userName === "") { | ||||
|     alert("You must choose at least one user to add"); | ||||
|     return; | ||||
|   } | ||||
|   api | ||||
|     .addUserToProject( | ||||
|   try { | ||||
|     const response = await api.addUserToProject( | ||||
|       props.memberToAdd, | ||||
|       localStorage.getItem("accessToken") ?? "", | ||||
|     ) | ||||
|     .then((response: APIResponse<void>) => { | ||||
|       if (response.success) { | ||||
|         alert("Member added"); | ||||
|         added = true; | ||||
|       } else { | ||||
|         alert("Member not added"); | ||||
|         console.error(response.message); | ||||
|       } | ||||
|     }) | ||||
|     .catch((error) => { | ||||
|       console.error("An error occurred during member add:", error); | ||||
|     }); | ||||
|   return added; | ||||
|     ); | ||||
|     if (response.success) { | ||||
|       alert(`[${props.memberToAdd.userName}] added`); | ||||
|     } else { | ||||
|       alert(`[${props.memberToAdd.userName}] not added`); | ||||
|       console.error(response.message); | ||||
|     } | ||||
|   } catch (error) { | ||||
|     alert(`[${props.memberToAdd.userName}] not added`); | ||||
|     console.error("An error occurred during member add:", error); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export default AddMember; | ||||
|  |  | |||
|  | @ -1,37 +1,10 @@ | |||
| import { useState } from "react"; | ||||
| import { APIResponse, api } from "../API/API"; | ||||
| import { api } from "../API/API"; | ||||
| import { NewProject } from "../Types/goTypes"; | ||||
| import InputField from "./InputField"; | ||||
| import Logo from "../assets/Logo.svg"; | ||||
| import Button from "./Button"; | ||||
| 
 | ||||
| /** | ||||
|  * Tries to add a project to the system | ||||
|  * @param {Object} props - Project name and description | ||||
|  * @returns {boolean} True if created, false if not | ||||
|  */ | ||||
| function CreateProject(props: { name: string; description: string }): void { | ||||
|   const project: NewProject = { | ||||
|     name: props.name, | ||||
|     description: props.description, | ||||
|   }; | ||||
| 
 | ||||
|   api | ||||
|     .createProject(project, localStorage.getItem("accessToken") ?? "") | ||||
|     .then((response: APIResponse<void>) => { | ||||
|       if (response.success) { | ||||
|         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); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Provides UI for adding a project to the system. | ||||
|  * @returns {JSX.Element} - Returns the component UI for adding a project | ||||
|  | @ -40,6 +13,33 @@ function AddProject(): JSX.Element { | |||
|   const [name, setName] = useState(""); | ||||
|   const [description, setDescription] = useState(""); | ||||
| 
 | ||||
|   /** | ||||
|    * Tries to add a project to the system | ||||
|    */ | ||||
|   const handleCreateProject = async (): Promise<void> => { | ||||
|     const project: NewProject = { | ||||
|       name: name.replace(/ /g, ""), | ||||
|       description: description.trim(), | ||||
|     }; | ||||
|     try { | ||||
|       const response = await api.createProject( | ||||
|         project, | ||||
|         localStorage.getItem("accessToken") ?? "", | ||||
|       ); | ||||
|       if (response.success) { | ||||
|         alert(`${project.name} added!`); | ||||
|         setDescription(""); | ||||
|         setName(""); | ||||
|       } else { | ||||
|         alert("Project not added, name could be taken"); | ||||
|         console.error(response.message); | ||||
|       } | ||||
|     } catch (error) { | ||||
|       alert("Project not added"); | ||||
|       console.error(error); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   return ( | ||||
|     <div className="flex flex-col h-fit w-screen items-center justify-center"> | ||||
|       <div className="border-4 border-black bg-white flex flex-col items-center justify-center h-fit w-fit rounded-3xl content-center pl-20 pr-20"> | ||||
|  | @ -47,10 +47,7 @@ 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, | ||||
|             }); | ||||
|             void handleCreateProject(); | ||||
|           }} | ||||
|         > | ||||
|           <img | ||||
|  |  | |||
|  | @ -1,71 +1,86 @@ | |||
| import { useState } from "react"; | ||||
| import { useEffect, useState } from "react"; | ||||
| import Button from "./Button"; | ||||
| import GetAllUsers from "./GetAllUsers"; | ||||
| import AddMember, { NewProjMember } from "./AddMember"; | ||||
| import AddMember, { AddMemberInfo } from "./AddMember"; | ||||
| import BackButton from "./BackButton"; | ||||
| import GetUsersInProject, { ProjectMember } from "./GetUsersInProject"; | ||||
| import GetAllUsers from "./GetAllUsers"; | ||||
| 
 | ||||
| /** | ||||
|  * Provides UI for adding a member to a project. | ||||
|  * @returns {JSX.Element} - Returns the component UI for adding a member | ||||
|  */ | ||||
| function AddUserToProject(props: { projectName: string }): JSX.Element { | ||||
|   const [name, setName] = useState(""); | ||||
|   const [names, setNames] = useState<string[]>([]); | ||||
|   const [users, setUsers] = useState<string[]>([]); | ||||
|   const [role, setRole] = useState(""); | ||||
|   GetAllUsers({ setUsersProp: setUsers }); | ||||
|   const [usersProj, setUsersProj] = useState<ProjectMember[]>([]); | ||||
| 
 | ||||
|   const handleClick = (): boolean => { | ||||
|     const newMember: NewProjMember = { | ||||
|       username: name, | ||||
|       projectname: props.projectName, | ||||
|       role: role, | ||||
|     }; | ||||
|     return AddMember({ memberToAdd: newMember }); | ||||
|   // Gets all users and project members for filtering
 | ||||
|   GetAllUsers({ setUsersProp: setUsers }); | ||||
|   GetUsersInProject({ | ||||
|     setUsersProp: setUsersProj, | ||||
|     projectName: props.projectName, | ||||
|   }); | ||||
|   /* | ||||
|    * Filters the members from users so that users who are already | ||||
|    * members are not shown | ||||
|    */ | ||||
|   useEffect(() => { | ||||
|     setUsers((prevUsers) => { | ||||
|       const filteredUsers = prevUsers.filter( | ||||
|         (user) => | ||||
|           !usersProj.some((projectUser) => projectUser.Username === user), | ||||
|       ); | ||||
|       return filteredUsers; | ||||
|     }); | ||||
|   }, [usersProj]); | ||||
| 
 | ||||
|   // Attempts to add all of the selected users to the project
 | ||||
|   const handleAddClick = async (): Promise<void> => { | ||||
|     if (names.length === 0) | ||||
|       alert("You have to choose at least one user to add"); | ||||
|     for (const name of names) { | ||||
|       const newMember: AddMemberInfo = { | ||||
|         userName: name, | ||||
|         projectName: props.projectName, | ||||
|       }; | ||||
|       await AddMember({ memberToAdd: newMember }); | ||||
|     } | ||||
|     setNames([]); | ||||
|     location.reload(); | ||||
|   }; | ||||
| 
 | ||||
|   // Updates the names that have been selected
 | ||||
|   const handleUserClick = (user: string): void => { | ||||
|     setNames((prevNames): string[] => { | ||||
|       if (!prevNames.includes(user)) { | ||||
|         return [...prevNames, user]; | ||||
|       } | ||||
|       return prevNames.filter((name) => name !== user); | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   return ( | ||||
|     <div className="border-4 border-black bg-white flex flex-col items-center justify-center rounded-3xl content-center pl-20 pr-20  h-[75vh] w-[50vh]"> | ||||
|       <p className="pb-4 mb-2 text-center font-bold text-[18px]"> | ||||
|         User chosen: [{name}] | ||||
|     <div className="border-4 border-black bg-white flex flex-col items-center pt-10 rounded-3xl content-center pl-20 pr-20  h-[63vh] w-[50] overflow-auto"> | ||||
|       <h1 className="text-center font-bold text-[36px] pb-10"> | ||||
|         {props.projectName} | ||||
|       </h1> | ||||
|       <p className="p-1 text-center font-bold text-[26px]"> | ||||
|         Choose users to add: | ||||
|       </p> | ||||
|       <p className="pb-4 mb-2 text-center font-bold text-[18px]"> | ||||
|         Role chosen: [{role}] | ||||
|       </p> | ||||
|       <p className="pb-4 mb-2 text-center font-bold text-[18px]"> | ||||
|         Project chosen: [{props.projectName}] | ||||
|       </p> | ||||
|       <p className="p-1">Choose role:</p> | ||||
|       <div className="border-2 border-black p-2 rounded-xl text-center h-[10h] w-[16] overflow-auto"> | ||||
|         <ul className="text-center items-center font-medium space-y-2"> | ||||
|           <li | ||||
|             className="h-[10] w-[14] items-start px-2 py-1 border-2 border-black rounded-full bg-orange-200 hover:bg-orange-600 hover:text-slate-100 hover:cursor-pointer" | ||||
|             onClick={() => { | ||||
|               setRole("member"); | ||||
|             }} | ||||
|           > | ||||
|             {"Member"} | ||||
|           </li> | ||||
|           <li | ||||
|             className="h-[10] w-[14] items-start px-2 py-1 border-2 border-black rounded-full bg-orange-200 hover:bg-orange-600 hover:text-slate-100 hover:cursor-pointer" | ||||
|             onClick={() => { | ||||
|               setRole("project_manager"); | ||||
|             }} | ||||
|           > | ||||
|             {"Project manager"} | ||||
|           </li> | ||||
|         </ul> | ||||
|       </div> | ||||
|       <p className="p-1">Choose user:</p> | ||||
|       <div className="border-2 border-black p-2 rounded-xl text-center overflow-scroll h-[26vh] w-[26vh]"> | ||||
|       <div className="border-2 border-black pl-2 pr-2 pb-2 rounded-xl text-center overflow-auto h-[26vh] w-[26vh]"> | ||||
|         <ul className="text-center font-medium space-y-2"> | ||||
|           <div></div> | ||||
|           {users.map((user) => ( | ||||
|             <li | ||||
|               className="items-start p-1 border-2 border-black rounded-full bg-orange-200 hover:bg-orange-600 hover:text-slate-100 hover:cursor-pointer" | ||||
|               className={ | ||||
|                 names.includes(user) | ||||
|                   ? "items-start p-1 border-2 border-transparent rounded-full bg-orange-500 hover:bg-orange-600 text-white hover:cursor-pointer ring-2 ring-black" | ||||
|                   : "items-start p-1 border-2 border-black rounded-full bg-orange-200 hover:bg-orange-400 hover:text-slate-100 hover:cursor-pointer" | ||||
|               } | ||||
|               key={user} | ||||
|               value={user} | ||||
|               onClick={() => { | ||||
|                 setName(user); | ||||
|                 handleUserClick(user); | ||||
|               }} | ||||
|             > | ||||
|               <span>{user}</span> | ||||
|  | @ -73,13 +88,16 @@ function AddUserToProject(props: { projectName: string }): JSX.Element { | |||
|           ))} | ||||
|         </ul> | ||||
|       </div> | ||||
|       <div className="flex space-x-5 items-center justify-between"> | ||||
|       <p className="pt-10 pb-5 underline text-center font-bold text-[18px]"> | ||||
|         Number of users to be added: {names.length} | ||||
|       </p> | ||||
|       <div className="space-x-10 items-center"> | ||||
|         <Button | ||||
|           text="Add" | ||||
|           onClick={(): void => { | ||||
|             handleClick(); | ||||
|             void handleAddClick(); | ||||
|           }} | ||||
|           type="submit" | ||||
|           type="button" | ||||
|         /> | ||||
|         <BackButton /> | ||||
|       </div> | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import { useState } from "react"; | |||
| import Button from "./Button"; | ||||
| import ChangeRole, { ProjectRoleChange } from "./ChangeRole"; | ||||
| 
 | ||||
| export default function ChangeRoles(props: { | ||||
| export default function ChangeRoleView(props: { | ||||
|   projectName: string; | ||||
|   username: string; | ||||
| }): JSX.Element { | ||||
|  |  | |||
|  | @ -2,8 +2,11 @@ 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"); | ||||
|   if ( | ||||
|     props.nameChange.newName === "" || | ||||
|     props.nameChange.newName === props.nameChange.prevName | ||||
|   ) { | ||||
|     alert("You have to give a new name\n\nName not changed"); | ||||
|     return; | ||||
|   } | ||||
|   api | ||||
|  | @ -13,7 +16,7 @@ function ChangeUsername(props: { nameChange: StrNameChange }): void { | |||
|         alert("Name changed successfully"); | ||||
|         location.reload(); | ||||
|       } else { | ||||
|         alert("Name not changed"); | ||||
|         alert("Name not changed, name could be taken"); | ||||
|         console.error(response.message); | ||||
|       } | ||||
|     }) | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| import Button from "./Button"; | ||||
| import DeleteUser from "./DeleteUser"; | ||||
| import UserProjectListAdmin from "./UserProjectListAdmin"; | ||||
| import { useState } from "react"; | ||||
| import ChangeRoleView from "./ChangeRoleView"; | ||||
| import RemoveUserFromProj from "./RemoveUserFromProj"; | ||||
| 
 | ||||
| function MemberInfoModal(props: { | ||||
|   projectName: string; | ||||
|  | @ -20,7 +20,7 @@ function MemberInfoModal(props: { | |||
|   }; | ||||
|   return ( | ||||
|     <div | ||||
|       className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm  | ||||
|       className="fixed inset-10 bg-opacity-30 backdrop-blur-sm  | ||||
|       flex justify-center items-center" | ||||
|     > | ||||
|       <div className="border-4 border-black bg-white rounded-lg text-center flex flex-col"> | ||||
|  | @ -42,13 +42,16 @@ function MemberInfoModal(props: { | |||
|           <UserProjectListAdmin username={props.username} /> | ||||
|           <div className="items-center space-x-6"> | ||||
|             <Button | ||||
|               text={"Delete"} | ||||
|               text={"Remove"} | ||||
|               onClick={function (): void { | ||||
|                 if ( | ||||
|                   window.confirm("Are you sure you want to delete this user?") | ||||
|                   window.confirm( | ||||
|                     "Are you sure you want to remove this user from the project?", | ||||
|                   ) | ||||
|                 ) { | ||||
|                   DeleteUser({ | ||||
|                     usernameToDelete: props.username, | ||||
|                   RemoveUserFromProj({ | ||||
|                     userToRemove: props.username, | ||||
|                     projectName: props.projectName, | ||||
|                   }); | ||||
|                 } | ||||
|               }} | ||||
|  |  | |||
|  | @ -15,17 +15,21 @@ export default function Register(): JSX.Element { | |||
|   const [errMessage, setErrMessage] = useState<string>(); | ||||
| 
 | ||||
|   const handleRegister = async (): Promise<void> => { | ||||
|     if (username === "" || password === "") { | ||||
|       alert("Must provide username and password"); | ||||
|       return; | ||||
|     } | ||||
|     const newUser: NewUser = { | ||||
|       username: username ?? "", | ||||
|       username: username?.replace(/ /g, "") ?? "", | ||||
|       password: password ?? "", | ||||
|     }; | ||||
|     const response = await api.registerUser(newUser); | ||||
|     if (response.success) { | ||||
|       alert("User added!"); | ||||
|       alert(`${newUser.username} added!`); | ||||
|       setPassword(""); | ||||
|       setUsername(""); | ||||
|     } else { | ||||
|       alert("User not added"); | ||||
|       alert("User not added, name could be taken"); | ||||
|       setErrMessage(response.message ?? "Unknown error"); | ||||
|       console.error(errMessage); | ||||
|     } | ||||
|  |  | |||
							
								
								
									
										41
									
								
								frontend/src/Components/RemoveUserFromProj.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								frontend/src/Components/RemoveUserFromProj.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| import { api, APIResponse } from "../API/API"; | ||||
| 
 | ||||
| /** | ||||
|  * Removes a user from a project | ||||
|  * @param {string} props.usernameToDelete - The username of user to remove | ||||
|  * @param {string} props.projectName - Project to remove user from | ||||
|  * @returns {void} | ||||
|  * @example | ||||
|  * const exampleUsername = "user"; | ||||
|  * const exampleProjectName "project"; | ||||
|  * RemoveUserFromProj({ userToRemove: exampleUsername, projectName: exampleProjectName }); | ||||
|  */ | ||||
| 
 | ||||
| export default function RemoveUserFromProj(props: { | ||||
|   userToRemove: string; | ||||
|   projectName: string; | ||||
| }): void { | ||||
|   if (props.userToRemove === localStorage.getItem("username")) { | ||||
|     alert("Cannot remove yourself"); | ||||
|     return; | ||||
|   } | ||||
|   api | ||||
|     .removeUserFromProject( | ||||
|       props.userToRemove, | ||||
|       props.projectName, | ||||
|       localStorage.getItem("accessToken") ?? "", | ||||
|     ) | ||||
|     .then((response: APIResponse<void>) => { | ||||
|       if (response.success) { | ||||
|         alert(`${props.userToRemove} has been removed!`); | ||||
|         location.reload(); | ||||
|       } else { | ||||
|         alert(`${props.userToRemove} has not been removed due to an error`); | ||||
|         console.error(response.message); | ||||
|       } | ||||
|     }) | ||||
|     .catch((error) => { | ||||
|       alert(`${props.userToRemove} has not been removed due to an error`); | ||||
|       console.error("An error occurred during deletion:", error); | ||||
|     }); | ||||
| } | ||||
|  | @ -28,7 +28,7 @@ function UserInfoModal(props: { | |||
|   const handleClickChangeName = (): void => { | ||||
|     const nameChange: StrNameChange = { | ||||
|       prevName: props.username, | ||||
|       newName: newUsername, | ||||
|       newName: newUsername.replace(/ /g, ""), | ||||
|     }; | ||||
|     ChangeUsername({ nameChange: nameChange }); | ||||
|   }; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Davenludd
						Davenludd