From 632676e3d2c76c808d48c1b455949a71c0334dad Mon Sep 17 00:00:00 2001 From: Peter KW Date: Tue, 2 Apr 2024 19:16:02 +0200 Subject: [PATCH 01/45] Added admin to all projects in sample_data --- backend/internal/database/sample_data/0010_sample_data.sql | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/internal/database/sample_data/0010_sample_data.sql b/backend/internal/database/sample_data/0010_sample_data.sql index ab74f1a..70499b0 100644 --- a/backend/internal/database/sample_data/0010_sample_data.sql +++ b/backend/internal/database/sample_data/0010_sample_data.sql @@ -21,6 +21,12 @@ VALUES ("projecttest3","test project3", 1); INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) VALUES (1,1,"project_manager"); +INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) +VALUES (1,2,"project_manager"); + +INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) +VALUES (1,3,"project_manager"); + INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) VALUES (2,1,"member"); From 46eb3c76a8aa83aff5413a5c389c8c019da7c998 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Tue, 2 Apr 2024 19:18:05 +0200 Subject: [PATCH 02/45] Small fix to statistics --- frontend/src/Components/ProjectInfoModal.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/Components/ProjectInfoModal.tsx b/frontend/src/Components/ProjectInfoModal.tsx index 6236089..1f98d79 100644 --- a/frontend/src/Components/ProjectInfoModal.tsx +++ b/frontend/src/Components/ProjectInfoModal.tsx @@ -38,7 +38,12 @@ function ProjectInfoModal(props: {

Number of members: {users.length}

-

Total time reported: {totalTime.current}

+

+ Total time reported:{" "} + {Math.floor(totalTime.current / 60 / 24) + " d "} + {Math.floor((totalTime.current / 60) % 24) + " h "} + {(totalTime.current % 60) + " m "} +

Project members:

From 1f9ccca9bf508f78313a04666e47af006568567b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 14:21:55 +0200 Subject: [PATCH 03/45] Fixing integration test script --- testing.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/testing.py b/testing.py index d4594d1..7be2290 100644 --- a/testing.py +++ b/testing.py @@ -83,13 +83,14 @@ def test_ProjectRoleChange(): def test_get_user_projects(): + username = "user2" + password = "123" dprint("Testing get user projects") - loginResponse = login("user2", "123") + loginResponse = login(username, password) # Check if the user is added to the project response = requests.get( - getUserProjectsPath, - json={"username": "user2"}, + getUserProjectsPath + "/" + username, headers={"Authorization": "Bearer " + loginResponse.json()["token"]}, ) dprint(response.text) From 9cca8edd9d9176dfa6a11e45e10128932c454e9e Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 14:50:04 +0200 Subject: [PATCH 04/45] Additional tests --- testing.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/testing.py b/testing.py index 7be2290..5a181ec 100644 --- a/testing.py +++ b/testing.py @@ -257,13 +257,9 @@ def test_sign_report(): ) response = requests.put( - addUserToProjectPath, - json={ - "projectName": projectName, - "username": project_manager, - "role": "project_manager", - }, + addUserToProjectPath + "/" + projectName, headers={"Authorization": "Bearer " + admin_token}, + params={"userName": project_manager} ) assert response.status_code == 200, "Add project manager to project failed" dprint("Project manager added to project successfully") @@ -515,6 +511,58 @@ def test_get_unsigned_reports(): assert response.status_code == 200, "Get unsigned reports failed" gprint("test_get_unsigned_reports successful") +def test_get_other_users_report_as_pm(): + # Create user + user = randomString() + register(user, "password") + + # Create project + project = randomString() + pm_token = login(user, "password").json()["token"] + response = requests.post( + addProjectPath, + json={"name": project, "description": "This is a project"}, + headers={"Authorization": "Bearer " + pm_token}, + ) + assert response.status_code == 200, "Add project failed" + + # Create other user + other_user = randomString() + register(other_user, "password") + user_token = login(other_user, "password").json()["token"] + + # Add other user to project + response = requests.put( + addUserToProjectPath + "/" + project, + headers={"Authorization": "Bearer " + pm_token}, # note pm_token + params={"userName": other_user}, + ) + assert response.status_code == 200, "Add user to project failed" + + # Submit report as other user + response = requests.post( + submitReportPath, + json={ + "projectName": project, + "week": 1, + "developmentTime": 10, + "meetingTime": 5, + "adminTime": 5, + "ownWorkTime": 10, + "studyTime": 10, + "testingTime": 10, + }, + headers={"Authorization": "Bearer " + user_token}, + ) + assert response.status_code == 200, "Submit report failed" + + # Get report as project manager + response = requests.get( + getWeeklyReportPath, + headers={"Authorization": "Bearer " + pm_token}, + params={"targetUser": other_user, "projectName": project, "week": 1}, + ) + assert response.status_code == 200, "Get weekly report failed" if __name__ == "__main__": test_remove_project() @@ -535,4 +583,5 @@ if __name__ == "__main__": test_list_all_users_project() test_change_user_name() test_update_weekly_report() + test_get_other_users_report_as_pm() \ No newline at end of file From 61a2d1ce0c3e2599d47de9dcd23d4ab0221f11c7 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 15:53:15 +0200 Subject: [PATCH 05/45] PromoteToPm handler --- .../internal/handlers/projects/PromoteToPm.go | 51 ++ backend/main.go | 3 +- frontend/src/API/API.ts | 44 ++ testing.py | 587 ------------------ 4 files changed, 97 insertions(+), 588 deletions(-) create mode 100644 backend/internal/handlers/projects/PromoteToPm.go delete mode 100644 testing.py diff --git a/backend/internal/handlers/projects/PromoteToPm.go b/backend/internal/handlers/projects/PromoteToPm.go new file mode 100644 index 0000000..ffe2215 --- /dev/null +++ b/backend/internal/handlers/projects/PromoteToPm.go @@ -0,0 +1,51 @@ +package projects + +import ( + db "ttime/internal/database" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" + "github.com/golang-jwt/jwt/v5" +) + +// @Summary Promote to project manager +// @Description Promote a user to project manager +// @Tags Auth +// @Security JWT +// @Accept plain +// @Produce plain +// @Param projectName path string true "Project name" +// @Param userName query string true "User name" +// @Failure 500 {string} string "Internal server error" +// @Failure 403 {string} string "Forbidden" +// @Router /promote/{projectName} [put] +// +// Login logs in a user and returns a JWT token +// Promote to project manager +func PromoteToPm(c *fiber.Ctx) error { + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + pm_name := claims["name"].(string) + + project := c.Params("projectName") + new_pm_name := 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") + } + + // Add the user to the project with the specified role + err = db.GetDb(c).ChangeUserRole(new_pm_name, project, "project_manager") + + // Return success message + log.Info("User : ", new_pm_name, " promoted to project manager in project: ", project) + return c.SendStatus(fiber.StatusOK) +} diff --git a/backend/main.go b/backend/main.go index 6e65386..f811a58 100644 --- a/backend/main.go +++ b/backend/main.go @@ -119,6 +119,8 @@ func main() { api.Get("/getUsersProject/:projectName", projects.ListAllUsersProject) api.Post("/project", projects.CreateProject) api.Post("/ProjectRoleChange", projects.ProjectRoleChange) + api.Put("/promoteToPm/:projectName", projects.PromoteToPm) + api.Put("/addUserToProject/:projectName", projects.AddUserToProjectHandler) api.Delete("/removeProject/:projectName", projects.RemoveProject) api.Delete("/project/:projectID", projects.DeleteProject) @@ -129,7 +131,6 @@ func main() { api.Get("/getWeeklyReportsUser/:projectName", reports.GetWeeklyReportsUserHandler) api.Post("/submitWeeklyReport", reports.SubmitWeeklyReport) api.Put("/signReport/:reportId", reports.SignReport) - api.Put("/addUserToProject", projects.AddUserToProjectHandler) api.Put("/updateWeeklyReport", reports.UpdateWeeklyReport) // Announce the port we are listening on and start the server diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index c1480fb..f8d3e5c 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -209,6 +209,19 @@ interface API { * @param {string} token The authentication token */ signReport(reportId: number, token: string): Promise>; + + /** + * Promotes a user to project manager within a project. + * + * @param {string} userName The username of the user to promote + * @param {string} projectName The name of the project to promote the user in + * @returns {Promise} A promise resolving to an API response. + */ + promoteToPm( + userName: string, + projectName: string, + token: string, + ): Promise>; } /** An instance of the API */ @@ -783,4 +796,35 @@ export const api: API = { return { success: false, message: "Failed to sign report" }; } }, + + async promoteToPm( + userName: string, + projectName: string, + token: string, + ): Promise> { + try { + const response = await fetch( + `/api/promoteToPm/${projectName}?userName=${userName}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, + }, + ); + if (!response.ok) { + return { + success: false, + message: "Failed to promote user to project manager", + }; + } + } catch (e) { + return { + success: false, + message: "Failed to promote user to project manager", + }; + } + return { success: true, message: "User promoted to project manager" }; + }, }; diff --git a/testing.py b/testing.py deleted file mode 100644 index 5a181ec..0000000 --- a/testing.py +++ /dev/null @@ -1,587 +0,0 @@ -import requests -import string -import random - -debug_output = True - -def gprint(*args, **kwargs): - print("\033[92m", *args, "\033[00m", **kwargs) - -print("Running Tests...") - -def dprint(*args, **kwargs): - if debug_output: - print(*args, **kwargs) - -def randomString(len=10): - """Generate a random string of fixed length""" - letters = string.ascii_lowercase - return "".join(random.choice(letters) for i in range(len)) - - -# Defined once per test run -username = "user_" + randomString() -projectName = "project_" + randomString() - -# The base URL of the API -base_url = "http://localhost:8080" - -# Endpoint to test -registerPath = base_url + "/api/register" -loginPath = base_url + "/api/login" -addProjectPath = base_url + "/api/project" -submitReportPath = base_url + "/api/submitWeeklyReport" -getWeeklyReportPath = base_url + "/api/getWeeklyReport" -getProjectPath = base_url + "/api/project" -signReportPath = base_url + "/api/signReport" -addUserToProjectPath = base_url + "/api/addUserToProject" -promoteToAdminPath = base_url + "/api/promoteToAdmin" -getUserProjectsPath = base_url + "/api/getUserProjects" -getWeeklyReportsUserPath = base_url + "/api/getWeeklyReportsUser" -checkIfProjectManagerPath = base_url + "/api/checkIfProjectManager" -ProjectRoleChangePath = base_url + "/api/ProjectRoleChange" -getUsersProjectPath = base_url + "/api/getUsersProject" -getUnsignedReportsPath = base_url + "/api/getUnsignedReports" -getChangeUserNamePath = base_url + "/api/changeUserName" -getUpdateWeeklyReportPath = base_url + "/api/updateWeeklyReport" -removeProjectPath = base_url + "/api/removeProject" - -#ta bort auth i handlern för att få testet att gå igenom -def test_ProjectRoleChange(): - dprint("Testing ProjectRoleChange") - localUsername = randomString() - localProjectName = randomString() - register(localUsername, "username_password") - - token = login(localUsername, "username_password").json()[ - "token" - ] - - # Just checking since this test is built somewhat differently than the others - assert token != None, "Login failed" - - response = requests.post( - addProjectPath, - json={"name": localProjectName, "description": "This is a project"}, - headers={"Authorization": "Bearer " + token}, - ) - - if response.status_code != 200: - print("Add project failed") - - response = requests.post( - ProjectRoleChangePath, - headers={"Authorization": "Bearer " + token}, - json={ - "projectName": localProjectName, - "role": "project_manager", - }, - ) - - assert response.status_code == 200, "ProjectRoleChange failed" - gprint("test_ProjectRoleChange successful") - - -def test_get_user_projects(): - username = "user2" - password = "123" - - dprint("Testing get user projects") - loginResponse = login(username, password) - # Check if the user is added to the project - response = requests.get( - getUserProjectsPath + "/" + username, - headers={"Authorization": "Bearer " + loginResponse.json()["token"]}, - ) - dprint(response.text) - dprint(response.json()) - assert response.status_code == 200, "Get user projects failed" - gprint("test_get_user_projects successful") - - -# Posts the username and password to the register endpoint -def register(username: string, password: string): - dprint("Registering with username: ", username, " and password: ", password) - response = requests.post( - registerPath, json={"username": username, "password": password} - ) - dprint(response.text) - return response - - -# Posts the username and password to the login endpoint -def login(username: string, password: string): - dprint("Logging in with username: ", username, " and password: ", password) - response = requests.post( - loginPath, json={"username": username, "password": password} - ) - dprint(response.text) - return response - - -# Test function to login -def test_login(): - response = login(username, "always_same") - assert response.status_code == 200, "Login failed" - dprint("Login successful") - gprint("test_login successful") - return response.json()["token"] - - -# Test function to create a new user -def test_create_user(): - response = register(username, "always_same") - assert response.status_code == 200, "Registration failed" - gprint("test_create_user successful") - -# Test function to add a project -def test_add_project(): - loginResponse = login(username, "always_same") - token = loginResponse.json()["token"] - response = requests.post( - addProjectPath, - json={"name": projectName, "description": "This is a project"}, - headers={"Authorization": "Bearer " + token}, - ) - dprint(response.text) - assert response.status_code == 200, "Add project failed" - gprint("test_add_project successful") - -# Test function to submit a report -def test_submit_report(): - token = login(username, "always_same").json()["token"] - response = requests.post( - submitReportPath, - json={ - "projectName": projectName, - "week": 1, - "developmentTime": 10, - "meetingTime": 5, - "adminTime": 5, - "ownWorkTime": 10, - "studyTime": 10, - "testingTime": 10, - }, - headers={"Authorization": "Bearer " + token}, - ) - dprint(response.text) - assert response.status_code == 200, "Submit report failed" - gprint("test_submit_report successful") - -# Test function to get a weekly report -def test_get_weekly_report(): - token = login(username, "always_same").json()["token"] - response = requests.get( - getWeeklyReportPath, - headers={"Authorization": "Bearer " + token}, - params={"username": username, "projectName": projectName, "week": 1}, - ) - dprint(response.text) - assert response.status_code == 200, "Get weekly report failed" - gprint("test_get_weekly_report successful") - - -# Tests getting a project by id -def test_get_project(): - token = login(username, "always_same").json()["token"] - response = requests.get( - getProjectPath + "/1", # Assumes that the project with id 1 exists - headers={"Authorization": "Bearer " + token}, - ) - dprint(response.text) - assert response.status_code == 200, "Get project failed" - gprint("test_get_project successful") - - -# Test function to add a user to a project -def test_add_user_to_project(): - # Log in as a site admin - admin_username = randomString() - admin_password = "admin_password" - dprint( - "Registering with username: ", admin_username, " and password: ", admin_password - ) - response = requests.post( - registerPath, json={"username": admin_username, "password": admin_password} - ) - dprint(response.text) - - admin_token = login(admin_username, admin_password).json()["token"] - response = requests.post( - promoteToAdminPath, - json={"username": admin_username}, - headers={"Authorization": "Bearer " + admin_token}, - ) - dprint(response.text) - assert response.status_code == 200, "Promote to site admin failed" - dprint("Admin promoted to site admin successfully") - - # Create a new user to add to the project - new_user = randomString() - register(new_user, "new_user_password") - - # Add the new user to the project as a member - response = requests.put( - addUserToProjectPath, - json={"projectName": projectName, "username": new_user, "role": "member"}, - headers={"Authorization": "Bearer " + admin_token}, - ) - - dprint(response.text) - assert response.status_code == 200, "Add user to project failed" - gprint("test_add_user_to_project successful") - -# Test function to sign a report -def test_sign_report(): - # Create a project manager user - project_manager = randomString() - register(project_manager, "project_manager_password") - - # Register an admin - admin_username = randomString() - admin_password = "admin_password2" - dprint( - "Registering with username: ", admin_username, " and password: ", admin_password - ) - response = requests.post( - registerPath, json={"username": admin_username, "password": admin_password} - ) - dprint(response.text) - - # Log in as the admin - admin_token = login(admin_username, admin_password).json()["token"] - response = requests.post( - promoteToAdminPath, - json={"username": admin_username}, - headers={"Authorization": "Bearer " + admin_token}, - ) - - response = requests.put( - addUserToProjectPath + "/" + projectName, - headers={"Authorization": "Bearer " + admin_token}, - params={"userName": project_manager} - ) - assert response.status_code == 200, "Add project manager to project failed" - dprint("Project manager added to project successfully") - - # Log in as the project manager - project_manager_token = login(project_manager, "project_manager_password").json()[ - "token" - ] - - # Submit a report for the project - token = login(username, "always_same").json()["token"] - response = requests.post( - submitReportPath, - json={ - "projectName": projectName, - "week": 2, - "developmentTime": 10, - "meetingTime": 5, - "adminTime": 5, - "ownWorkTime": 10, - "studyTime": 10, - "testingTime": 10, - }, - headers={"Authorization": "Bearer " + token}, - ) - assert response.status_code == 200, "Submit report failed" - dprint("Submit report successful") - - # Retrieve the report ID - response = requests.get( - getWeeklyReportPath, - headers={"Authorization": "Bearer " + token}, - params={"username": username, "projectName": projectName, "week": 1}, - ) - dprint(response.text) - report_id = response.json()["reportId"] - - # Sign the report as the project manager - response = requests.put( - signReportPath + "/" + str(report_id), - headers={"Authorization": "Bearer " + project_manager_token}, - ) - assert response.status_code == 200, "Sign report failed" - dprint("Sign report successful") - - # Retrieve the report ID again for confirmation - response = requests.get( - getWeeklyReportPath, - headers={"Authorization": "Bearer " + token}, - params={"username": username, "projectName": projectName, "week": 1}, - ) - dprint(response.text) - gprint("test_sign_report successful") - -# Test function to get weekly reports for a user in a project -def test_get_weekly_reports_user(): - # Log in as the user - token = login(username, "always_same").json()["token"] - - # Get weekly reports for the user in the project - response = requests.get( - getWeeklyReportsUserPath + "/" + projectName, - headers={"Authorization": "Bearer " + token}, - ) - - dprint(response.text) - assert response.status_code == 200, "Get weekly reports for user failed" - gprint("test_get_weekly_reports_user successful") - - - -# Test function to check if a user is a project manager -def test_check_if_project_manager(): - # Log in as the user - token = login(username, "always_same").json()["token"] - - # Check if the user is a project manager for the project - response = requests.get( - checkIfProjectManagerPath + "/" + projectName, - headers={"Authorization": "Bearer " + token}, - ) - - dprint(response.text) - assert response.status_code == 200, "Check if project manager failed" - gprint("test_check_if_project_manager successful") - -def test_ensure_manager_of_created_project(): - # Create a new user to add to the project - newUser = "karen_" + randomString() - newProject = "HR_" + randomString() - register(newUser, "new_user_password") - token = login(newUser, "new_user_password").json()["token"] - - # Create a new project - response = requests.post( - addProjectPath, - json={"name": newProject, "description": "This is a project"}, - headers={"Authorization": "Bearer " + token}, - ) - assert response.status_code == 200, "Add project failed" - - response = requests.get( - checkIfProjectManagerPath + "/" + newProject, - headers={"Authorization": "Bearer " + token}, - ) - assert response.status_code == 200, "Check if project manager failed" - assert response.json()["isProjectManager"] == True, "User is not project manager" - gprint("test_ensure_admin_of_created_project successful") - -def test_change_user_name(): - # Register a new user - new_user = randomString() - register(new_user, "password") - - # Log in as the new user - token = login(new_user, "password").json()["token"] - - # Register a new admin - admin_username = randomString() - admin_password = "admin_password" - dprint( - "Registering with username: ", admin_username, " and password: ", admin_password - ) - response = requests.post( - registerPath, json={"username": admin_username, "password": admin_password} - ) - admin_token = login(admin_username, admin_password).json()["token"] - - # Promote to admin - response = requests.post( - promoteToAdminPath, - json={"username": admin_username}, - headers={"Authorization": "Bearer " + admin_token}, - ) - - # Change the user's name - response = requests.put( - getChangeUserNamePath, - json={"prevName": new_user, "newName": randomString()}, - headers={"Authorization": "Bearer " + admin_token}, - ) - - # Check if the change was successful - assert response.status_code == 200, "Change user name failed" - gprint("test_change_user_name successful") - -def test_list_all_users_project(): - # Log in as a user who is a member of the project - admin_username = randomString() - admin_password = "admin_password2" - dprint( - "Registering with username: ", admin_username, " and password: ", admin_password - ) - response = requests.post( - registerPath, json={"username": admin_username, "password": admin_password} - ) - dprint(response.text) - - # Log in as the admin - admin_token = login(admin_username, admin_password).json()["token"] - response = requests.post( - promoteToAdminPath, - json={"username": admin_username}, - headers={"Authorization": "Bearer " + admin_token}, - ) - - # Make a request to list all users associated with the project - response = requests.get( - getUsersProjectPath + "/" + projectName, - headers={"Authorization": "Bearer " + admin_token}, - ) - assert response.status_code == 200, "List all users project failed" - gprint("test_list_all_users_project sucessful") - -def test_update_weekly_report(): - # Log in as the user - token = login(username, "always_same").json()["token"] - - # Prepare the JSON data for updating the weekly report - update_data = { - "projectName": projectName, - "userName": username, - "week": 1, - "developmentTime": 8, - "meetingTime": 6, - "adminTime": 4, - "ownWorkTime": 11, - "studyTime": 8, - "testingTime": 18, - } - - # Send a request to update the weekly report - response = requests.put( - getUpdateWeeklyReportPath, - json=update_data, - headers={"Authorization": "Bearer " + token}, - ) - - # Check if the update was successful - assert response.status_code == 200, "Update weekly report failed" - gprint("test_update_weekly_report successful") - - -def test_remove_project(): - admin_username = randomString() - admin_password = "admin_password2" - dprint( - "Registering with username: ", admin_username, " and password: ", admin_password - ) - response = requests.post( - registerPath, json={"username": admin_username, "password": admin_password} - ) - dprint(response.text) - - # Log in as the admin - admin_token = login(admin_username, admin_password).json()["token"] - response = requests.post( - promoteToAdminPath, - json={"username": admin_username}, - headers={"Authorization": "Bearer " + admin_token}, - ) - - # Create a new project - new_project = randomString() - response = requests.post( - addProjectPath, - json={"name": new_project, "description": "This is a project"}, - headers={"Authorization": "Bearer " + admin_token}, - ) - assert response.status_code == 200, "Add project failed" - - # Remove the project - response = requests.delete( - removeProjectPath + "/" + new_project, - headers={"Authorization": "Bearer " + admin_token}, - ) - assert response.status_code == 200, "Remove project failed" - gprint("test_remove_project successful") - -def test_get_unsigned_reports(): - # Log in as the user - token = login("user2", "123").json()["token"] - - # Make a request to get all unsigned reports - response = requests.get( - getUnsignedReportsPath + "/" + projectName, - headers={"Authorization": "Bearer " + token}, - ) - assert response.status_code == 200, "Get unsigned reports failed" - gprint("test_get_unsigned_reports successful") - -def test_get_other_users_report_as_pm(): - # Create user - user = randomString() - register(user, "password") - - # Create project - project = randomString() - pm_token = login(user, "password").json()["token"] - response = requests.post( - addProjectPath, - json={"name": project, "description": "This is a project"}, - headers={"Authorization": "Bearer " + pm_token}, - ) - assert response.status_code == 200, "Add project failed" - - # Create other user - other_user = randomString() - register(other_user, "password") - user_token = login(other_user, "password").json()["token"] - - # Add other user to project - response = requests.put( - addUserToProjectPath + "/" + project, - headers={"Authorization": "Bearer " + pm_token}, # note pm_token - params={"userName": other_user}, - ) - assert response.status_code == 200, "Add user to project failed" - - # Submit report as other user - response = requests.post( - submitReportPath, - json={ - "projectName": project, - "week": 1, - "developmentTime": 10, - "meetingTime": 5, - "adminTime": 5, - "ownWorkTime": 10, - "studyTime": 10, - "testingTime": 10, - }, - headers={"Authorization": "Bearer " + user_token}, - ) - assert response.status_code == 200, "Submit report failed" - - # Get report as project manager - response = requests.get( - getWeeklyReportPath, - headers={"Authorization": "Bearer " + pm_token}, - params={"targetUser": other_user, "projectName": project, "week": 1}, - ) - assert response.status_code == 200, "Get weekly report failed" - -if __name__ == "__main__": - test_remove_project() - test_get_user_projects() - test_create_user() - test_login() - test_add_project() - test_submit_report() - test_get_weekly_report() - test_get_project() - test_sign_report() - test_add_user_to_project() - test_get_weekly_reports_user() - test_check_if_project_manager() - test_ProjectRoleChange() - test_ensure_manager_of_created_project() - test_get_unsigned_reports() - test_list_all_users_project() - test_change_user_name() - test_update_weekly_report() - test_get_other_users_report_as_pm() - \ No newline at end of file From 8ea6dec346ee3def8458f185300233118e3d6e27 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 15:53:36 +0200 Subject: [PATCH 06/45] Fixes for various paths --- .../handlers/projects/AddUserToProject.go | 33 +++++++------------ .../handlers/reports/GetWeeklyReport.go | 24 ++++++++++++-- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/backend/internal/handlers/projects/AddUserToProject.go b/backend/internal/handlers/projects/AddUserToProject.go index 702b7dd..3195314 100644 --- a/backend/internal/handlers/projects/AddUserToProject.go +++ b/backend/internal/handlers/projects/AddUserToProject.go @@ -10,42 +10,33 @@ import ( // AddUserToProjectHandler is a handler that adds a user to a project with a specified role func AddUserToProjectHandler(c *fiber.Ctx) error { - // Extract necessary parameters from the request - var requestData struct { - Username string `json:"username"` - ProjectName string `json:"projectName"` - Role string `json:"role"` - } - if err := c.BodyParser(&requestData); err != nil { - log.Info("Error parsing request body:", err) - return c.Status(400).SendString("Bad request") - } - - // Check if the user adding another user to the project is a site admin user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) - adminUsername := claims["name"].(string) - log.Info("Admin username from claims:", adminUsername) + pm_name := claims["name"].(string) - isAdmin, err := db.GetDb(c).IsSiteAdmin(adminUsername) + 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 admin status:", err) + log.Info("Error checking if user is project manager:", err) return c.Status(500).SendString(err.Error()) } - if !isAdmin { - log.Info("User is not a site admin:", adminUsername) - return c.Status(403).SendString("User is not a site admin") + 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") } // Add the user to the project with the specified role - err = db.GetDb(c).AddUserToProject(requestData.Username, requestData.ProjectName, requestData.Role) + err = db.GetDb(c).AddUserToProject(username, project, "member") if err != nil { log.Info("Error adding user to project:", err) return c.Status(500).SendString(err.Error()) } // Return success message - log.Info("User added to project successfully:", requestData.Username) + log.Info("User : ", username, " added to project: ", project) return c.SendStatus(fiber.StatusOK) } diff --git a/backend/internal/handlers/reports/GetWeeklyReport.go b/backend/internal/handlers/reports/GetWeeklyReport.go index 422bc0b..04bdc0d 100644 --- a/backend/internal/handlers/reports/GetWeeklyReport.go +++ b/backend/internal/handlers/reports/GetWeeklyReport.go @@ -16,11 +16,17 @@ func GetWeeklyReport(c *fiber.Ctx) error { claims := user.Claims.(jwt.MapClaims) username := claims["name"].(string) - log.Info("Getting weekly report for: ", username) - // Extract project name and week from query parameters projectName := c.Query("projectName") week := c.Query("week") + target_user := c.Query("targetUser") // The user whose report is being requested + + // If the target user is not empty, use it as the username + if target_user == "" { + target_user = username + } + + log.Info(username, " trying to get weekly report for: ", target_user) if projectName == "" || week == "" { log.Info("Missing project name or week number") @@ -34,8 +40,20 @@ func GetWeeklyReport(c *fiber.Ctx) error { return c.Status(400).SendString("Invalid week number") } + // If the token user is not an admin, check if the target user is the same as the token user + pm, err := db.GetDb(c).IsProjectManager(username, projectName) + if err != nil { + log.Info("Error checking if user is project manager:", err) + return c.Status(500).SendString(err.Error()) + } + + if pm == false && target_user != username { + log.Info("Unauthorized access") + return c.Status(403).SendString("Unauthorized access") + } + // Call the database function to get the weekly report - report, err := db.GetDb(c).GetWeeklyReport(username, projectName, weekInt) + report, err := db.GetDb(c).GetWeeklyReport(target_user, projectName, weekInt) if err != nil { log.Info("Error getting weekly report from db:", err) return c.Status(500).SendString(err.Error()) From ffe5d53625796a2b66cd8dbdb1bbb21a88e3e46e Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 15:53:52 +0200 Subject: [PATCH 07/45] Splitting test script --- testing/helpers.py | 121 ++++++++++ testing/testing.py | 540 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 661 insertions(+) create mode 100644 testing/helpers.py create mode 100644 testing/testing.py diff --git a/testing/helpers.py b/testing/helpers.py new file mode 100644 index 0000000..1dba7f0 --- /dev/null +++ b/testing/helpers.py @@ -0,0 +1,121 @@ +import requests +import string +import random + +# Helper function for the TTime API testing suite + +# For style guide, see: +# https://peps.python.org/pep-0008/#function-and-variable-names +# https://google.github.io/styleguide/pyguide.html#316-naming + +################## +## Static Paths ## +################## + +base_url = "http://localhost:8080" + +registerPath = base_url + "/api/register" +loginPath = base_url + "/api/login" +addProjectPath = base_url + "/api/project" +submitReportPath = base_url + "/api/submitWeeklyReport" +getWeeklyReportPath = base_url + "/api/getWeeklyReport" +getProjectPath = base_url + "/api/project" +signReportPath = base_url + "/api/signReport" +addUserToProjectPath = base_url + "/api/addUserToProject" +promoteToAdminPath = base_url + "/api/promoteToAdmin" +getUserProjectsPath = base_url + "/api/getUserProjects" +getWeeklyReportsUserPath = base_url + "/api/getWeeklyReportsUser" +checkIfProjectManagerPath = base_url + "/api/checkIfProjectManager" +ProjectRoleChangePath = base_url + "/api/ProjectRoleChange" +getUsersProjectPath = base_url + "/api/getUsersProject" +getUnsignedReportsPath = base_url + "/api/getUnsignedReports" +getChangeUserNamePath = base_url + "/api/changeUserName" +getUpdateWeeklyReportPath = base_url + "/api/updateWeeklyReport" +removeProjectPath = base_url + "/api/removeProject" +promoteToPmPath = base_url + "/api/promoteToPm" + +debug_output = False + + +def gprint(*args, **kwargs): + print("\033[92m", *args, "\033[00m", **kwargs) + + +def dprint(*args, **kwargs): + if debug_output: + print(*args, **kwargs) + + +def randomString(len=10): + """Generate a random string of fixed length""" + letters = string.ascii_lowercase + return "".join(random.choice(letters) for i in range(len)) + + +############ ############ ############ ############ ############ + + +# Posts the username and password to the register endpoint +def register(username: string, password: string): + dprint("Registering with username: ", username, " and password: ", password) + response = requests.post( + registerPath, json={"username": username, "password": password} + ) + dprint(response.text) + return response + + +# Posts the username and password to the login endpoint +def login(username: string, password: string): + dprint("Logging in with username: ", username, " and password: ", password) + response = requests.post( + loginPath, json={"username": username, "password": password} + ) + dprint(response.text) + return response + + +# Register a user and return the token +def register_and_login(username: string, password: string) -> string: + register(username, password) + response = login(username, password) + return response.json()["token"] + + +def create_project(token: string, project_name: string, description: string = "Test description"): + dprint("Creating project with name: ", project_name) + response = requests.post( + addProjectPath, + headers={"Authorization": "Bearer " + token}, + json={"name": project_name, "description": description}, + ) + dprint(response.text) + return response + + +# Add a user to a project, requires the user withing the token to be a project manager of said project +def addToProject(token: string, username: string, project_name: string): + dprint("Adding user with username: ", username, " to project: ", project_name) + response = requests.put( + addUserToProjectPath + "/" + project_name, + headers={"Authorization": "Bearer " + token}, + params={"userName": username}, + ) + dprint(response.text) + return response + + +def promoteToManager(token: string, username: string, project_name: string): + dprint( + "Promoting user with username: ", + username, + " to project manager of project: ", + project_name, + ) + response = requests.put( + promoteToPmPath + "/" + project_name, + headers={"Authorization": "Bearer " + token}, + params={"userName": username}, + ) + dprint(response.text) + return response diff --git a/testing/testing.py b/testing/testing.py new file mode 100644 index 0000000..7cc5e9d --- /dev/null +++ b/testing/testing.py @@ -0,0 +1,540 @@ +import requests +import string + +# This modules contains helper functions for the tests +from helpers import * + +print("Running Tests...") + +# Defined once per test run +username = "user_" + randomString() +projectName = "project_" + randomString() + +#ta bort auth i handlern för att få testet att gå igenom +def test_ProjectRoleChange(): + dprint("Testing ProjectRoleChange") + localUsername = randomString() + localProjectName = randomString() + register(localUsername, "username_password") + + token = login(localUsername, "username_password").json()[ + "token" + ] + + # Just checking since this test is built somewhat differently than the others + assert token != None, "Login failed" + + response = requests.post( + addProjectPath, + json={"name": localProjectName, "description": "This is a project"}, + headers={"Authorization": "Bearer " + token}, + ) + + if response.status_code != 200: + print("Add project failed") + + response = requests.post( + ProjectRoleChangePath, + headers={"Authorization": "Bearer " + token}, + json={ + "projectName": localProjectName, + "role": "project_manager", + }, + ) + + assert response.status_code == 200, "ProjectRoleChange failed" + gprint("test_ProjectRoleChange successful") + + +def test_get_user_projects(): + username = "user2" + password = "123" + + dprint("Testing get user projects") + loginResponse = login(username, password) + # Check if the user is added to the project + response = requests.get( + getUserProjectsPath + "/" + username, + headers={"Authorization": "Bearer " + loginResponse.json()["token"]}, + ) + dprint(response.text) + dprint(response.json()) + assert response.status_code == 200, "Get user projects failed" + gprint("test_get_user_projects successful") + + +# Test function to login +def test_login(): + response = login(username, "always_same") + assert response.status_code == 200, "Login failed" + dprint("Login successful") + gprint("test_login successful") + return response.json()["token"] + + +# Test function to create a new user +def test_create_user(): + response = register(username, "always_same") + assert response.status_code == 200, "Registration failed" + gprint("test_create_user successful") + +# Test function to add a project +def test_add_project(): + loginResponse = login(username, "always_same") + token = loginResponse.json()["token"] + response = requests.post( + addProjectPath, + json={"name": projectName, "description": "This is a project"}, + headers={"Authorization": "Bearer " + token}, + ) + dprint(response.text) + assert response.status_code == 200, "Add project failed" + gprint("test_add_project successful") + +# Test function to submit a report +def test_submit_report(): + token = login(username, "always_same").json()["token"] + response = requests.post( + submitReportPath, + json={ + "projectName": projectName, + "week": 1, + "developmentTime": 10, + "meetingTime": 5, + "adminTime": 5, + "ownWorkTime": 10, + "studyTime": 10, + "testingTime": 10, + }, + headers={"Authorization": "Bearer " + token}, + ) + dprint(response.text) + assert response.status_code == 200, "Submit report failed" + gprint("test_submit_report successful") + +# Test function to get a weekly report +def test_get_weekly_report(): + token = login(username, "always_same").json()["token"] + response = requests.get( + getWeeklyReportPath, + headers={"Authorization": "Bearer " + token}, + params={"username": username, "projectName": projectName, "week": 1}, + ) + dprint(response.text) + assert response.status_code == 200, "Get weekly report failed" + gprint("test_get_weekly_report successful") + + +# Tests getting a project by id +def test_get_project(): + token = login(username, "always_same").json()["token"] + response = requests.get( + getProjectPath + "/1", # Assumes that the project with id 1 exists + headers={"Authorization": "Bearer " + token}, + ) + dprint(response.text) + assert response.status_code == 200, "Get project failed" + gprint("test_get_project successful") + + +# Test function to add a user to a project +def test_add_user_to_project(): + # User to create + pm_user = "user"+randomString() + pm_passwd = "password" + + # User to add + member_user = "member"+randomString() + member_passwd = "password" + + # Name of the project to be created + project_name = "project"+randomString() + + pm_token = register_and_login(pm_user, pm_passwd) + register(member_user, member_passwd) + + response = create_project(pm_token, project_name) + assert response.status_code == 200, "Create project failed" + + # Promote the user to project manager + response = addToProject(pm_token, member_user, project_name) + assert response.status_code == 200, "Add user to project failed" + +# Test function to sign a report +def test_sign_report(): + # Create a project manager user + project_manager = randomString() + register(project_manager, "project_manager_password") + + # Register an admin + admin_username = randomString() + admin_password = "admin_password2" + dprint( + "Registering with username: ", admin_username, " and password: ", admin_password + ) + response = requests.post( + registerPath, json={"username": admin_username, "password": admin_password} + ) + dprint(response.text) + + # Log in as the admin + admin_token = login(admin_username, admin_password).json()["token"] + response = requests.post( + promoteToAdminPath, + json={"username": admin_username}, + headers={"Authorization": "Bearer " + admin_token}, + ) + + response = requests.put( + addUserToProjectPath + "/" + projectName, + headers={"Authorization": "Bearer " + admin_token}, + params={"userName": project_manager} + ) + assert response.status_code == 200, "Add project manager to project failed" + dprint("Project manager added to project successfully") + + # Log in as the project manager + project_manager_token = login(project_manager, "project_manager_password").json()[ + "token" + ] + + # Submit a report for the project + token = login(username, "always_same").json()["token"] + response = requests.post( + submitReportPath, + json={ + "projectName": projectName, + "week": 2, + "developmentTime": 10, + "meetingTime": 5, + "adminTime": 5, + "ownWorkTime": 10, + "studyTime": 10, + "testingTime": 10, + }, + headers={"Authorization": "Bearer " + token}, + ) + assert response.status_code == 200, "Submit report failed" + dprint("Submit report successful") + + # Retrieve the report ID + response = requests.get( + getWeeklyReportPath, + headers={"Authorization": "Bearer " + token}, + params={"username": username, "projectName": projectName, "week": 1}, + ) + dprint(response.text) + report_id = response.json()["reportId"] + + # Sign the report as the project manager + response = requests.put( + signReportPath + "/" + str(report_id), + headers={"Authorization": "Bearer " + project_manager_token}, + ) + assert response.status_code == 200, "Sign report failed" + dprint("Sign report successful") + + # Retrieve the report ID again for confirmation + response = requests.get( + getWeeklyReportPath, + headers={"Authorization": "Bearer " + token}, + params={"username": username, "projectName": projectName, "week": 1}, + ) + dprint(response.text) + gprint("test_sign_report successful") + +# Test function to get weekly reports for a user in a project +def test_get_weekly_reports_user(): + # Log in as the user + token = login(username, "always_same").json()["token"] + + # Get weekly reports for the user in the project + response = requests.get( + getWeeklyReportsUserPath + "/" + projectName, + headers={"Authorization": "Bearer " + token}, + ) + + dprint(response.text) + assert response.status_code == 200, "Get weekly reports for user failed" + gprint("test_get_weekly_reports_user successful") + + + +# Test function to check if a user is a project manager +def test_check_if_project_manager(): + # Log in as the user + token = login(username, "always_same").json()["token"] + + # Check if the user is a project manager for the project + response = requests.get( + checkIfProjectManagerPath + "/" + projectName, + headers={"Authorization": "Bearer " + token}, + ) + + dprint(response.text) + assert response.status_code == 200, "Check if project manager failed" + gprint("test_check_if_project_manager successful") + +def test_ensure_manager_of_created_project(): + # Create a new user to add to the project + newUser = "karen_" + randomString() + newProject = "HR_" + randomString() + register(newUser, "new_user_password") + token = login(newUser, "new_user_password").json()["token"] + + # Create a new project + response = requests.post( + addProjectPath, + json={"name": newProject, "description": "This is a project"}, + headers={"Authorization": "Bearer " + token}, + ) + assert response.status_code == 200, "Add project failed" + + response = requests.get( + checkIfProjectManagerPath + "/" + newProject, + headers={"Authorization": "Bearer " + token}, + ) + assert response.status_code == 200, "Check if project manager failed" + assert response.json()["isProjectManager"] == True, "User is not project manager" + gprint("test_ensure_admin_of_created_project successful") + +def test_change_user_name(): + # Register a new user + new_user = randomString() + register(new_user, "password") + + # Log in as the new user + token = login(new_user, "password").json()["token"] + + # Register a new admin + admin_username = randomString() + admin_password = "admin_password" + dprint( + "Registering with username: ", admin_username, " and password: ", admin_password + ) + response = requests.post( + registerPath, json={"username": admin_username, "password": admin_password} + ) + admin_token = login(admin_username, admin_password).json()["token"] + + # Promote to admin + response = requests.post( + promoteToAdminPath, + json={"username": admin_username}, + headers={"Authorization": "Bearer " + admin_token}, + ) + + # Change the user's name + response = requests.put( + getChangeUserNamePath, + json={"prevName": new_user, "newName": randomString()}, + headers={"Authorization": "Bearer " + admin_token}, + ) + + # Check if the change was successful + assert response.status_code == 200, "Change user name failed" + gprint("test_change_user_name successful") + +def test_list_all_users_project(): + # Log in as a user who is a member of the project + admin_username = randomString() + admin_password = "admin_password2" + dprint( + "Registering with username: ", admin_username, " and password: ", admin_password + ) + response = requests.post( + registerPath, json={"username": admin_username, "password": admin_password} + ) + dprint(response.text) + + # Log in as the admin + admin_token = login(admin_username, admin_password).json()["token"] + response = requests.post( + promoteToAdminPath, + json={"username": admin_username}, + headers={"Authorization": "Bearer " + admin_token}, + ) + + # Make a request to list all users associated with the project + response = requests.get( + getUsersProjectPath + "/" + projectName, + headers={"Authorization": "Bearer " + admin_token}, + ) + assert response.status_code == 200, "List all users project failed" + gprint("test_list_all_users_project sucessful") + +def test_update_weekly_report(): + # Log in as the user + token = login(username, "always_same").json()["token"] + + # Prepare the JSON data for updating the weekly report + update_data = { + "projectName": projectName, + "userName": username, + "week": 1, + "developmentTime": 8, + "meetingTime": 6, + "adminTime": 4, + "ownWorkTime": 11, + "studyTime": 8, + "testingTime": 18, + } + + # Send a request to update the weekly report + response = requests.put( + getUpdateWeeklyReportPath, + json=update_data, + headers={"Authorization": "Bearer " + token}, + ) + + # Check if the update was successful + assert response.status_code == 200, "Update weekly report failed" + gprint("test_update_weekly_report successful") + + +def test_remove_project(): + admin_username = randomString() + admin_password = "admin_password2" + dprint( + "Registering with username: ", admin_username, " and password: ", admin_password + ) + response = requests.post( + registerPath, json={"username": admin_username, "password": admin_password} + ) + dprint(response.text) + + # Log in as the admin + admin_token = login(admin_username, admin_password).json()["token"] + response = requests.post( + promoteToAdminPath, + json={"username": admin_username}, + headers={"Authorization": "Bearer " + admin_token}, + ) + + # Create a new project + new_project = randomString() + response = requests.post( + addProjectPath, + json={"name": new_project, "description": "This is a project"}, + headers={"Authorization": "Bearer " + admin_token}, + ) + assert response.status_code == 200, "Add project failed" + + # Remove the project + response = requests.delete( + removeProjectPath + "/" + new_project, + headers={"Authorization": "Bearer " + admin_token}, + ) + assert response.status_code == 200, "Remove project failed" + gprint("test_remove_project successful") + +def test_get_unsigned_reports(): + # Log in as the user + token = login("user2", "123").json()["token"] + + # Make a request to get all unsigned reports + response = requests.get( + getUnsignedReportsPath + "/" + projectName, + headers={"Authorization": "Bearer " + token}, + ) + assert response.status_code == 200, "Get unsigned reports failed" + gprint("test_get_unsigned_reports successful") + +def test_get_other_users_report_as_pm(): + # Create user + user = randomString() + register(user, "password") + + # Create project + project = randomString() + pm_token = login(user, "password").json()["token"] + response = requests.post( + addProjectPath, + json={"name": project, "description": "This is a project"}, + headers={"Authorization": "Bearer " + pm_token}, + ) + assert response.status_code == 200, "Add project failed" + + # Create other user + other_user = randomString() + register(other_user, "password") + user_token = login(other_user, "password").json()["token"] + + # Add other user to project + response = requests.put( + addUserToProjectPath + "/" + project, + headers={"Authorization": "Bearer " + pm_token}, # note pm_token + params={"userName": other_user}, + ) + assert response.status_code == 200, "Add user to project failed" + + # Submit report as other user + response = requests.post( + submitReportPath, + json={ + "projectName": project, + "week": 1, + "developmentTime": 10, + "meetingTime": 5, + "adminTime": 5, + "ownWorkTime": 10, + "studyTime": 10, + "testingTime": 10, + }, + headers={"Authorization": "Bearer " + user_token}, + ) + assert response.status_code == 200, "Submit report failed" + + # Get report as project manager + response = requests.get( + getWeeklyReportPath, + headers={"Authorization": "Bearer " + pm_token}, + params={"targetUser": other_user, "projectName": project, "week": 1}, + ) + assert response.status_code == 200, "Get weekly report failed" + +def test_promote_to_manager(): + # User to create + pm_user = "user"+randomString() + pm_passwd = "password" + + # User to promote + member_user = "member"+randomString() + member_passwd = "password" + + # Name of the project to be created + project_name = "project"+randomString() + + pm_token = register_and_login(pm_user, pm_passwd) + member_token = register_and_login(member_user, member_passwd) + + response = create_project(pm_token, project_name) + assert response.status_code == 200, "Create project failed" + + # Promote the user to project manager + response = promoteToManager(pm_token, member_user, project_name) + assert response.status_code == 200, "Promote to manager failed" + + +if __name__ == "__main__": + test_promote_to_manager() + test_remove_project() + test_get_user_projects() + test_create_user() + test_login() + test_add_project() + test_submit_report() + test_get_weekly_report() + test_get_project() + #test_sign_report() + test_add_user_to_project() + test_get_weekly_reports_user() + test_check_if_project_manager() + test_ProjectRoleChange() + test_ensure_manager_of_created_project() + test_get_unsigned_reports() + test_list_all_users_project() + test_change_user_name() + test_update_weekly_report() + test_get_other_users_report_as_pm() + \ No newline at end of file From 7c973009ac1eec2318a4908d7c600a02da0ba9cb Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 15:54:27 +0200 Subject: [PATCH 08/45] Adding pycache to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 281e866..c50fe24 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ diagram.puml backend/*.png backend/*.jpg backend/*.svg +__pycache__ /go.work.sum /package-lock.json From 903132e56d918654ad40b15a94409158f69c48d0 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:06:08 +0200 Subject: [PATCH 09/45] Polishing tests --- testing/helpers.py | 34 ++++++++++- testing/testing.py | 148 +++++++++++++++++++-------------------------- 2 files changed, 95 insertions(+), 87 deletions(-) diff --git a/testing/helpers.py b/testing/helpers.py index 1dba7f0..20b00b1 100644 --- a/testing/helpers.py +++ b/testing/helpers.py @@ -1,6 +1,7 @@ import requests import string import random +import json # Helper function for the TTime API testing suite @@ -34,7 +35,7 @@ getUpdateWeeklyReportPath = base_url + "/api/updateWeeklyReport" removeProjectPath = base_url + "/api/removeProject" promoteToPmPath = base_url + "/api/promoteToPm" -debug_output = False +debug_output = True def gprint(*args, **kwargs): @@ -82,7 +83,9 @@ def register_and_login(username: string, password: string) -> string: return response.json()["token"] -def create_project(token: string, project_name: string, description: string = "Test description"): +def create_project( + token: string, project_name: string, description: string = "Test description" +): dprint("Creating project with name: ", project_name) response = requests.post( addProjectPath, @@ -119,3 +122,30 @@ def promoteToManager(token: string, username: string, project_name: string): ) dprint(response.text) return response + + +def submitReport(token: string, report): + dprint("Submitting report: ", report) + response = requests.post( + submitReportPath, + json=report, + headers={"Authorization": "Bearer " + token}, + ) + return response + + +def getReport(token: string, username: string, projectName: string): + # Retrieve the report ID + response = requests.get( + getWeeklyReportPath, + headers={"Authorization": "Bearer " + token}, + params={"username": username, "projectName": projectName, "week": 1}, + ) + return response.json() + + +def signReport(project_manager_token: string, report_id: int): + return requests.put( + signReportPath + "/" + str(report_id), + headers={"Authorization": "Bearer " + project_manager_token}, + ) diff --git a/testing/testing.py b/testing/testing.py index 7cc5e9d..a2dfb64 100644 --- a/testing/testing.py +++ b/testing/testing.py @@ -1,5 +1,4 @@ import requests -import string # This modules contains helper functions for the tests from helpers import * @@ -10,16 +9,15 @@ print("Running Tests...") username = "user_" + randomString() projectName = "project_" + randomString() -#ta bort auth i handlern för att få testet att gå igenom + +# ta bort auth i handlern för att få testet att gå igenom def test_ProjectRoleChange(): dprint("Testing ProjectRoleChange") localUsername = randomString() localProjectName = randomString() register(localUsername, "username_password") - token = login(localUsername, "username_password").json()[ - "token" - ] + token = login(localUsername, "username_password").json()["token"] # Just checking since this test is built somewhat differently than the others assert token != None, "Login failed" @@ -44,14 +42,14 @@ def test_ProjectRoleChange(): assert response.status_code == 200, "ProjectRoleChange failed" gprint("test_ProjectRoleChange successful") - + def test_get_user_projects(): username = "user2" password = "123" dprint("Testing get user projects") - loginResponse = login(username, password) + loginResponse = login(username, password) # Check if the user is added to the project response = requests.get( getUserProjectsPath + "/" + username, @@ -78,6 +76,7 @@ def test_create_user(): assert response.status_code == 200, "Registration failed" gprint("test_create_user successful") + # Test function to add a project def test_add_project(): loginResponse = login(username, "always_same") @@ -91,6 +90,7 @@ def test_add_project(): assert response.status_code == 200, "Add project failed" gprint("test_add_project successful") + # Test function to submit a report def test_submit_report(): token = login(username, "always_same").json()["token"] @@ -112,6 +112,7 @@ def test_submit_report(): assert response.status_code == 200, "Submit report failed" gprint("test_submit_report successful") + # Test function to get a weekly report def test_get_weekly_report(): token = login(username, "always_same").json()["token"] @@ -140,15 +141,15 @@ def test_get_project(): # Test function to add a user to a project def test_add_user_to_project(): # User to create - pm_user = "user"+randomString() + pm_user = "user" + randomString() pm_passwd = "password" # User to add - member_user = "member"+randomString() + member_user = "member" + randomString() member_passwd = "password" # Name of the project to be created - project_name = "project"+randomString() + project_name = "project" + randomString() pm_token = register_and_login(pm_user, pm_passwd) register(member_user, member_passwd) @@ -160,51 +161,37 @@ def test_add_user_to_project(): response = addToProject(pm_token, member_user, project_name) assert response.status_code == 200, "Add user to project failed" + # Test function to sign a report def test_sign_report(): - # Create a project manager user - project_manager = randomString() - register(project_manager, "project_manager_password") + # Pm user + pm_username = "pm" + randomString() + pm_password = "admin_password2" - # Register an admin - admin_username = randomString() - admin_password = "admin_password2" - dprint( - "Registering with username: ", admin_username, " and password: ", admin_password - ) - response = requests.post( - registerPath, json={"username": admin_username, "password": admin_password} - ) - dprint(response.text) + # User to add + member_user = "member" + randomString() + member_passwd = "password" - # Log in as the admin - admin_token = login(admin_username, admin_password).json()["token"] - response = requests.post( - promoteToAdminPath, - json={"username": admin_username}, - headers={"Authorization": "Bearer " + admin_token}, - ) + # Name of the project to be created + project_name = "project" + randomString() - response = requests.put( - addUserToProjectPath + "/" + projectName, - headers={"Authorization": "Bearer " + admin_token}, - params={"userName": project_manager} - ) - assert response.status_code == 200, "Add project manager to project failed" - dprint("Project manager added to project successfully") + # Register and get the tokens for both users + pm_token = register_and_login(pm_username, pm_password) + member_token = register_and_login(member_user, member_passwd) - # Log in as the project manager - project_manager_token = login(project_manager, "project_manager_password").json()[ - "token" - ] + # Create the project + response = create_project(pm_token, project_name) + assert response.status_code == 200, "Create project failed" + + # Add the user to the project + response = addToProject(pm_token, member_user, project_name) # Submit a report for the project - token = login(username, "always_same").json()["token"] - response = requests.post( - submitReportPath, - json={ - "projectName": projectName, - "week": 2, + response = submitReport( + member_token, + { + "projectName": project_name, + "week": 1, "developmentTime": 10, "meetingTime": 5, "adminTime": 5, @@ -212,37 +199,23 @@ def test_sign_report(): "studyTime": 10, "testingTime": 10, }, - headers={"Authorization": "Bearer " + token}, ) assert response.status_code == 200, "Submit report failed" - dprint("Submit report successful") # Retrieve the report ID - response = requests.get( - getWeeklyReportPath, - headers={"Authorization": "Bearer " + token}, - params={"username": username, "projectName": projectName, "week": 1}, - ) - dprint(response.text) - report_id = response.json()["reportId"] + report_id = getReport(member_token, member_user, project_name)["reportId"] # Sign the report as the project manager - response = requests.put( - signReportPath + "/" + str(report_id), - headers={"Authorization": "Bearer " + project_manager_token}, - ) + response = signReport(pm_token, report_id) assert response.status_code == 200, "Sign report failed" dprint("Sign report successful") # Retrieve the report ID again for confirmation - response = requests.get( - getWeeklyReportPath, - headers={"Authorization": "Bearer " + token}, - params={"username": username, "projectName": projectName, "week": 1}, - ) - dprint(response.text) + report_id = getReport(member_token, member_user, project_name)["reportId"] + assert report_id != None, "Get report failed" gprint("test_sign_report successful") + # Test function to get weekly reports for a user in a project def test_get_weekly_reports_user(): # Log in as the user @@ -253,13 +226,12 @@ def test_get_weekly_reports_user(): getWeeklyReportsUserPath + "/" + projectName, headers={"Authorization": "Bearer " + token}, ) - + dprint(response.text) assert response.status_code == 200, "Get weekly reports for user failed" gprint("test_get_weekly_reports_user successful") - # Test function to check if a user is a project manager def test_check_if_project_manager(): # Log in as the user @@ -270,11 +242,12 @@ def test_check_if_project_manager(): checkIfProjectManagerPath + "/" + projectName, headers={"Authorization": "Bearer " + token}, ) - + dprint(response.text) assert response.status_code == 200, "Check if project manager failed" gprint("test_check_if_project_manager successful") + def test_ensure_manager_of_created_project(): # Create a new user to add to the project newUser = "karen_" + randomString() @@ -298,6 +271,7 @@ def test_ensure_manager_of_created_project(): assert response.json()["isProjectManager"] == True, "User is not project manager" gprint("test_ensure_admin_of_created_project successful") + def test_change_user_name(): # Register a new user new_user = randomString() @@ -317,7 +291,7 @@ def test_change_user_name(): ) admin_token = login(admin_username, admin_password).json()["token"] - # Promote to admin + # Promote to admin response = requests.post( promoteToAdminPath, json={"username": admin_username}, @@ -335,6 +309,7 @@ def test_change_user_name(): assert response.status_code == 200, "Change user name failed" gprint("test_change_user_name successful") + def test_list_all_users_project(): # Log in as a user who is a member of the project admin_username = randomString() @@ -358,11 +333,12 @@ def test_list_all_users_project(): # Make a request to list all users associated with the project response = requests.get( getUsersProjectPath + "/" + projectName, - headers={"Authorization": "Bearer " + admin_token}, + headers={"Authorization": "Bearer " + admin_token}, ) assert response.status_code == 200, "List all users project failed" gprint("test_list_all_users_project sucessful") + def test_update_weekly_report(): # Log in as the user token = login(username, "always_same").json()["token"] @@ -428,17 +404,19 @@ def test_remove_project(): assert response.status_code == 200, "Remove project failed" gprint("test_remove_project successful") + def test_get_unsigned_reports(): # Log in as the user - token = login("user2", "123").json()["token"] + token = login("user2", "123").json()["token"] + + # Make a request to get all unsigned reports + response = requests.get( + getUnsignedReportsPath + "/" + projectName, + headers={"Authorization": "Bearer " + token}, + ) + assert response.status_code == 200, "Get unsigned reports failed" + gprint("test_get_unsigned_reports successful") - # Make a request to get all unsigned reports - response = requests.get( - getUnsignedReportsPath + "/" + projectName, - headers={"Authorization": "Bearer " + token}, - ) - assert response.status_code == 200, "Get unsigned reports failed" - gprint("test_get_unsigned_reports successful") def test_get_other_users_report_as_pm(): # Create user @@ -463,7 +441,7 @@ def test_get_other_users_report_as_pm(): # Add other user to project response = requests.put( addUserToProjectPath + "/" + project, - headers={"Authorization": "Bearer " + pm_token}, # note pm_token + headers={"Authorization": "Bearer " + pm_token}, # note pm_token params={"userName": other_user}, ) assert response.status_code == 200, "Add user to project failed" @@ -493,17 +471,18 @@ def test_get_other_users_report_as_pm(): ) assert response.status_code == 200, "Get weekly report failed" + def test_promote_to_manager(): # User to create - pm_user = "user"+randomString() + pm_user = "user" + randomString() pm_passwd = "password" # User to promote - member_user = "member"+randomString() + member_user = "member" + randomString() member_passwd = "password" # Name of the project to be created - project_name = "project"+randomString() + project_name = "project" + randomString() pm_token = register_and_login(pm_user, pm_passwd) member_token = register_and_login(member_user, member_passwd) @@ -526,7 +505,7 @@ if __name__ == "__main__": test_submit_report() test_get_weekly_report() test_get_project() - #test_sign_report() + test_sign_report() test_add_user_to_project() test_get_weekly_reports_user() test_check_if_project_manager() @@ -537,4 +516,3 @@ if __name__ == "__main__": test_change_user_name() test_update_weekly_report() test_get_other_users_report_as_pm() - \ No newline at end of file From a1d2520d888556199c4a3457ffaa625479a2d1dc Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:12:29 +0200 Subject: [PATCH 10/45] Updating typescript api --- frontend/src/API/API.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index f8d3e5c..16aebff 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -99,16 +99,20 @@ interface API { token: string, ): Promise>; - /** Gets a weekly report for a specific user, project and week + /** Gets a weekly report for a specific user, project and week. + * Keep in mind that the user within the token needs to be PM + * of the project to get the report, unless the user is the target user. * @param {string} projectName The name of the project. * @param {string} week The week number. * @param {string} token The authentication token. + * @param {string} targetUser The username of the target user. Defaults to token user. * @returns {Promise>} A promise resolving to an API response with the retrieved report. */ getWeeklyReport( projectName: string, week: string, token: string, + targetUser?: string, ): Promise>; /** @@ -529,10 +533,11 @@ export const api: API = { projectName: string, week: string, token: string, + targetUser?: string, ): Promise> { try { const response = await fetch( - `/api/getWeeklyReport?projectName=${projectName}&week=${week}`, + `/api/getWeeklyReport?projectName=${projectName}&week=${week}&targetUser=${targetUser}`, { method: "GET", headers: { From a39cfedad3518a858b760d1f9592c6b8be2dc873 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:31:39 +0200 Subject: [PATCH 11/45] Rename, fix and testing for getAllWeeklyReports path --- backend/internal/database/db.go | 6 +-- backend/internal/database/db_test.go | 5 +-- .../reports/GetWeeklyReportsUserHandler.go | 40 ++++++++++++++----- backend/main.go | 2 +- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index f4c0f6e..6bf6fba 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -35,7 +35,7 @@ type Database interface { GetProject(projectId int) (types.Project, error) GetUserRole(username string, projectname string) (string, error) GetWeeklyReport(username string, projectName string, week int) (types.WeeklyReport, error) - GetWeeklyReportsUser(username string, projectname string) ([]types.WeeklyReportList, error) + GetAllWeeklyReports(username string, projectname string) ([]types.WeeklyReportList, error) GetUnsignedWeeklyReports(projectName string) ([]types.WeeklyReport, error) SignWeeklyReport(reportId int, projectManagerId int) error IsSiteAdmin(username string) (bool, error) @@ -463,8 +463,8 @@ func (d *Db) Migrate() error { return nil } -// GetWeeklyReportsUser retrieves weekly reports for a specific user and project. -func (d *Db) GetWeeklyReportsUser(username string, projectName string) ([]types.WeeklyReportList, error) { +// GetAllWeeklyReports retrieves weekly reports for a specific user and project. +func (d *Db) GetAllWeeklyReports(username string, projectName string) ([]types.WeeklyReportList, error) { query := ` SELECT wr.week, diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index fe3e6cd..a691a4d 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -705,7 +705,7 @@ func TestGetWeeklyReportsUser(t *testing.T) { t.Error("AddWeeklyReport failed:", err) } - reports, err := db.GetWeeklyReportsUser("testuser", "testproject") + reports, err := db.GetAllWeeklyReports("testuser", "testproject") if err != nil { t.Error("GetWeeklyReportsUser failed:", err) } @@ -962,6 +962,5 @@ func TestRemoveProject(t *testing.T) { if len(projects) != 0 { t.Error("RemoveProject failed: expected 0, got", len(projects)) } - + } - \ No newline at end of file diff --git a/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go b/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go index da8a90b..825c0cc 100644 --- a/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go +++ b/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go @@ -8,29 +8,49 @@ import ( "github.com/golang-jwt/jwt/v5" ) -// GetWeeklyReportsUserHandler retrieves all weekly reports for a user in a specific project -func GetWeeklyReportsUserHandler(c *fiber.Ctx) error { +// GetAllWeeklyReports retrieves all weekly reports for a user in a specific project +func GetAllWeeklyReports(c *fiber.Ctx) error { // Extract the necessary parameters from the token user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) username := claims["name"].(string) - // Extract necessary (path) parameters from the request + // Extract project name and week from query parameters projectName := c.Params("projectName") + target_user := c.Query("targetUser") // The user whose reports are being requested - // TODO: Here we need to check whether the user is a member of the project - // If not, we should return an error. On the other hand, if the user not a member, - // the returned list of reports will (should) allways be empty. + // If the target user is not empty, use it as the username + if target_user == "" { + target_user = username + } + + log.Info(username, " trying to get all weekly reports for: ", target_user) + + if projectName == "" { + log.Info("Missing project name") + return c.Status(400).SendString("Missing project name") + } + + // If the token user is not an admin, check if the target user is the same as the token user + pm, err := db.GetDb(c).IsProjectManager(username, projectName) + if err != nil { + log.Info("Error checking if user is project manager:", err) + return c.Status(500).SendString(err.Error()) + } + + if pm == false && target_user != username { + log.Info("Unauthorized access") + return c.Status(403).SendString("Unauthorized access") + } // Retrieve weekly reports for the user in the project from the database - reports, err := db.GetDb(c).GetWeeklyReportsUser(username, projectName) + reports, err := db.GetDb(c).GetAllWeeklyReports(username, projectName) if err != nil { log.Error("Error getting weekly reports for user:", username, "in project:", projectName, ":", err) return c.Status(500).SendString(err.Error()) } - log.Info("Returning weekly reports for user:", username, "in project:", projectName) - - // Return the list of reports as JSON + log.Info("Returning weekly report") + // Return the retrieved weekly report return c.JSON(reports) } diff --git a/backend/main.go b/backend/main.go index f811a58..b5ecacf 100644 --- a/backend/main.go +++ b/backend/main.go @@ -128,7 +128,7 @@ func main() { // reportGroup := api.Group("/report") // Not currently in use api.Get("/getWeeklyReport", reports.GetWeeklyReport) api.Get("/getUnsignedReports/:projectName", reports.GetUnsignedReports) - api.Get("/getWeeklyReportsUser/:projectName", reports.GetWeeklyReportsUserHandler) + api.Get("/getAllWeeklyReports/:projectName", reports.GetAllWeeklyReports) api.Post("/submitWeeklyReport", reports.SubmitWeeklyReport) api.Put("/signReport/:reportId", reports.SignReport) api.Put("/updateWeeklyReport", reports.UpdateWeeklyReport) From fcd035fe6e4cea059dfad4062d9c7528d569814d Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:32:07 +0200 Subject: [PATCH 12/45] TS Api for getAllWeeklyReport --- frontend/src/API/API.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 16aebff..c4cf445 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -100,7 +100,7 @@ interface API { ): Promise>; /** Gets a weekly report for a specific user, project and week. - * Keep in mind that the user within the token needs to be PM + * Keep in mind that the user within the token needs to be PM * of the project to get the report, unless the user is the target user. * @param {string} projectName The name of the project. * @param {string} week The week number. @@ -122,9 +122,10 @@ interface API { * @param {string} token The token of the user * @returns {APIResponse} A list of weekly reports */ - getWeeklyReportsForUser( + getAllWeeklyReportsForUser( projectName: string, token: string, + targetUser?: string, ): Promise>; /** Gets all the projects of a user @@ -558,18 +559,22 @@ export const api: API = { } }, - async getWeeklyReportsForUser( + async getAllWeeklyReportsForUser( projectName: string, token: string, + targetUser?: string, ): Promise> { try { - const response = await fetch(`/api/getWeeklyReportsUser/${projectName}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, + const response = await fetch( + `/api/getAllWeeklyReports/${projectName}?targetUser=${targetUser}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, }, - }); + ); if (!response.ok) { return { From 9c5aa1041443573be97c3cb3df4cab074dcc2086 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:32:50 +0200 Subject: [PATCH 13/45] Tests for getAllWeeklyReports --- testing/helpers.py | 2 +- testing/testing.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/testing/helpers.py b/testing/helpers.py index 20b00b1..5f2f367 100644 --- a/testing/helpers.py +++ b/testing/helpers.py @@ -25,7 +25,7 @@ signReportPath = base_url + "/api/signReport" addUserToProjectPath = base_url + "/api/addUserToProject" promoteToAdminPath = base_url + "/api/promoteToAdmin" getUserProjectsPath = base_url + "/api/getUserProjects" -getWeeklyReportsUserPath = base_url + "/api/getWeeklyReportsUser" +getAllWeeklyReportsPath = base_url + "/api/getAllWeeklyReports" checkIfProjectManagerPath = base_url + "/api/checkIfProjectManager" ProjectRoleChangePath = base_url + "/api/ProjectRoleChange" getUsersProjectPath = base_url + "/api/getUsersProject" diff --git a/testing/testing.py b/testing/testing.py index a2dfb64..ba38ced 100644 --- a/testing/testing.py +++ b/testing/testing.py @@ -217,14 +217,15 @@ def test_sign_report(): # Test function to get weekly reports for a user in a project -def test_get_weekly_reports_user(): +def test_get_all_weekly_reports(): # Log in as the user token = login(username, "always_same").json()["token"] # Get weekly reports for the user in the project response = requests.get( - getWeeklyReportsUserPath + "/" + projectName, + getAllWeeklyReportsPath + "/" + projectName, headers={"Authorization": "Bearer " + token}, + params={"targetUser": username}, ) dprint(response.text) @@ -507,7 +508,7 @@ if __name__ == "__main__": test_get_project() test_sign_report() test_add_user_to_project() - test_get_weekly_reports_user() + test_get_all_weekly_reports() test_check_if_project_manager() test_ProjectRoleChange() test_ensure_manager_of_created_project() From 12a2691d557ac21149c872bf055f4054236e3b0d Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:40:48 +0200 Subject: [PATCH 14/45] Rename --- .../{GetWeeklyReportsUserHandler.go => GetAllWeeklyReports.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename backend/internal/handlers/reports/{GetWeeklyReportsUserHandler.go => GetAllWeeklyReports.go} (100%) diff --git a/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go b/backend/internal/handlers/reports/GetAllWeeklyReports.go similarity index 100% rename from backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go rename to backend/internal/handlers/reports/GetAllWeeklyReports.go From 8cc0458e1b472d7f532c63fc0abb0eb647d1d38a Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 3 Apr 2024 17:44:26 +0200 Subject: [PATCH 15/45] Refactor API call in AllTimeReportsInProjectOtherUser component --- frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx index ef78642..570b94e 100644 --- a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx +++ b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx @@ -17,10 +17,10 @@ function AllTimeReportsInProject(): JSX.Element { useEffect(() => { const getWeeklyReports = async (): Promise => { const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getWeeklyReportsForDifferentUser( + const response = await api.getAllWeeklyReportsForUser( projectName ?? "", - username ?? "", token, + username ?? "", ); console.log(response); if (response.success) { From 12810075f9d9ce1eeaf490f51c12e15024bb496b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:44:50 +0200 Subject: [PATCH 16/45] Logic error in getAllWeeklyReports fixed --- backend/internal/handlers/reports/GetAllWeeklyReports.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/internal/handlers/reports/GetAllWeeklyReports.go b/backend/internal/handlers/reports/GetAllWeeklyReports.go index 825c0cc..ee81c82 100644 --- a/backend/internal/handlers/reports/GetAllWeeklyReports.go +++ b/backend/internal/handlers/reports/GetAllWeeklyReports.go @@ -31,7 +31,7 @@ func GetAllWeeklyReports(c *fiber.Ctx) error { return c.Status(400).SendString("Missing project name") } - // If the token user is not an admin, check if the target user is the same as the token user + // If the user is not a project manager, they can only view their own reports pm, err := db.GetDb(c).IsProjectManager(username, projectName) if err != nil { log.Info("Error checking if user is project manager:", err) @@ -44,9 +44,9 @@ func GetAllWeeklyReports(c *fiber.Ctx) error { } // Retrieve weekly reports for the user in the project from the database - reports, err := db.GetDb(c).GetAllWeeklyReports(username, projectName) + reports, err := db.GetDb(c).GetAllWeeklyReports(target_user, projectName) if err != nil { - log.Error("Error getting weekly reports for user:", username, "in project:", projectName, ":", err) + log.Error("Error getting weekly reports for user:", target_user, "in project:", projectName, ":", err) return c.Status(500).SendString(err.Error()) } From f9c30c4951b8cbb91eb21895667fc85c284ecaf0 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:48:02 +0200 Subject: [PATCH 17/45] Docs --- backend/docs/docs.go | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 7a08b0e..c8b020d 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -108,6 +108,56 @@ const docTemplate = `{ } } }, + "/promote/{projectName}": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Promote a user to project manager", + "consumes": [ + "text/plain" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "Auth" + ], + "summary": "Promote to project manager", + "parameters": [ + { + "type": "string", + "description": "Project name", + "name": "projectName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "User name", + "name": "userName", + "in": "query", + "required": true + } + ], + "responses": { + "403": { + "description": "Forbidden", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, "/promoteToAdmin": { "post": { "security": [ From c0d36f8df163494e63870c6f368bb8bf0f1af90d Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 3 Apr 2024 18:05:07 +0200 Subject: [PATCH 18/45] Update API method to get all weekly reports --- frontend/src/Components/AllTimeReportsInProject.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Components/AllTimeReportsInProject.tsx b/frontend/src/Components/AllTimeReportsInProject.tsx index 4fa9ad8..0d5916b 100644 --- a/frontend/src/Components/AllTimeReportsInProject.tsx +++ b/frontend/src/Components/AllTimeReportsInProject.tsx @@ -17,7 +17,7 @@ function AllTimeReportsInProject(): JSX.Element { useEffect(() => { const getWeeklyReports = async (): Promise => { const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getWeeklyReportsForUser( + const response = await api.getAllWeeklyReportsForUser( projectName ?? "", token, ); From be963b2281cfddad079582701766c114c8d06a60 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 3 Apr 2024 18:05:19 +0200 Subject: [PATCH 19/45] Fix input fields to be read-only in OtherUsersTR and initialize state variables in TimePerRole --- frontend/src/Components/OtherUsersTR.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/Components/OtherUsersTR.tsx b/frontend/src/Components/OtherUsersTR.tsx index 2b00e16..ce7761c 100644 --- a/frontend/src/Components/OtherUsersTR.tsx +++ b/frontend/src/Components/OtherUsersTR.tsx @@ -29,6 +29,7 @@ export default function OtherUsersTR(): JSX.Element { projectName ?? "", fetchedWeek?.toString() ?? "0", token, + username ?? "", ); if (response.success) { @@ -86,6 +87,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={developmentTime === 0 ? "" : developmentTime} + readOnly /> @@ -97,6 +99,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={meetingTime === 0 ? "" : meetingTime} + readOnly /> @@ -108,6 +111,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={adminTime === 0 ? "" : adminTime} + readOnly /> @@ -119,6 +123,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={ownWorkTime === 0 ? "" : ownWorkTime} + readOnly /> @@ -130,6 +135,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={studyTime === 0 ? "" : studyTime} + readOnly /> @@ -141,6 +147,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={testingTime === 0 ? "" : testingTime} + readOnly /> From 66a064c9f849bc162c9bc1b6da1617fb642aff57 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 3 Apr 2024 18:05:30 +0200 Subject: [PATCH 20/45] Fix initial state values in TimePerRole component --- frontend/src/Components/TimePerActivity.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/Components/TimePerActivity.tsx b/frontend/src/Components/TimePerActivity.tsx index 6175845..c5e4a9f 100644 --- a/frontend/src/Components/TimePerActivity.tsx +++ b/frontend/src/Components/TimePerActivity.tsx @@ -8,12 +8,12 @@ import { projectTimes } from "./GetProjectTimes"; * @returns JSX.Element */ export default function TimePerRole(): JSX.Element { - const [development, setDevelopment] = useState(); - const [meeting, setMeeting] = useState(); - const [admin, setAdmin] = useState(); - const [own_work, setOwnWork] = useState(); - const [study, setStudy] = useState(); - const [testing, setTesting] = useState(); + const [development, setDevelopment] = useState(0); + const [meeting, setMeeting] = useState(0); + const [admin, setAdmin] = useState(0); + const [own_work, setOwnWork] = useState(0); + const [study, setStudy] = useState(0); + const [testing, setTesting] = useState(0); const token = localStorage.getItem("accessToken") ?? ""; const { projectName } = useParams(); From 76ae587116e080ba758400055548394b6eaf55b0 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 18:05:56 +0200 Subject: [PATCH 21/45] Fix for javascript optional parameter formatting error --- frontend/src/API/API.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index c4cf445..b3c6eae 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -538,7 +538,7 @@ export const api: API = { ): Promise> { try { const response = await fetch( - `/api/getWeeklyReport?projectName=${projectName}&week=${week}&targetUser=${targetUser}`, + `/api/getWeeklyReport?projectName=${projectName}&week=${week}&targetUser=${targetUser ?? ""}`, { method: "GET", headers: { @@ -566,7 +566,7 @@ export const api: API = { ): Promise> { try { const response = await fetch( - `/api/getAllWeeklyReports/${projectName}?targetUser=${targetUser}`, + `/api/getAllWeeklyReports/${projectName}?targetUser=${targetUser ?? ""}`, { method: "GET", headers: { From e67c54540c389cfca61b0778ea7d3eab219e9f3b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 18:08:02 +0200 Subject: [PATCH 22/45] RemoveUserFromProject handler implemented, corresponding TS api, untested --- backend/internal/database/db.go | 10 +++++ .../projects/RemoveUserFromProject.go | 40 +++++++++++++++++++ backend/main.go | 1 + frontend/src/API/API.ts | 31 ++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 backend/internal/handlers/projects/RemoveUserFromProject.go diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 6bf6fba..0bd67bc 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -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 @@ -86,6 +87,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 +152,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 diff --git a/backend/internal/handlers/projects/RemoveUserFromProject.go b/backend/internal/handlers/projects/RemoveUserFromProject.go new file mode 100644 index 0000000..7aefcf8 --- /dev/null +++ b/backend/internal/handlers/projects/RemoveUserFromProject.go @@ -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) +} diff --git a/backend/main.go b/backend/main.go index b5ecacf..42daa5c 100644 --- a/backend/main.go +++ b/backend/main.go @@ -121,6 +121,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) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index b3c6eae..3c0f0e9 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -201,6 +201,12 @@ interface API { token: string, ): Promise>; + removeUserFromProject( + user: string, + project: string, + token: string, + ): Promise>; + removeProject( projectName: string, token: string, @@ -359,6 +365,31 @@ export const api: API = { } }, + async removeUserFromProject( + user: string, + project: string, + token: string, + ): Promise> { + 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> { try { const response = await fetch("/api/loginrenew", { From a0ff329845f24ff67774005ee43a285f19ff4a61 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 3 Apr 2024 18:36:17 +0200 Subject: [PATCH 23/45] Update useEffect dependencies in AllTimeReportsInProjectOtherUser component --- frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx index 570b94e..cde9fa7 100644 --- a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx +++ b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx @@ -31,7 +31,7 @@ function AllTimeReportsInProject(): JSX.Element { }; void getWeeklyReports(); - }, []); + }, [projectName, username]); return ( <> From a5ea74c996dd3aa7fd6626c91ec410367928f560 Mon Sep 17 00:00:00 2001 From: Mattias Date: Thu, 4 Apr 2024 10:29:25 +0200 Subject: [PATCH 24/45] Add useNavigate hook and handle navigation based on user "role" --- frontend/src/Components/Header.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/frontend/src/Components/Header.tsx b/frontend/src/Components/Header.tsx index eb4fa5a..9be2f4b 100644 --- a/frontend/src/Components/Header.tsx +++ b/frontend/src/Components/Header.tsx @@ -1,6 +1,6 @@ //info: Header component to display the header of the page including the logo and user information where thr user can logout import { useState } from "react"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import backgroundImage from "../assets/1.jpg"; /** @@ -9,23 +9,33 @@ import backgroundImage from "../assets/1.jpg"; */ function Header(): JSX.Element { const [isOpen, setIsOpen] = useState(false); + const username = localStorage.getItem("username"); + const navigate = useNavigate(); const handleLogout = (): void => { localStorage.clear(); }; + const handleNavigation = (): void => { + if (username === "admin") { + navigate("/admin"); + } else { + navigate("/yourProjects"); + } + }; + return (
- +
TTIME Logo - +
Date: Thu, 4 Apr 2024 11:19:35 +0200 Subject: [PATCH 25/45] Update error message in NewWeeklyReport component --- frontend/src/Components/NewWeeklyReport.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx index 1bb5cd4..ffc8b21 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -62,7 +62,7 @@ export default function NewWeeklyReport(): JSX.Element { const success = await handleNewWeeklyReport(); if (!success) { alert( - "A Time Report for this week already exists, please go to the edit page to edit it or change week number.", + "Error occurred! Your connection to the server might be lost or a time report for this week already exists, please check your connection or go to the edit page to edit your report or change week number.", ); return; } From 9a0f855d2b3f78bf4d18fff7d8faff62d0739215 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Thu, 4 Apr 2024 11:26:39 +0200 Subject: [PATCH 26/45] Fixes to adding members --- frontend/src/API/API.ts | 24 ++-- frontend/src/Components/AddMember.tsx | 55 ++++----- frontend/src/Components/AddUserToProject.tsx | 118 +++++++++++-------- 3 files changed, 104 insertions(+), 93 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 16aebff..414d978 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -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"; @@ -100,7 +100,7 @@ interface API { ): Promise>; /** Gets a weekly report for a specific user, project and week. - * Keep in mind that the user within the token needs to be PM + * Keep in mind that the user within the token needs to be PM * of the project to get the report, unless the user is the target user. * @param {string} projectName The name of the project. * @param {string} week The week number. @@ -196,7 +196,7 @@ interface API { ): Promise>; addUserToProject( - user: NewProjMember, + addMemberInfo: AddMemberInfo, token: string, ): Promise>; @@ -335,18 +335,20 @@ export const api: API = { }, async addUserToProject( - user: NewProjMember, + addMemberInfo: AddMemberInfo, token: string, ): Promise> { 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" }; diff --git a/frontend/src/Components/AddMember.tsx b/frontend/src/Components/AddMember.tsx index 194afe8..d8036b7 100644 --- a/frontend/src/Components/AddMember.tsx +++ b/frontend/src/Components/AddMember.tsx @@ -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} */ -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 { + 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) => { - 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; diff --git a/frontend/src/Components/AddUserToProject.tsx b/frontend/src/Components/AddUserToProject.tsx index 039d3c0..f567560 100644 --- a/frontend/src/Components/AddUserToProject.tsx +++ b/frontend/src/Components/AddUserToProject.tsx @@ -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([]); const [users, setUsers] = useState([]); - const [role, setRole] = useState(""); - GetAllUsers({ setUsersProp: setUsers }); + const [usersProj, setUsersProj] = useState([]); - 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 => { + 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 ( -
-

- User chosen: [{name}] +

+

+ {props.projectName} +

+

+ Choose users to add:

-

- Role chosen: [{role}] -

-

- Project chosen: [{props.projectName}] -

-

Choose role:

-
-
    -
  • { - setRole("member"); - }} - > - {"Member"} -
  • -
  • { - setRole("project_manager"); - }} - > - {"Project manager"} -
  • -
-
-

Choose user:

-
+
    {users.map((user) => (
  • { - setName(user); + handleUserClick(user); }} > {user} @@ -73,13 +88,16 @@ function AddUserToProject(props: { projectName: string }): JSX.Element { ))}
-
+

+ Number of users to be added: {names.length} +

+
From 13eb6597a7ad993dcd69c5b7887dda826052611f Mon Sep 17 00:00:00 2001 From: Peter KW Date: Thu, 4 Apr 2024 11:54:34 +0200 Subject: [PATCH 27/45] Some fixes to how they handle names and inputs --- frontend/src/Components/AddProject.tsx | 61 ++++++++++------------ frontend/src/Components/ChangeRoleView.tsx | 2 +- frontend/src/Components/ChangeUsername.tsx | 9 ++-- frontend/src/Components/Register.tsx | 10 ++-- frontend/src/Components/UserInfoModal.tsx | 2 +- 5 files changed, 44 insertions(+), 40 deletions(-) diff --git a/frontend/src/Components/AddProject.tsx b/frontend/src/Components/AddProject.tsx index e2ad8b9..c8a1c66 100644 --- a/frontend/src/Components/AddProject.tsx +++ b/frontend/src/Components/AddProject.tsx @@ -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) => { - 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 => { + 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 (
@@ -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(); }} > (); const handleRegister = async (): Promise => { + 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); } diff --git a/frontend/src/Components/UserInfoModal.tsx b/frontend/src/Components/UserInfoModal.tsx index 8cb4c9d..eae011c 100644 --- a/frontend/src/Components/UserInfoModal.tsx +++ b/frontend/src/Components/UserInfoModal.tsx @@ -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 }); }; From 0a951ecd2b691a52511b5c733598baa930d6a5bd Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:31:39 +0200 Subject: [PATCH 28/45] Rename, fix and testing for getAllWeeklyReports path --- backend/internal/database/db.go | 6 +-- backend/internal/database/db_test.go | 5 +-- .../reports/GetWeeklyReportsUserHandler.go | 40 ++++++++++++++----- backend/main.go | 2 +- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index f4c0f6e..6bf6fba 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -35,7 +35,7 @@ type Database interface { GetProject(projectId int) (types.Project, error) GetUserRole(username string, projectname string) (string, error) GetWeeklyReport(username string, projectName string, week int) (types.WeeklyReport, error) - GetWeeklyReportsUser(username string, projectname string) ([]types.WeeklyReportList, error) + GetAllWeeklyReports(username string, projectname string) ([]types.WeeklyReportList, error) GetUnsignedWeeklyReports(projectName string) ([]types.WeeklyReport, error) SignWeeklyReport(reportId int, projectManagerId int) error IsSiteAdmin(username string) (bool, error) @@ -463,8 +463,8 @@ func (d *Db) Migrate() error { return nil } -// GetWeeklyReportsUser retrieves weekly reports for a specific user and project. -func (d *Db) GetWeeklyReportsUser(username string, projectName string) ([]types.WeeklyReportList, error) { +// GetAllWeeklyReports retrieves weekly reports for a specific user and project. +func (d *Db) GetAllWeeklyReports(username string, projectName string) ([]types.WeeklyReportList, error) { query := ` SELECT wr.week, diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index fe3e6cd..a691a4d 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -705,7 +705,7 @@ func TestGetWeeklyReportsUser(t *testing.T) { t.Error("AddWeeklyReport failed:", err) } - reports, err := db.GetWeeklyReportsUser("testuser", "testproject") + reports, err := db.GetAllWeeklyReports("testuser", "testproject") if err != nil { t.Error("GetWeeklyReportsUser failed:", err) } @@ -962,6 +962,5 @@ func TestRemoveProject(t *testing.T) { if len(projects) != 0 { t.Error("RemoveProject failed: expected 0, got", len(projects)) } - + } - \ No newline at end of file diff --git a/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go b/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go index da8a90b..825c0cc 100644 --- a/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go +++ b/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go @@ -8,29 +8,49 @@ import ( "github.com/golang-jwt/jwt/v5" ) -// GetWeeklyReportsUserHandler retrieves all weekly reports for a user in a specific project -func GetWeeklyReportsUserHandler(c *fiber.Ctx) error { +// GetAllWeeklyReports retrieves all weekly reports for a user in a specific project +func GetAllWeeklyReports(c *fiber.Ctx) error { // Extract the necessary parameters from the token user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) username := claims["name"].(string) - // Extract necessary (path) parameters from the request + // Extract project name and week from query parameters projectName := c.Params("projectName") + target_user := c.Query("targetUser") // The user whose reports are being requested - // TODO: Here we need to check whether the user is a member of the project - // If not, we should return an error. On the other hand, if the user not a member, - // the returned list of reports will (should) allways be empty. + // If the target user is not empty, use it as the username + if target_user == "" { + target_user = username + } + + log.Info(username, " trying to get all weekly reports for: ", target_user) + + if projectName == "" { + log.Info("Missing project name") + return c.Status(400).SendString("Missing project name") + } + + // If the token user is not an admin, check if the target user is the same as the token user + pm, err := db.GetDb(c).IsProjectManager(username, projectName) + if err != nil { + log.Info("Error checking if user is project manager:", err) + return c.Status(500).SendString(err.Error()) + } + + if pm == false && target_user != username { + log.Info("Unauthorized access") + return c.Status(403).SendString("Unauthorized access") + } // Retrieve weekly reports for the user in the project from the database - reports, err := db.GetDb(c).GetWeeklyReportsUser(username, projectName) + reports, err := db.GetDb(c).GetAllWeeklyReports(username, projectName) if err != nil { log.Error("Error getting weekly reports for user:", username, "in project:", projectName, ":", err) return c.Status(500).SendString(err.Error()) } - log.Info("Returning weekly reports for user:", username, "in project:", projectName) - - // Return the list of reports as JSON + log.Info("Returning weekly report") + // Return the retrieved weekly report return c.JSON(reports) } diff --git a/backend/main.go b/backend/main.go index f811a58..b5ecacf 100644 --- a/backend/main.go +++ b/backend/main.go @@ -128,7 +128,7 @@ func main() { // reportGroup := api.Group("/report") // Not currently in use api.Get("/getWeeklyReport", reports.GetWeeklyReport) api.Get("/getUnsignedReports/:projectName", reports.GetUnsignedReports) - api.Get("/getWeeklyReportsUser/:projectName", reports.GetWeeklyReportsUserHandler) + api.Get("/getAllWeeklyReports/:projectName", reports.GetAllWeeklyReports) api.Post("/submitWeeklyReport", reports.SubmitWeeklyReport) api.Put("/signReport/:reportId", reports.SignReport) api.Put("/updateWeeklyReport", reports.UpdateWeeklyReport) From 2ca8c604186c1009fa66821b550966b8c4018f65 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:32:07 +0200 Subject: [PATCH 29/45] TS Api for getAllWeeklyReport --- frontend/src/API/API.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 414d978..fd6b4d8 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -122,9 +122,10 @@ interface API { * @param {string} token The token of the user * @returns {APIResponse} A list of weekly reports */ - getWeeklyReportsForUser( + getAllWeeklyReportsForUser( projectName: string, token: string, + targetUser?: string, ): Promise>; /** Gets all the projects of a user @@ -560,18 +561,22 @@ export const api: API = { } }, - async getWeeklyReportsForUser( + async getAllWeeklyReportsForUser( projectName: string, token: string, + targetUser?: string, ): Promise> { try { - const response = await fetch(`/api/getWeeklyReportsUser/${projectName}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, + const response = await fetch( + `/api/getAllWeeklyReports/${projectName}?targetUser=${targetUser}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, }, - }); + ); if (!response.ok) { return { From 4319f307996d2ce2852556b99ae1d4cb0f5c05ba Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:32:50 +0200 Subject: [PATCH 30/45] Tests for getAllWeeklyReports --- testing/helpers.py | 2 +- testing/testing.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/testing/helpers.py b/testing/helpers.py index 20b00b1..5f2f367 100644 --- a/testing/helpers.py +++ b/testing/helpers.py @@ -25,7 +25,7 @@ signReportPath = base_url + "/api/signReport" addUserToProjectPath = base_url + "/api/addUserToProject" promoteToAdminPath = base_url + "/api/promoteToAdmin" getUserProjectsPath = base_url + "/api/getUserProjects" -getWeeklyReportsUserPath = base_url + "/api/getWeeklyReportsUser" +getAllWeeklyReportsPath = base_url + "/api/getAllWeeklyReports" checkIfProjectManagerPath = base_url + "/api/checkIfProjectManager" ProjectRoleChangePath = base_url + "/api/ProjectRoleChange" getUsersProjectPath = base_url + "/api/getUsersProject" diff --git a/testing/testing.py b/testing/testing.py index a2dfb64..ba38ced 100644 --- a/testing/testing.py +++ b/testing/testing.py @@ -217,14 +217,15 @@ def test_sign_report(): # Test function to get weekly reports for a user in a project -def test_get_weekly_reports_user(): +def test_get_all_weekly_reports(): # Log in as the user token = login(username, "always_same").json()["token"] # Get weekly reports for the user in the project response = requests.get( - getWeeklyReportsUserPath + "/" + projectName, + getAllWeeklyReportsPath + "/" + projectName, headers={"Authorization": "Bearer " + token}, + params={"targetUser": username}, ) dprint(response.text) @@ -507,7 +508,7 @@ if __name__ == "__main__": test_get_project() test_sign_report() test_add_user_to_project() - test_get_weekly_reports_user() + test_get_all_weekly_reports() test_check_if_project_manager() test_ProjectRoleChange() test_ensure_manager_of_created_project() From d7789ab844dd2b4e092f756fb3e670d5b50be683 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 3 Apr 2024 17:44:26 +0200 Subject: [PATCH 31/45] Refactor API call in AllTimeReportsInProjectOtherUser component --- frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx index ef78642..570b94e 100644 --- a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx +++ b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx @@ -17,10 +17,10 @@ function AllTimeReportsInProject(): JSX.Element { useEffect(() => { const getWeeklyReports = async (): Promise => { const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getWeeklyReportsForDifferentUser( + const response = await api.getAllWeeklyReportsForUser( projectName ?? "", - username ?? "", token, + username ?? "", ); console.log(response); if (response.success) { From 7313a27b312043c4f0a553dfe01d4c47d559e032 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:40:48 +0200 Subject: [PATCH 32/45] Rename --- .../{GetWeeklyReportsUserHandler.go => GetAllWeeklyReports.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename backend/internal/handlers/reports/{GetWeeklyReportsUserHandler.go => GetAllWeeklyReports.go} (100%) diff --git a/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go b/backend/internal/handlers/reports/GetAllWeeklyReports.go similarity index 100% rename from backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go rename to backend/internal/handlers/reports/GetAllWeeklyReports.go From 620073f688da805ff2960ac770fe1d63757f38ea Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:44:50 +0200 Subject: [PATCH 33/45] Logic error in getAllWeeklyReports fixed --- backend/internal/handlers/reports/GetAllWeeklyReports.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/internal/handlers/reports/GetAllWeeklyReports.go b/backend/internal/handlers/reports/GetAllWeeklyReports.go index 825c0cc..ee81c82 100644 --- a/backend/internal/handlers/reports/GetAllWeeklyReports.go +++ b/backend/internal/handlers/reports/GetAllWeeklyReports.go @@ -31,7 +31,7 @@ func GetAllWeeklyReports(c *fiber.Ctx) error { return c.Status(400).SendString("Missing project name") } - // If the token user is not an admin, check if the target user is the same as the token user + // If the user is not a project manager, they can only view their own reports pm, err := db.GetDb(c).IsProjectManager(username, projectName) if err != nil { log.Info("Error checking if user is project manager:", err) @@ -44,9 +44,9 @@ func GetAllWeeklyReports(c *fiber.Ctx) error { } // Retrieve weekly reports for the user in the project from the database - reports, err := db.GetDb(c).GetAllWeeklyReports(username, projectName) + reports, err := db.GetDb(c).GetAllWeeklyReports(target_user, projectName) if err != nil { - log.Error("Error getting weekly reports for user:", username, "in project:", projectName, ":", err) + log.Error("Error getting weekly reports for user:", target_user, "in project:", projectName, ":", err) return c.Status(500).SendString(err.Error()) } From 82e72432f632af361173a6604598b1d2906e540c Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 3 Apr 2024 18:05:07 +0200 Subject: [PATCH 34/45] Update API method to get all weekly reports --- frontend/src/Components/AllTimeReportsInProject.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Components/AllTimeReportsInProject.tsx b/frontend/src/Components/AllTimeReportsInProject.tsx index 4fa9ad8..0d5916b 100644 --- a/frontend/src/Components/AllTimeReportsInProject.tsx +++ b/frontend/src/Components/AllTimeReportsInProject.tsx @@ -17,7 +17,7 @@ function AllTimeReportsInProject(): JSX.Element { useEffect(() => { const getWeeklyReports = async (): Promise => { const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getWeeklyReportsForUser( + const response = await api.getAllWeeklyReportsForUser( projectName ?? "", token, ); From 194e1d52a8e8693aaec02f67cafb5af0a67ade00 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 3 Apr 2024 18:05:19 +0200 Subject: [PATCH 35/45] Fix input fields to be read-only in OtherUsersTR and initialize state variables in TimePerRole --- frontend/src/Components/OtherUsersTR.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/Components/OtherUsersTR.tsx b/frontend/src/Components/OtherUsersTR.tsx index 2b00e16..ce7761c 100644 --- a/frontend/src/Components/OtherUsersTR.tsx +++ b/frontend/src/Components/OtherUsersTR.tsx @@ -29,6 +29,7 @@ export default function OtherUsersTR(): JSX.Element { projectName ?? "", fetchedWeek?.toString() ?? "0", token, + username ?? "", ); if (response.success) { @@ -86,6 +87,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={developmentTime === 0 ? "" : developmentTime} + readOnly /> @@ -97,6 +99,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={meetingTime === 0 ? "" : meetingTime} + readOnly /> @@ -108,6 +111,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={adminTime === 0 ? "" : adminTime} + readOnly /> @@ -119,6 +123,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={ownWorkTime === 0 ? "" : ownWorkTime} + readOnly /> @@ -130,6 +135,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={studyTime === 0 ? "" : studyTime} + readOnly /> @@ -141,6 +147,7 @@ export default function OtherUsersTR(): JSX.Element { min="0" className="border-2 border-black rounded-md text-center w-1/2" value={testingTime === 0 ? "" : testingTime} + readOnly /> From 266aaa482c2f1b26a27bff209dd428bb200636df Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 3 Apr 2024 18:05:30 +0200 Subject: [PATCH 36/45] Fix initial state values in TimePerRole component --- frontend/src/Components/TimePerActivity.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/Components/TimePerActivity.tsx b/frontend/src/Components/TimePerActivity.tsx index 6175845..c5e4a9f 100644 --- a/frontend/src/Components/TimePerActivity.tsx +++ b/frontend/src/Components/TimePerActivity.tsx @@ -8,12 +8,12 @@ import { projectTimes } from "./GetProjectTimes"; * @returns JSX.Element */ export default function TimePerRole(): JSX.Element { - const [development, setDevelopment] = useState(); - const [meeting, setMeeting] = useState(); - const [admin, setAdmin] = useState(); - const [own_work, setOwnWork] = useState(); - const [study, setStudy] = useState(); - const [testing, setTesting] = useState(); + const [development, setDevelopment] = useState(0); + const [meeting, setMeeting] = useState(0); + const [admin, setAdmin] = useState(0); + const [own_work, setOwnWork] = useState(0); + const [study, setStudy] = useState(0); + const [testing, setTesting] = useState(0); const token = localStorage.getItem("accessToken") ?? ""; const { projectName } = useParams(); From ee49bbde69f8d26d1fd98f5350d9a80967033fa1 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 17:48:02 +0200 Subject: [PATCH 37/45] Docs --- backend/docs/docs.go | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 7a08b0e..c8b020d 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -108,6 +108,56 @@ const docTemplate = `{ } } }, + "/promote/{projectName}": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Promote a user to project manager", + "consumes": [ + "text/plain" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "Auth" + ], + "summary": "Promote to project manager", + "parameters": [ + { + "type": "string", + "description": "Project name", + "name": "projectName", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "User name", + "name": "userName", + "in": "query", + "required": true + } + ], + "responses": { + "403": { + "description": "Forbidden", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, "/promoteToAdmin": { "post": { "security": [ From 10d06767c4a35ee8e6c604f9c371ab6f8beef832 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 3 Apr 2024 18:05:56 +0200 Subject: [PATCH 38/45] Fix for javascript optional parameter formatting error --- frontend/src/API/API.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index fd6b4d8..e9820a0 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -540,7 +540,7 @@ export const api: API = { ): Promise> { try { const response = await fetch( - `/api/getWeeklyReport?projectName=${projectName}&week=${week}&targetUser=${targetUser}`, + `/api/getWeeklyReport?projectName=${projectName}&week=${week}&targetUser=${targetUser ?? ""}`, { method: "GET", headers: { @@ -568,7 +568,7 @@ export const api: API = { ): Promise> { try { const response = await fetch( - `/api/getAllWeeklyReports/${projectName}?targetUser=${targetUser}`, + `/api/getAllWeeklyReports/${projectName}?targetUser=${targetUser ?? ""}`, { method: "GET", headers: { From f75e6e4a6e8941742e36033a7af8e5d1f1823112 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 3 Apr 2024 18:36:17 +0200 Subject: [PATCH 39/45] Update useEffect dependencies in AllTimeReportsInProjectOtherUser component --- frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx index 570b94e..cde9fa7 100644 --- a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx +++ b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx @@ -31,7 +31,7 @@ function AllTimeReportsInProject(): JSX.Element { }; void getWeeklyReports(); - }, []); + }, [projectName, username]); return ( <> From 2d8d200340da02676c575a071cde0cb61a229cb6 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Thu, 4 Apr 2024 12:22:28 +0200 Subject: [PATCH 40/45] Can now remove users from projects --- frontend/src/Components/MemberInfoModal.tsx | 13 +++--- .../src/Components/RemoveUserFromProj.tsx | 41 +++++++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 frontend/src/Components/RemoveUserFromProj.tsx diff --git a/frontend/src/Components/MemberInfoModal.tsx b/frontend/src/Components/MemberInfoModal.tsx index 8b32367..adcb39f 100644 --- a/frontend/src/Components/MemberInfoModal.tsx +++ b/frontend/src/Components/MemberInfoModal.tsx @@ -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; @@ -42,13 +42,16 @@ function MemberInfoModal(props: {