From 5f6977354f143e519abfbe9f88dc6bc3535fa268 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 19 Mar 2024 04:02:04 +0100 Subject: [PATCH 001/219] Justfile save-release target now saves image with name containing commit hash and date --- Justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Justfile b/Justfile index 432fbd1..cb905e4 100644 --- a/Justfile +++ b/Justfile @@ -15,7 +15,7 @@ remove-podman-containers: # Saves the release container to a tarball, pigz is just gzip but multithreaded save-release: build-container-release - podman save --format=oci-archive ttime-server | pigz -9 > ttime-server.tar.gz + podman save --format=oci-archive ttime-server | pigz -9 > ttime-server_`date -I`_`git rev-parse --short HEAD`.tar.gz # Loads the release container from a tarball load-release file: From 2f3730ca90285dc270e419a64e92d712e5380893 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 19 Mar 2024 16:50:16 +0100 Subject: [PATCH 002/219] New typescript types from tygo --- frontend/src/Types/goTypes.ts | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/frontend/src/Types/goTypes.ts b/frontend/src/Types/goTypes.ts index 89084b7..43bb35f 100644 --- a/frontend/src/Types/goTypes.ts +++ b/frontend/src/Types/goTypes.ts @@ -40,6 +40,52 @@ export interface NewWeeklyReport { */ testingTime: number /* int */; } +export interface WeeklyReport { + /** + * The ID of the report + */ + reportId: number /* int */; + /** + * The user id of the user who submitted the report + */ + userId: number /* int */; + /** + * The name of the project, as it appears in the database + */ + projectId: number /* int */; + /** + * The week number + */ + week: number /* int */; + /** + * Total time spent on development + */ + developmentTime: number /* int */; + /** + * Total time spent in meetings + */ + meetingTime: number /* int */; + /** + * Total time spent on administrative tasks + */ + adminTime: number /* int */; + /** + * Total time spent on personal projects + */ + ownWorkTime: number /* int */; + /** + * Total time spent on studying + */ + studyTime: number /* int */; + /** + * Total time spent on testing + */ + testingTime: number /* int */; + /** + * The project manager who signed it + */ + signedBy?: number /* int */; +} ////////// // source: project.go @@ -86,3 +132,9 @@ export interface PublicUser { userId: string; username: string; } +/** + * wrapper type for token + */ +export interface Token { + token: string; +} From 8dfe9dac961a8fe34c468a251542746c9eac6c51 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Tue, 19 Mar 2024 17:09:45 +0100 Subject: [PATCH 003/219] Removed unused path --- frontend/src/main.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 9970cfa..55c6685 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -42,10 +42,6 @@ const router = createBrowserRouter([ path: "/admin", element: , }, - { - path: "/pm", - element: , - }, { path: "/yourProjects", element: , From cd84a2bb2137572aa16cc852febcf82d38d933ea Mon Sep 17 00:00:00 2001 From: Peter KW Date: Tue, 19 Mar 2024 17:12:09 +0100 Subject: [PATCH 004/219] Everyone except admin has authority = 2 --- frontend/src/Components/LoginCheck.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/frontend/src/Components/LoginCheck.tsx b/frontend/src/Components/LoginCheck.tsx index ce7d52c..f44d7f3 100644 --- a/frontend/src/Components/LoginCheck.tsx +++ b/frontend/src/Components/LoginCheck.tsx @@ -32,16 +32,11 @@ function LoginCheck(props: { prevAuth = 1; return prevAuth; }); - } else if (token !== "" && props.username === "pm") { + } else if (token !== "") { props.setAuthority((prevAuth) => { prevAuth = 2; return prevAuth; }); - } else if (token !== "" && props.username === "user") { - props.setAuthority((prevAuth) => { - prevAuth = 3; - return prevAuth; - }); } } else { console.error("Token was undefined"); From a72aea1382e1b2834304df5126e872ba71b9e6f1 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Tue, 19 Mar 2024 17:12:38 +0100 Subject: [PATCH 005/219] Removed unused authority check --- frontend/src/Pages/App.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/Pages/App.tsx b/frontend/src/Pages/App.tsx index 69fd698..62e1b36 100644 --- a/frontend/src/Pages/App.tsx +++ b/frontend/src/Pages/App.tsx @@ -11,8 +11,6 @@ function App(): JSX.Element { if (authority === 1) { navigate("/admin"); } else if (authority === 2) { - navigate("/pm"); - } else if (authority === 3) { navigate("/yourProjects"); } }, [authority, navigate]); From 2b41085865c7a1809bb598f362e65860bafd3456 Mon Sep 17 00:00:00 2001 From: dDogge Date: Tue, 19 Mar 2024 19:04:45 +0100 Subject: [PATCH 006/219] Added GetWeeklyReportsUser function and handler, also corresponding tests to both GetWeeklyReportsUser and handler added --- backend/internal/database/db.go | 31 +++++++++++++++ backend/internal/database/db_test.go | 39 +++++++++++++++++++ backend/internal/handlers/global_state.go | 1 + .../handlers/handlers_report_related.go | 19 +++++++++ backend/internal/types/WeeklyReport.go | 21 ++++++++++ backend/main.go | 2 + testing.py | 18 +++++++++ 7 files changed, 131 insertions(+) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index bc6e1e8..574557a 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -32,6 +32,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) SignWeeklyReport(reportId int, projectManagerId int) error IsSiteAdmin(username string) (bool, error) } @@ -402,6 +403,36 @@ 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) { + query := ` + SELECT + wr.week, + wr.development_time, + wr.meeting_time, + wr.admin_time, + wr.own_work_time, + wr.study_time, + wr.testing_time, + wr.signed_by + FROM + weekly_reports wr + INNER JOIN + users u ON wr.user_id = u.id + INNER JOIN + projects p ON wr.project_id = p.id + WHERE + u.username = ? AND p.name = ? + ` + + var reports []types.WeeklyReportList + if err := d.Select(&reports, query, username, projectName); err != nil { + return nil, err + } + + return reports, nil +} + // MigrateSampleData applies sample data to the database. func (d *Db) MigrateSampleData() error { // Insert sample data diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index a7f3878..a6009cf 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -566,3 +566,42 @@ func TestGetProject(t *testing.T) { t.Errorf("Expected Name to be testproject, got %s", project.Name) } } + +func TestGetWeeklyReportsUser(t *testing.T) { + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + + err = db.AddUser("testuser", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + err = db.AddProject("testproject", "description", "testuser") + if err != nil { + t.Error("AddProject failed:", err) + } + + err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) + if err != nil { + t.Error("AddWeeklyReport failed:", err) + } + + err = db.AddWeeklyReport("testproject", "testuser", 2, 1, 1, 1, 1, 1, 1) + if err != nil { + t.Error("AddWeeklyReport failed:", err) + } + + reports, err := db.GetWeeklyReportsUser("testuser", "testproject") + if err != nil { + t.Error("GetWeeklyReportsUser failed:", err) + } + + // Check if the retrieved reports match the expected values + if len(reports) != 2 { + t.Errorf("Expected 1 report, got %d", len(reports)) + } + + // You can add further checks here if needed +} diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 932451d..0d041b0 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -20,6 +20,7 @@ type GlobalState interface { GetProject(c *fiber.Ctx) error AddUserToProjectHandler(c *fiber.Ctx) error PromoteToAdmin(c *fiber.Ctx) error + GetWeeklyReportsUserHandler(c *fiber.Ctx) error // GetProject(c *fiber.Ctx) error // To get a specific project // UpdateProject(c *fiber.Ctx) error // To update a project // DeleteProject(c *fiber.Ctx) error // To delete a project diff --git a/backend/internal/handlers/handlers_report_related.go b/backend/internal/handlers/handlers_report_related.go index 85eb6e2..b745400 100644 --- a/backend/internal/handlers/handlers_report_related.go +++ b/backend/internal/handlers/handlers_report_related.go @@ -114,3 +114,22 @@ func (gs *GState) SignReport(c *fiber.Ctx) error { return c.Status(200).SendString("Weekly report signed successfully") } + +// GetWeeklyReportsUserHandler retrieves all weekly reports for a user in a specific project +func (gs *GState) GetWeeklyReportsUserHandler(c *fiber.Ctx) error { + // Extract necessary parameters from the request + username := c.Params("username") + projectName := c.Params("projectName") + + // Retrieve weekly reports for the user in the project from the database + reports, err := gs.Db.GetWeeklyReportsUser(username, projectName) + if err != nil { + log.Info("Error getting weekly reports for user:", 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 + return c.JSON(reports) +} diff --git a/backend/internal/types/WeeklyReport.go b/backend/internal/types/WeeklyReport.go index 299395a..8d22b6a 100644 --- a/backend/internal/types/WeeklyReport.go +++ b/backend/internal/types/WeeklyReport.go @@ -20,6 +20,27 @@ type NewWeeklyReport struct { TestingTime int `json:"testingTime"` } +type WeeklyReportList struct { + // The name of the project, as it appears in the database + ProjectName string `json:"projectName" db:"project_name"` + // The week number + Week int `json:"week" db:"week"` + // Total time spent on development + DevelopmentTime int `json:"developmentTime" db:"development_time"` + // Total time spent in meetings + MeetingTime int `json:"meetingTime" db:"meeting_time"` + // Total time spent on administrative tasks + AdminTime int `json:"adminTime" db:"admin_time"` + // Total time spent on personal projects + OwnWorkTime int `json:"ownWorkTime" db:"own_work_time"` + // Total time spent on studying + StudyTime int `json:"studyTime" db:"study_time"` + // Total time spent on testing + TestingTime int `json:"testingTime" db:"testing_time"` + // The project manager who signed it + SignedBy *int `json:"signedBy" db:"signed_by"` +} + type WeeklyReport struct { // The ID of the report ReportId int `json:"reportId" db:"report_id"` diff --git a/backend/main.go b/backend/main.go index e578c52..900e1bd 100644 --- a/backend/main.go +++ b/backend/main.go @@ -94,6 +94,8 @@ func main() { server.Put("/api/addUserToProject", gs.AddUserToProjectHandler) server.Post("/api/promoteToAdmin", gs.PromoteToAdmin) server.Get("/api/users/all", gs.ListAllUsers) + server.Get("/api/getWeeklyReportsUser", gs.GetWeeklyReportsUserHandler) + // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) if err != nil { diff --git a/testing.py b/testing.py index a3de715..6b262fe 100644 --- a/testing.py +++ b/testing.py @@ -37,6 +37,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" def test_get_user_projects(): @@ -275,6 +276,22 @@ def test_sign_report(): 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, + headers={"Authorization": "Bearer " + token}, + params={"username": username, "projectName": projectName}, + ) + + dprint(response.text) + assert response.status_code == 200, "Get weekly reports for user failed" + gprint("test_get_weekly_reports_user successful") + if __name__ == "__main__": test_get_user_projects() @@ -286,3 +303,4 @@ if __name__ == "__main__": test_get_project() test_sign_report() test_add_user_to_project() + test_get_weekly_reports_user() From 5778d6e892435db5eafb59d7117cad9681220aa3 Mon Sep 17 00:00:00 2001 From: dDogge Date: Tue, 19 Mar 2024 19:14:55 +0100 Subject: [PATCH 007/219] Added isProjectManager function to db.go and corresponding test to db_test.go --- backend/internal/database/db.go | 22 +++++++++++ backend/internal/database/db_test.go | 56 +++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 574557a..842ffbd 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -35,6 +35,7 @@ type Database interface { GetWeeklyReportsUser(username string, projectname string) ([]types.WeeklyReportList, error) SignWeeklyReport(reportId int, projectManagerId int) error IsSiteAdmin(username string) (bool, error) + IsProjectManager(username string, projectname string) (bool, error) } // This struct is a wrapper type that holds the database connection @@ -433,6 +434,27 @@ func (d *Db) GetWeeklyReportsUser(username string, projectName string) ([]types. return reports, nil } +// IsProjectManager checks if a given username is a project manager for the specified project +func (d *Db) IsProjectManager(username string, projectname string) (bool, error) { + // Define the SQL query to check if the user is a project manager for the project + query := ` + SELECT COUNT(*) FROM user_roles + JOIN users ON user_roles.user_id = users.id + JOIN projects ON user_roles.project_id = projects.id + WHERE users.username = ? AND projects.name = ? AND user_roles.p_role = 'project_manager' + ` + + // Execute the query + var count int + err := d.Get(&count, query, username, projectname) + if err != nil { + return false, err + } + + // If count is greater than 0, the user is a project manager for the project + return count > 0, nil +} + // MigrateSampleData applies sample data to the database. func (d *Db) MigrateSampleData() error { // Insert sample data diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index a6009cf..2448e7e 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -602,6 +602,58 @@ func TestGetWeeklyReportsUser(t *testing.T) { if len(reports) != 2 { t.Errorf("Expected 1 report, got %d", len(reports)) } - - // You can add further checks here if needed +} + +func TestIsProjectManager(t *testing.T) { + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + + // Add a project manager + err = db.AddUser("projectManager", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Add a regular user + err = db.AddUser("testuser", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Add project + err = db.AddProject("testproject", "description", "projectManager") + if err != nil { + t.Error("AddProject failed:", err) + } + + // Add both regular users as members to the project + err = db.AddUserToProject("testuser", "testproject", "member") + if err != nil { + t.Error("AddUserToProject failed:", err) + } + + err = db.AddUserToProject("projectManager", "testproject", "project_manager") + if err != nil { + t.Error("AddUserToProject failed:", err) + } + + // Check if the regular user is not a project manager + isManager, err := db.IsProjectManager("testuser", "testproject") + if err != nil { + t.Error("IsProjectManager failed:", err) + } + if isManager { + t.Error("Expected testuser not to be a project manager, but it is.") + } + + // Check if the project manager is indeed a project manager + isManager, err = db.IsProjectManager("projectManager", "testproject") + if err != nil { + t.Error("IsProjectManager failed:", err) + } + if !isManager { + t.Error("Expected projectManager to be a project manager, but it's not.") + } } From ce4cf788ae2beaf148825b0753992cd85498f81f Mon Sep 17 00:00:00 2001 From: dDogge Date: Tue, 19 Mar 2024 19:30:01 +0100 Subject: [PATCH 008/219] Added handler and corresponding test for the IsProjectManager function --- backend/internal/handlers/global_state.go | 1 + .../handlers/handlers_project_related.go | 17 ++++++++++++++++ backend/main.go | 1 + testing.py | 20 +++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 0d041b0..5d01368 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -21,6 +21,7 @@ type GlobalState interface { AddUserToProjectHandler(c *fiber.Ctx) error PromoteToAdmin(c *fiber.Ctx) error GetWeeklyReportsUserHandler(c *fiber.Ctx) error + IsProjectManagerHandler(c *fiber.Ctx) error // GetProject(c *fiber.Ctx) error // To get a specific project // UpdateProject(c *fiber.Ctx) error // To update a project // DeleteProject(c *fiber.Ctx) error // To delete a project diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index f3a7ea0..df9f3e0 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -154,3 +154,20 @@ func (gs *GState) AddUserToProjectHandler(c *fiber.Ctx) error { log.Info("User added to project successfully:", requestData.Username) return c.SendStatus(fiber.StatusOK) } + +// IsProjectManagerHandler is a handler that checks if a user is a project manager for a given project +func (gs *GState) IsProjectManagerHandler(c *fiber.Ctx) error { + // Extract necessary parameters from the request query string + username := c.Query("username") + projectName := c.Query("projectName") + + // Check if the user is a project manager for the specified project + isManager, err := gs.Db.IsProjectManager(username, projectName) + if err != nil { + log.Info("Error checking project manager status:", err) + return c.Status(500).SendString(err.Error()) + } + + // Return the result as JSON + return c.JSON(map[string]bool{"isProjectManager": isManager}) +} diff --git a/backend/main.go b/backend/main.go index 900e1bd..abdf0db 100644 --- a/backend/main.go +++ b/backend/main.go @@ -95,6 +95,7 @@ func main() { server.Post("/api/promoteToAdmin", gs.PromoteToAdmin) server.Get("/api/users/all", gs.ListAllUsers) server.Get("/api/getWeeklyReportsUser", gs.GetWeeklyReportsUserHandler) + server.Get("api/checkIfProjectManager", gs.IsProjectManagerHandler) // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) diff --git a/testing.py b/testing.py index 6b262fe..670abcd 100644 --- a/testing.py +++ b/testing.py @@ -38,6 +38,7 @@ 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" def test_get_user_projects(): @@ -292,6 +293,24 @@ def test_get_weekly_reports_user(): 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, + headers={"Authorization": "Bearer " + token}, + params={"username": username, "projectName": projectName}, + ) + + dprint(response.text) + assert response.status_code == 200, "Check if project manager failed" + gprint("test_check_if_project_manager successful") + + + if __name__ == "__main__": test_get_user_projects() @@ -304,3 +323,4 @@ if __name__ == "__main__": test_sign_report() test_add_user_to_project() test_get_weekly_reports_user() + test_check_if_project_manager() From 74285dc1cfb4d60e95b63bb0dced87855f8e4c87 Mon Sep 17 00:00:00 2001 From: al8763be Date: Tue, 19 Mar 2024 19:45:56 +0100 Subject: [PATCH 009/219] Before fuck up --- frontend/src/API/API.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 8fd66d3..0fc29a0 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -24,6 +24,7 @@ interface API { login(NewUser: NewUser): Promise>; /** Renew the token */ renewToken(token: string): Promise>; + /** Promote user to admin */ /** Create a project */ createProject( project: NewProject, @@ -41,6 +42,10 @@ interface API { week: string, token: string, ): Promise>; + getWeeklyReportsForProject( + projectName: string, + token: string, + ): Promise>; /** Gets all the projects of a user*/ getUserProjects(token: string): Promise>; /** Gets a project from id*/ @@ -232,6 +237,34 @@ export const api: API = { } }, + async getWeeklyReportsForProject(projectName: string, token: string) { + try { + const response = await fetch("/api/getWeeklyReportsForProject", { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, + body: JSON.stringify({ projectName }), + }); + + if (!response.ok) { + return { + success: false, + message: "Failed to get weekly reports for project", + }; + } else { + const data = (await response.json()) as NewWeeklyReport[]; + return { success: true, data }; + } + } catch (e) { + return { + success: false, + message: "Failed to get weekly reports for project", + }; + } + }, + async login(NewUser: NewUser): Promise> { try { const response = await fetch("/api/login", { From 88c53ae4b67f2bb2fc2105e5c8a5b20e617f4b5b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 19 Mar 2024 20:21:38 +0100 Subject: [PATCH 010/219] Golds automatic documentation generator --- backend/Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/Makefile b/backend/Makefile index 65a2f3c..331f8d5 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -130,4 +130,12 @@ install-just: .PHONY: types types: - tygo generate \ No newline at end of file + tygo generate + +.PHONY: install-golds +install-golds: + go install go101.org/golds@latest + +.PHONY: golds +golds: + golds -port 6060 -nouses -plainsrc -wdpkgs-listing=promoted ./... From e48bf5d98cdbb468690473a06f0dca364d668a19 Mon Sep 17 00:00:00 2001 From: al8763be Date: Tue, 19 Mar 2024 20:25:26 +0100 Subject: [PATCH 011/219] Added checkIfProjectManager, hope it works --- frontend/src/API/API.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 0fc29a0..a1ecfde 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -20,6 +20,12 @@ interface API { registerUser(user: NewUser): Promise>; /** Remove a user */ removeUser(username: string, token: string): Promise>; + /** Check if user is project manager */ + checkIfProjectManager( + username: string, + projectName: string, + token: string, + ): Promise>; /** Login */ login(NewUser: NewUser): Promise>; /** Renew the token */ @@ -106,6 +112,30 @@ export const api: API = { } }, + async checkIfProjectManager(token: string): Promise> { + try { + const response = await fetch("/api/checkIfProjectManager", { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, + }); + + if (!response.ok) { + return { + success: false, + message: "Failed to check if project manager", + }; + } else { + const data = (await response.json()) as boolean; + return { success: true, data }; + } + } catch (e) { + return { success: false, message: "fuck" }; + } + }, + async createProject( project: NewProject, token: string, From 0b6edd359ecaa6b19986b520618225c65203223b Mon Sep 17 00:00:00 2001 From: al8763be Date: Tue, 19 Mar 2024 20:29:36 +0100 Subject: [PATCH 012/219] Bumfix for BumCode on checkIfProjectManager --- frontend/src/API/API.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index a1ecfde..fc2367b 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -112,7 +112,11 @@ export const api: API = { } }, - async checkIfProjectManager(token: string): Promise> { + async checkIfProjectManager( + username: string, + projectName: string, + token: string, + ): Promise> { try { const response = await fetch("/api/checkIfProjectManager", { method: "GET", @@ -120,6 +124,7 @@ export const api: API = { "Content-Type": "application/json", Authorization: "Bearer " + token, }, + body: JSON.stringify({ username, projectName }), }); if (!response.ok) { From 3fea87a88a9e58fbb7360b6ef4dbc6b65677710a Mon Sep 17 00:00:00 2001 From: Mattias Date: Tue, 19 Mar 2024 21:22:49 +0100 Subject: [PATCH 013/219] New component for displaying userprojects --- .../src/Components/DisplayUserProjects.tsx | 40 +++++++++++++++++++ frontend/src/Pages/YourProjectsPage.tsx | 33 +-------------- 2 files changed, 42 insertions(+), 31 deletions(-) create mode 100644 frontend/src/Components/DisplayUserProjects.tsx diff --git a/frontend/src/Components/DisplayUserProjects.tsx b/frontend/src/Components/DisplayUserProjects.tsx new file mode 100644 index 0000000..266f1ce --- /dev/null +++ b/frontend/src/Components/DisplayUserProjects.tsx @@ -0,0 +1,40 @@ +import { useState, useEffect } from "react"; +import { Project } from "../Types/goTypes"; +import { Link } from "react-router-dom"; +import { api } from "../API/API"; + +function DisplayUserProject(): JSX.Element { + const [projects, setProjects] = useState([]); + + const getProjects = async (): Promise => { + const token = localStorage.getItem("accessToken") ?? ""; + const response = await api.getUserProjects(token); + console.log(response); + if (response.success) { + setProjects(response.data ?? []); + } else { + console.error(response.message); + } + }; + // Call getProjects when the component mounts + useEffect(() => { + void getProjects(); + }, []); + + return ( + <> +

Your Projects

+
+ {projects.map((project, index) => ( + +

+ {project.name} +

+ + ))} +
+ + ); +} + +export default DisplayUserProject; diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index b5644c3..c048746 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -1,39 +1,10 @@ -import { useState, useEffect } from "react"; -import { Project } from "../Types/goTypes"; -import { Link } from "react-router-dom"; import BasicWindow from "../Components/BasicWindow"; -import { api } from "../API/API"; +import DisplayUserProjects from "../Components/DisplayUserProjects"; function UserProjectPage(): JSX.Element { - const [projects, setProjects] = useState([]); - - const getProjects = async (): Promise => { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getUserProjects(token); - console.log(response); - if (response.success) { - setProjects(response.data ?? []); - } else { - console.error(response.message); - } - }; - // Call getProjects when the component mounts - useEffect(() => { - void getProjects(); - }, []); - const content = ( <> -

Your Projects

-
- {projects.map((project, index) => ( - -

- {project.name} -

- - ))} -
+ ); From 05b9740c3602c9a26f48efc74aa0a66c9da1b800 Mon Sep 17 00:00:00 2001 From: Mattias Date: Tue, 19 Mar 2024 21:29:37 +0100 Subject: [PATCH 014/219] Fixed links and changed path in main for pm --- .../src/Pages/ProjectManagerPages/PMProjectPage.tsx | 12 +++++++----- frontend/src/main.tsx | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx index bd4e6ef..15d1d28 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx @@ -1,28 +1,30 @@ -import { Link } from "react-router-dom"; +import { Link, useParams } from "react-router-dom"; import BasicWindow from "../../Components/BasicWindow"; import { JSX } from "react/jsx-runtime"; function PMProjectPage(): JSX.Element { + const { projectName } = useParams(); + const content = ( <>

ProjectNameExample

- +

Your Time Reports

- +

New Time Report

- +

Statistics

- +

Unsigned Time Reports

diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 9970cfa..20ecbcd 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -91,7 +91,7 @@ const router = createBrowserRouter([ element: , }, { - path: "/PMUnsignedReports", + path: "/unsignedReports", element: , }, { From dc6ce4b725a4d1714ce394e2cd4006d3406a7ce3 Mon Sep 17 00:00:00 2001 From: Mattias Date: Tue, 19 Mar 2024 21:31:20 +0100 Subject: [PATCH 015/219] Additional minor fix --- frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx index 15d1d28..b88ad58 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx @@ -7,7 +7,7 @@ function PMProjectPage(): JSX.Element { const content = ( <> -

ProjectNameExample

+

{projectName}

From 63fd2e9b6f46aa1ca81ce358a4aed5cde5844648 Mon Sep 17 00:00:00 2001 From: Johanna Date: Tue, 19 Mar 2024 21:47:16 +0100 Subject: [PATCH 016/219] //fix parse in NewWeeklyReport --- frontend/src/Components/NewWeeklyReport.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx index ab9084d..1d029c3 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -54,8 +54,7 @@ export default function NewWeeklyReport(): JSX.Element { type="week" placeholder="Week" onChange={(e) => { - const weekNumber = parseInt(e.target.value.split("-W")[1]); - setWeek(weekNumber); + setWeek(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; From d355b556346606a105314a2e0f725d31aa889ac6 Mon Sep 17 00:00:00 2001 From: Mattias Date: Tue, 19 Mar 2024 21:59:37 +0100 Subject: [PATCH 017/219] Add UserProjectMenu component and refactor UserProjectPage --- frontend/src/Components/UserProjectMenu.tsx | 25 +++++++++++++++++++ .../src/Pages/UserPages/UserProjectPage.tsx | 18 ++----------- 2 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 frontend/src/Components/UserProjectMenu.tsx diff --git a/frontend/src/Components/UserProjectMenu.tsx b/frontend/src/Components/UserProjectMenu.tsx new file mode 100644 index 0000000..986b6e6 --- /dev/null +++ b/frontend/src/Components/UserProjectMenu.tsx @@ -0,0 +1,25 @@ +import { useParams, Link } from "react-router-dom"; +import { JSX } from "react/jsx-runtime"; + +function UserProjectMenu(): JSX.Element { + const { projectName } = useParams(); + + return ( + <> +

{projectName}

+
+ +

+ Your Time Reports +

+ + +

+ New Time Report +

+ +
+ + ); +} +export default UserProjectMenu; diff --git a/frontend/src/Pages/UserPages/UserProjectPage.tsx b/frontend/src/Pages/UserPages/UserProjectPage.tsx index 596c37d..b9578b4 100644 --- a/frontend/src/Pages/UserPages/UserProjectPage.tsx +++ b/frontend/src/Pages/UserPages/UserProjectPage.tsx @@ -1,25 +1,11 @@ -import { Link, useParams } from "react-router-dom"; import BasicWindow from "../../Components/BasicWindow"; import BackButton from "../../Components/BackButton"; +import UserProjectMenu from "../../Components/UserProjectMenu"; function UserProjectPage(): JSX.Element { - const { projectName } = useParams(); - const content = ( <> -

{projectName}

-
- -

- Your Time Reports -

- - -

- New Time Report -

- -
+ ); From 55a2dc7fac2a81b17e2fb4b5d31ff9486e0ce5ba Mon Sep 17 00:00:00 2001 From: Mattias Date: Tue, 19 Mar 2024 22:03:45 +0100 Subject: [PATCH 018/219] Add PMProjectMenu component and refactor PMProjectPage --- frontend/src/Components/PMProjectMenu.tsx | 34 +++++++++++++++++++ .../ProjectManagerPages/PMProjectPage.tsx | 28 ++------------- 2 files changed, 36 insertions(+), 26 deletions(-) create mode 100644 frontend/src/Components/PMProjectMenu.tsx diff --git a/frontend/src/Components/PMProjectMenu.tsx b/frontend/src/Components/PMProjectMenu.tsx new file mode 100644 index 0000000..ce7c5c5 --- /dev/null +++ b/frontend/src/Components/PMProjectMenu.tsx @@ -0,0 +1,34 @@ +import { Link, useParams } from "react-router-dom"; +import { JSX } from "react/jsx-runtime"; + +function PMProjectMenu(): JSX.Element { + const { projectName } = useParams(); + return ( + <> +

{projectName}

+
+ +

+ Your Time Reports +

+ + +

+ New Time Report +

+ + +

+ Statistics +

+ + +

+ Unsigned Time Reports +

+ +
+ + ); +} +export default PMProjectMenu; diff --git a/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx index b88ad58..4b0ef05 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx @@ -1,35 +1,11 @@ -import { Link, useParams } from "react-router-dom"; import BasicWindow from "../../Components/BasicWindow"; import { JSX } from "react/jsx-runtime"; +import PMProjectMenu from "../../Components/PMProjectMenu"; function PMProjectPage(): JSX.Element { - const { projectName } = useParams(); - const content = ( <> -

{projectName}

-
- -

- Your Time Reports -

- - -

- New Time Report -

- - -

- Statistics -

- - -

- Unsigned Time Reports -

- -
+ ); From 1974607fc705fc17165732ff6282bea955cd55b4 Mon Sep 17 00:00:00 2001 From: Johanna Date: Tue, 19 Mar 2024 22:04:20 +0100 Subject: [PATCH 019/219] Formating --- 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 1d029c3..4542855 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -54,7 +54,7 @@ export default function NewWeeklyReport(): JSX.Element { type="week" placeholder="Week" onChange={(e) => { - setWeek(parseInt(e.target.value)); + setWeek(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; From fe89ae0970b5f20bed63de576565678a278ec0fd Mon Sep 17 00:00:00 2001 From: Mattias Date: Tue, 19 Mar 2024 22:13:06 +0100 Subject: [PATCH 020/219] Added backbutton --- frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx index 4b0ef05..3d550f6 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx @@ -1,6 +1,7 @@ import BasicWindow from "../../Components/BasicWindow"; import { JSX } from "react/jsx-runtime"; import PMProjectMenu from "../../Components/PMProjectMenu"; +import BackButton from "../../Components/BackButton"; function PMProjectPage(): JSX.Element { const content = ( @@ -9,6 +10,12 @@ function PMProjectPage(): JSX.Element { ); - return ; + const buttons = ( + <> + + + ); + + return ; } export default PMProjectPage; From cc9678f375005e5cb2065f1d3e6eb7c89e797bdb Mon Sep 17 00:00:00 2001 From: Mattias Date: Tue, 19 Mar 2024 22:50:23 +0100 Subject: [PATCH 021/219] Created component for authorized route --- frontend/src/Components/AuthorizedRoute.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 frontend/src/Components/AuthorizedRoute.tsx diff --git a/frontend/src/Components/AuthorizedRoute.tsx b/frontend/src/Components/AuthorizedRoute.tsx new file mode 100644 index 0000000..d9c8c59 --- /dev/null +++ b/frontend/src/Components/AuthorizedRoute.tsx @@ -0,0 +1,18 @@ +import { Navigate } from "react-router-dom"; +import React from "react"; + +interface AuthorizedRouteProps { + children: React.ReactNode; + isAuthorized: boolean; +} + +export function AuthorizedRoute({ + children, + isAuthorized, +}: AuthorizedRouteProps): JSX.Element { + if (!isAuthorized) { + return ; + } + + return children as React.ReactElement; +} From cd2a81e7f2c755f31931e41b3fb7a308b306bd5d Mon Sep 17 00:00:00 2001 From: Mattias Date: Tue, 19 Mar 2024 22:50:40 +0100 Subject: [PATCH 022/219] Added unauthorizedpage and path in main --- frontend/src/Pages/UnauthorizedPage.tsx | 18 ++++++++++++++++++ frontend/src/main.tsx | 5 +++++ 2 files changed, 23 insertions(+) create mode 100644 frontend/src/Pages/UnauthorizedPage.tsx diff --git a/frontend/src/Pages/UnauthorizedPage.tsx b/frontend/src/Pages/UnauthorizedPage.tsx new file mode 100644 index 0000000..ed21b25 --- /dev/null +++ b/frontend/src/Pages/UnauthorizedPage.tsx @@ -0,0 +1,18 @@ +import Button from "../Components/Button"; + +export default function UnauthorizedPage(): JSX.Element { + return ( + + ); +} \ No newline at end of file diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 20ecbcd..29a67d1 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -30,6 +30,7 @@ import AdminProjectStatistics from "./Pages/AdminPages/AdminProjectStatistics.ts import AdminProjectViewMemberInfo from "./Pages/AdminPages/AdminProjectViewMemberInfo.tsx"; import AdminProjectPage from "./Pages/AdminPages/AdminProjectPage.tsx"; import NotFoundPage from "./Pages/NotFoundPage.tsx"; +import UnauthorizedPage from "./Pages/UnauthorizedPage.tsx"; // This is where the routes are mounted const router = createBrowserRouter([ @@ -146,6 +147,10 @@ const router = createBrowserRouter([ path: "/adminManageUser", element: , }, + { + path: "unauthorized", + element: , + }, ]); // Semi-hacky way to get the root element From 900c1614e9dc6c9621ab826f647249b55a61d23c Mon Sep 17 00:00:00 2001 From: Mattias Date: Tue, 19 Mar 2024 22:50:57 +0100 Subject: [PATCH 023/219] minor fix --- frontend/src/Pages/UnauthorizedPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Pages/UnauthorizedPage.tsx b/frontend/src/Pages/UnauthorizedPage.tsx index ed21b25..37e3d2c 100644 --- a/frontend/src/Pages/UnauthorizedPage.tsx +++ b/frontend/src/Pages/UnauthorizedPage.tsx @@ -15,4 +15,4 @@ export default function UnauthorizedPage(): JSX.Element {

); -} \ No newline at end of file +} From ffaa6616281dcd4165e575bd9d660b27e4908d8b Mon Sep 17 00:00:00 2001 From: Mattias Date: Tue, 19 Mar 2024 22:51:54 +0100 Subject: [PATCH 024/219] Minor fix --- frontend/src/main.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 29a67d1..ddf7119 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -148,7 +148,7 @@ const router = createBrowserRouter([ element: , }, { - path: "unauthorized", + path: "/unauthorized", element: , }, ]); From cb44954477c975d77e5f05588e13f4253acaa47b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20H=C3=B6gbom=20Aronson?= Date: Tue, 19 Mar 2024 23:08:14 +0100 Subject: [PATCH 025/219] -added auth for rolechange, endpoint and test --- .../handlers/handlers_project_related.go | 26 +++++++++++++--- backend/internal/types/project.go | 6 ++++ backend/main.go | 2 +- testing.py | 30 +++++++++++++++++++ 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index df9f3e0..2ab5869 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -49,13 +49,31 @@ func (gs *GState) GetUserProjects(c *fiber.Ctx) error { // ProjectRoleChange is a handler that changes a user's role within a project func (gs *GState) ProjectRoleChange(c *fiber.Ctx) error { + + //check token and get username of current user + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + projectManagerUsername := claims["name"].(string) + log.Info(projectManagerUsername) // Extract the necessary parameters from the request - username := c.Params("username") - projectName := c.Params("projectName") - role := c.Params("role") + data := new(types.RoleChange) + if err := c.BodyParser(data); err != nil { + log.Info("error parsing username, project or role") + return c.Status(400).SendString(err.Error()) + } + + // dubble diping and checcking if current user is + + if ismanager, err := gs.Db.IsProjectManager(projectManagerUsername, data.Projectname); err != nil { + log.Warn("Error checking if projectmanager:", err) + return c.Status(500).SendString(err.Error()) + } else if !ismanager { + log.Warn("tried chaning role when not projectmanager:", err) + return c.Status(401).SendString("you can not change role when not projectManager") + } // Change the user's role within the project in the database - if err := gs.Db.ChangeUserRole(username, projectName, role); err != nil { + if err := gs.Db.ChangeUserRole(data.Username, data.Projectname, data.Role); err != nil { return c.Status(500).SendString(err.Error()) } diff --git a/backend/internal/types/project.go b/backend/internal/types/project.go index 7e1747f..c336bcb 100644 --- a/backend/internal/types/project.go +++ b/backend/internal/types/project.go @@ -13,3 +13,9 @@ type NewProject struct { Name string `json:"name"` Description string `json:"description"` } + +type RoleChange struct { + Role string `json:"role" tstype:"'project_manager' | 'user'"` + Username string `json:"username"` + Projectname string `json:"projectname"` +} diff --git a/backend/main.go b/backend/main.go index abdf0db..60027fd 100644 --- a/backend/main.go +++ b/backend/main.go @@ -96,7 +96,7 @@ func main() { server.Get("/api/users/all", gs.ListAllUsers) server.Get("/api/getWeeklyReportsUser", gs.GetWeeklyReportsUserHandler) server.Get("api/checkIfProjectManager", gs.IsProjectManagerHandler) - + server.Post("/api/ProjectRoleChange", gs.ProjectRoleChange) // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) if err != nil { diff --git a/testing.py b/testing.py index 670abcd..0b803f5 100644 --- a/testing.py +++ b/testing.py @@ -39,7 +39,36 @@ 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" +#ta bort auth i handlern för att få testet att gå igenom +def test_ProjectRoleChange(): + dprint("Testing ProjectRoleChange") + project_manager = randomString() + register(project_manager, "project_manager_password") + + token = login(project_manager, "project_manager_password").json()[ + "token" + ] + response = requests.post( + addProjectPath, + json={"name": projectName, "description": "This is a project"}, + headers={"Authorization": "Bearer " + token}, + ) + response = requests.post( + ProjectRoleChangePath, + headers={"Authorization": "Bearer " + token}, + json={ + "username": username, + "projectName": projectName, + "week": 1 + }, + ) + if response.status_code != 200: + print("auth not working, för att man inte kan få tag på pm token atm, för att få igenom det så ta bort auth i handler") + + assert response.status_code == 200, "change role successfully" + def test_get_user_projects(): @@ -324,3 +353,4 @@ if __name__ == "__main__": test_add_user_to_project() test_get_weekly_reports_user() test_check_if_project_manager() + test_ProjectRoleChange() From 863a14c316eafb0d1d0dbc1d5392e771bc95c659 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Tue, 19 Mar 2024 23:44:00 +0100 Subject: [PATCH 026/219] Add WeeklyReport type and update getWeeklyReportsForProject method --- frontend/src/API/API.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index fc2367b..3ebf343 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -4,6 +4,7 @@ import { User, Project, NewProject, + WeeklyReport, } from "../Types/goTypes"; // This type of pattern should be hard to misuse @@ -49,9 +50,10 @@ interface API { token: string, ): Promise>; getWeeklyReportsForProject( + username: string, projectName: string, token: string, - ): Promise>; + ): Promise>; /** Gets all the projects of a user*/ getUserProjects(token: string): Promise>; /** Gets a project from id*/ @@ -272,15 +274,19 @@ export const api: API = { } }, - async getWeeklyReportsForProject(projectName: string, token: string) { + async getWeeklyReportsForProject( + username: string, + projectName: string, + token: string, + ) { try { - const response = await fetch("/api/getWeeklyReportsForProject", { + const response = await fetch("/api/getWeeklyReportsUser", { method: "GET", headers: { "Content-Type": "application/json", Authorization: "Bearer " + token, }, - body: JSON.stringify({ projectName }), + body: JSON.stringify({ username, projectName }), }); if (!response.ok) { @@ -289,13 +295,13 @@ export const api: API = { message: "Failed to get weekly reports for project", }; } else { - const data = (await response.json()) as NewWeeklyReport[]; + const data = (await response.json()) as WeeklyReport[]; return { success: true, data }; } } catch (e) { return { success: false, - message: "Failed to get weekly reports for project", + message: "fuck", }; } }, From b5d3ab7cb73e2a75473770b6166ce94d152bb776 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Tue, 19 Mar 2024 23:44:25 +0100 Subject: [PATCH 027/219] Refactor AllTimeReportsInProject component to use API for fetching weekly reports --- .../Components/AllTimeReportsInProject.tsx | 75 ++++--------------- 1 file changed, 16 insertions(+), 59 deletions(-) diff --git a/frontend/src/Components/AllTimeReportsInProject.tsx b/frontend/src/Components/AllTimeReportsInProject.tsx index 067712e..cc2bac4 100644 --- a/frontend/src/Components/AllTimeReportsInProject.tsx +++ b/frontend/src/Components/AllTimeReportsInProject.tsx @@ -1,68 +1,25 @@ import React, { useEffect, useState } from "react"; -import { NewWeeklyReport } from "../Types/goTypes"; +import { WeeklyReport } from "../Types/goTypes"; import { Link, useParams } from "react-router-dom"; +import { api } from "../API/API"; function AllTimeReportsInProject(): JSX.Element { const { projectName } = useParams(); - const [weeklyReports, setWeeklyReports] = useState([]); - - /* const getWeeklyReports = async (): Promise => { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getWeeklyReports(token); - console.log(response); - if (response.success) { - setWeeklyReports(response.data ?? []); - } else { - console.error(response.message); - } -}; */ + const [weeklyReports, setWeeklyReports] = useState([]); const getWeeklyReports = async (): Promise => { - const report: NewWeeklyReport[] = [ - { - projectName: projectName ?? "", - week: 10, - developmentTime: 1, - meetingTime: 1, - adminTime: 1, - ownWorkTime: 1, - studyTime: 1, - testingTime: 1, - }, - { - projectName: projectName ?? "", - week: 11, - developmentTime: 1, - meetingTime: 1, - adminTime: 1, - ownWorkTime: 100, - studyTime: 1, - testingTime: 1, - }, - { - projectName: projectName ?? "", - week: 12, - developmentTime: 1, - meetingTime: 1, - adminTime: 1, - ownWorkTime: 1, - studyTime: 1, - testingTime: 1000, - }, - { - projectName: projectName ?? "", - week: 20, - developmentTime: 1, - meetingTime: 1, - adminTime: 1, - ownWorkTime: 1, - studyTime: 1, - testingTime: 10000, - }, - // Add more reports as needed - ]; - setWeeklyReports(report); - await Promise.resolve(); + const token = localStorage.getItem("accessToken") ?? ""; + const response = await api.getWeeklyReportsForProject( + localStorage.getItem("username") ?? "", + projectName ?? "", + token, + ); + console.log(response); + if (response.success) { + setWeeklyReports(response.data ?? []); + } else { + console.error(response.message); + } }; // Call getProjects when the component mounts @@ -96,7 +53,7 @@ function AllTimeReportsInProject(): JSX.Element {

{"Signed: "} - YES + {newWeeklyReport.signedBy ? "YES" : "NO"}

From b7b831d869b2dc0f0f314339853209821f139b85 Mon Sep 17 00:00:00 2001 From: Melker Date: Tue, 19 Mar 2024 23:54:54 +0100 Subject: [PATCH 028/219] Lagt till om Admin=True/false --- backend/internal/handlers/handlers_user_related.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index 96fddb7..75b8953 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -101,10 +101,15 @@ func (gs *GState) Login(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusUnauthorized) } + isAdmin, err := gs.Db.IsSiteAdmin(u.Username) + if err != nil { + log.Info("Error checking admin status:", err) + return c.Status(500).SendString(err.Error()) +} // Create the Claims claims := jwt.MapClaims{ "name": u.Username, - "admin": false, + "admin": isAdmin, "exp": time.Now().Add(time.Hour * 72).Unix(), } From 8300fb3a6fe0ec4abe47a8e5bde02928b21b845d Mon Sep 17 00:00:00 2001 From: Peter KW Date: Wed, 20 Mar 2024 00:10:24 +0100 Subject: [PATCH 029/219] Added getAllUsers to API --- frontend/src/API/API.ts | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 8fd66d3..7f5c420 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -45,6 +45,8 @@ interface API { getUserProjects(token: string): Promise>; /** Gets a project from id*/ getProject(id: number): Promise>; + /** Gets a project from id*/ + getAllUsers(token: string): Promise>; } // Export an instance of the API @@ -81,7 +83,7 @@ export const api: API = { token: string, ): Promise> { try { - const response = await fetch("/api/userdelete", { + const response = await fetch(`/api/userdelete/${username}`, { method: "POST", headers: { "Content-Type": "application/json", @@ -278,4 +280,32 @@ export const api: API = { }; } }, + + // Gets all users + async getAllUsers(token: string): Promise> { + try { + const response = await fetch("/api/users/all", { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, + }); + + if (!response.ok) { + return Promise.resolve({ + success: false, + message: "Failed to get users", + }); + } else { + const data = (await response.json()) as string[]; + return Promise.resolve({ success: true, data }); + } + } catch (e) { + return Promise.resolve({ + success: false, + message: "API is not ok", + }); + } + }, }; From cc231dbfaad1f5adbff021cd6270ad7db2f38ba3 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Wed, 20 Mar 2024 00:14:23 +0100 Subject: [PATCH 030/219] Component for getting all users --- frontend/src/Components/GetAllUsers.tsx | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/src/Components/GetAllUsers.tsx diff --git a/frontend/src/Components/GetAllUsers.tsx b/frontend/src/Components/GetAllUsers.tsx new file mode 100644 index 0000000..73ad244 --- /dev/null +++ b/frontend/src/Components/GetAllUsers.tsx @@ -0,0 +1,35 @@ +import { Dispatch, useEffect } from "react"; +import { api } from "../API/API"; + +/** + * Gets all usernames in the system and puts them in an array + * @param props - A setStateAction for the array you want to put users in + * @returns {void} Nothing + * @example + * const [users, setUsers] = useState([]); + * GetAllUsers({ setUsersProp: setUsers }); + */ +function GetAllUsers(props: { + setUsersProp: Dispatch>; +}): void { + const setUsers: Dispatch> = props.setUsersProp; + useEffect(() => { + const fetchUsers = async (): Promise => { + try { + const token = localStorage.getItem("accessToken") ?? ""; + const response = await api.getAllUsers(token); + if (response.success) { + setUsers(response.data ?? []); + } else { + console.error("Failed to fetch users:", response.message); + } + } catch (error) { + console.error("Error fetching users:", error); + } + }; + + void fetchUsers(); + }, [setUsers]); +} + +export default GetAllUsers; From b20817ec8aafd833b4e37e989db5cf45b830a7a9 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Wed, 20 Mar 2024 00:16:30 +0100 Subject: [PATCH 031/219] Small fix to props and imports --- frontend/src/Components/UserListAdmin.tsx | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/frontend/src/Components/UserListAdmin.tsx b/frontend/src/Components/UserListAdmin.tsx index 3d2bcae..76cae9f 100644 --- a/frontend/src/Components/UserListAdmin.tsx +++ b/frontend/src/Components/UserListAdmin.tsx @@ -1,14 +1,6 @@ import { useState } from "react"; -import { PublicUser } from "../Types/goTypes"; import UserInfoModal from "./UserInfoModal"; -/** - * The props for the UserProps component - */ -interface UserProps { - users: PublicUser[]; -} - /** * A list of users for admin manage users page, that sets an onClick * function for eact user
  • element, which displays a modul with @@ -20,7 +12,7 @@ interface UserProps { * return ; */ -export function UserListAdmin(props: UserProps): JSX.Element { +export function UserListAdmin(props: { users: string[] }): JSX.Element { const [modalVisible, setModalVisible] = useState(false); const [username, setUsername] = useState(""); @@ -46,12 +38,12 @@ export function UserListAdmin(props: UserProps): JSX.Element { {props.users.map((user) => (
  • { - handleClick(user.username); + handleClick(user); }} > - {user.username} + {user}
  • ))} From 4e100229f5c3c84d65865547de4c8e1cd03fc2d1 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Wed, 20 Mar 2024 00:16:56 +0100 Subject: [PATCH 032/219] Now uses GetAllUsers --- frontend/src/Pages/AdminPages/AdminManageUsers.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/src/Pages/AdminPages/AdminManageUsers.tsx b/frontend/src/Pages/AdminPages/AdminManageUsers.tsx index 0939d77..353fddc 100644 --- a/frontend/src/Pages/AdminPages/AdminManageUsers.tsx +++ b/frontend/src/Pages/AdminPages/AdminManageUsers.tsx @@ -2,15 +2,13 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; import BackButton from "../../Components/BackButton"; import { UserListAdmin } from "../../Components/UserListAdmin"; -import { PublicUser } from "../../Types/goTypes"; import { useNavigate } from "react-router-dom"; +import GetAllUsers from "../../Components/GetAllUsers"; +import { useState } from "react"; function AdminManageUsers(): JSX.Element { - //TODO: Change so that it reads users from database - const users: PublicUser[] = []; - for (let i = 1; i <= 20; i++) { - users.push({ userId: "id" + i, username: "Example User " + i }); - } + const [users, setUsers] = useState([]); + GetAllUsers({ setUsersProp: setUsers }); const navigate = useNavigate(); From b13a8b54323db49cb72f7f248f4b867b7d993264 Mon Sep 17 00:00:00 2001 From: dDogge Date: Wed, 20 Mar 2024 00:35:37 +0100 Subject: [PATCH 033/219] Merge --- .../handlers/handlers_project_related.go | 25 +++++++++++++++++ backend/main.go | 2 ++ testing.py | 28 +++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index 2ab5869..a92616d 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -118,6 +118,31 @@ func (gs *GState) ListAllUsersProject(c *fiber.Ctx) error { return c.Status(400).SendString("No project name provided") } + // Get the user token + userToken := c.Locals("user").(*jwt.Token) + claims := userToken.Claims.(jwt.MapClaims) + username := claims["name"].(string) + + // Check if the user is a project manager for the specified project + isManager, err := gs.Db.IsProjectManager(username, projectName) + if err != nil { + log.Info("Error checking project manager status:", err) + return c.Status(500).SendString(err.Error()) + } + + // If the user is not a project manager, check if the user is a site admin + if !isManager { + isAdmin, err := gs.Db.IsSiteAdmin(username) + if err != nil { + log.Info("Error checking admin status:", err) + return c.Status(500).SendString(err.Error()) + } + if !isAdmin { + log.Info("User is neither a project manager nor a site admin:", username) + return c.Status(403).SendString("User is neither a project manager nor a site admin") + } + } + // Get all users associated with the project from the database users, err := gs.Db.GetAllUsersProject(projectName) if err != nil { diff --git a/backend/main.go b/backend/main.go index 60027fd..b907177 100644 --- a/backend/main.go +++ b/backend/main.go @@ -97,6 +97,8 @@ func main() { server.Get("/api/getWeeklyReportsUser", gs.GetWeeklyReportsUserHandler) server.Get("api/checkIfProjectManager", gs.IsProjectManagerHandler) server.Post("/api/ProjectRoleChange", gs.ProjectRoleChange) + server.Get("/api/getUsersProject/:projectName", gs.ListAllUsersProject) + // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) if err != nil { diff --git a/testing.py b/testing.py index 0b803f5..491419f 100644 --- a/testing.py +++ b/testing.py @@ -40,6 +40,7 @@ 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" #ta bort auth i handlern för att få testet att gå igenom def test_ProjectRoleChange(): @@ -338,7 +339,33 @@ def test_check_if_project_manager(): assert response.status_code == 200, "Check if project manager failed" gprint("test_check_if_project_manager 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") if __name__ == "__main__": @@ -354,3 +381,4 @@ if __name__ == "__main__": test_get_weekly_reports_user() test_check_if_project_manager() test_ProjectRoleChange() + test_list_all_users_project() From 7b78767f5016251fe1d47feecc4c56385526fec5 Mon Sep 17 00:00:00 2001 From: al8763be Date: Wed, 20 Mar 2024 01:14:30 +0100 Subject: [PATCH 034/219] restore --- frontend/src/API/API.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index fc2367b..6a52fb8 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -4,6 +4,7 @@ import { User, Project, NewProject, + WeeklyReport, } from "../Types/goTypes"; // This type of pattern should be hard to misuse @@ -49,9 +50,10 @@ interface API { token: string, ): Promise>; getWeeklyReportsForProject( + username: string, projectName: string, token: string, - ): Promise>; + ): Promise>; /** Gets all the projects of a user*/ getUserProjects(token: string): Promise>; /** Gets a project from id*/ @@ -272,15 +274,19 @@ export const api: API = { } }, - async getWeeklyReportsForProject(projectName: string, token: string) { + async getWeeklyReportsForProject( + username: string, + projectName: string, + token: string, + ) { try { - const response = await fetch("/api/getWeeklyReportsForProject", { + const response = await fetch("/api/getWeeklyReportsUser", { method: "GET", headers: { "Content-Type": "application/json", Authorization: "Bearer " + token, }, - body: JSON.stringify({ projectName }), + body: JSON.stringify({ username, projectName }), }); if (!response.ok) { @@ -289,7 +295,7 @@ export const api: API = { message: "Failed to get weekly reports for project", }; } else { - const data = (await response.json()) as NewWeeklyReport[]; + const data = (await response.json()) as WeeklyReport[]; return { success: true, data }; } } catch (e) { From 033268f4eff1908317f86e93f7c73664860ebe9d Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 08:47:11 +0100 Subject: [PATCH 035/219] Added info to component --- frontend/src/Components/AllTimeReportsInProject.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/Components/AllTimeReportsInProject.tsx b/frontend/src/Components/AllTimeReportsInProject.tsx index cc2bac4..809e6ed 100644 --- a/frontend/src/Components/AllTimeReportsInProject.tsx +++ b/frontend/src/Components/AllTimeReportsInProject.tsx @@ -1,3 +1,6 @@ +//Info: This component is used to display all the time reports for a project. It will display the week number, +//total time spent, and if the report has been signed or not. The user can click on a report to edit it. + import React, { useEffect, useState } from "react"; import { WeeklyReport } from "../Types/goTypes"; import { Link, useParams } from "react-router-dom"; From a468b896db0018d70e6b350b76a4f0cdcd1f9143 Mon Sep 17 00:00:00 2001 From: borean Date: Wed, 20 Mar 2024 10:41:16 +0100 Subject: [PATCH 036/219] WIP for funcs that need to be implemented --- backend/internal/handlers/global_state.go | 27 +++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 5d01368..1492a09 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -22,20 +22,19 @@ type GlobalState interface { PromoteToAdmin(c *fiber.Ctx) error GetWeeklyReportsUserHandler(c *fiber.Ctx) error IsProjectManagerHandler(c *fiber.Ctx) error - // GetProject(c *fiber.Ctx) error // To get a specific project - // UpdateProject(c *fiber.Ctx) error // To update a project - // DeleteProject(c *fiber.Ctx) error // To delete a project - // CreateTask(c *fiber.Ctx) error // To create a new task - // GetTasks(c *fiber.Ctx) error // To get all tasks - // GetTask(c *fiber.Ctx) error // To get a specific task - // UpdateTask(c *fiber.Ctx) error // To update a task - // DeleteTask(c *fiber.Ctx) error // To delete a task - // CreateCollection(c *fiber.Ctx) error // To create a new collection - // GetCollections(c *fiber.Ctx) error // To get all collections - // GetCollection(c *fiber.Ctx) error // To get a specific collection - // UpdateCollection(c *fiber.Ctx) error // To update a collection - // DeleteCollection(c *fiber.Ctx) error // To delete a collection - // SignCollection(c *fiber.Ctx) error // To sign a collection + // UpdateProject(c *fiber.Ctx) error // To update a project // WIP + // DeleteProject(c *fiber.Ctx) error // To delete a project // WIP + // CreateTask(c *fiber.Ctx) error // To create a new task // WIP + // GetTasks(c *fiber.Ctx) error // To get all tasks // WIP + // GetTask(c *fiber.Ctx) error // To get a specific task // WIP + // UpdateTask(c *fiber.Ctx) error // To update a task // WIP + // DeleteTask(c *fiber.Ctx) error // To delete a task // WIP + // CreateCollection(c *fiber.Ctx) error // To create a new collection // WIP + // GetCollections(c *fiber.Ctx) error // To get all collections // WIP + // GetCollection(c *fiber.Ctx) error // To get a specific collection // WIP + // UpdateCollection(c *fiber.Ctx) error // To update a collection // WIP + // DeleteCollection(c *fiber.Ctx) error // To delete a collection // WIP + // SignCollection(c *fiber.Ctx) error // To sign a collection // WIP ListAllUsers(c *fiber.Ctx) error // To get a list of all users in the application database ListAllUsersProject(c *fiber.Ctx) error // To get a list of all users for a specific project ProjectRoleChange(c *fiber.Ctx) error // To change a users role in a project From cd74758b2f647c291e86b96be1f8c6c3e345c0c1 Mon Sep 17 00:00:00 2001 From: borean Date: Wed, 20 Mar 2024 11:09:48 +0100 Subject: [PATCH 037/219] DeleteProject added in db.go, untested --- backend/internal/database/db.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 842ffbd..92cbc71 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -3,6 +3,7 @@ package database import ( "embed" "errors" + "fmt" "path/filepath" "ttime/internal/types" @@ -19,6 +20,7 @@ type Database interface { PromoteToAdmin(username string) error GetUserId(username string) (int, error) AddProject(name string, description string, username string) error + // DeleteProject(name string, username string) error Migrate() error MigrateSampleData() error GetProjectId(projectname string) (int, error) @@ -70,6 +72,8 @@ const getProjectsForUser = `SELECT p.id, p.name, p.description FROM projects p JOIN user_roles ur ON p.id = ur.project_id JOIN users u ON ur.user_id = u.id WHERE u.username = ?` +const deleteProject = `DELETE FROM projects + WHERE id = ? AND owner_username = ?` // DbConnect connects to the database func DbConnect(dbpath string) Database { @@ -225,6 +229,21 @@ func (d *Db) AddProject(name string, description string, username string) error return err } +func (d *Db) DeleteProject(projectID string, username string) error { + tx := d.MustBegin() + + _, err := tx.Exec(deleteProject, projectID, username) + + if err != nil { + if rollbackErr := tx.Rollback(); rollbackErr != nil { + return fmt.Errorf("error rolling back transaction: %v, delete error: %v", rollbackErr, err) + } + panic(err) + } + + return err +} + func (d *Db) GetAllUsersProject(projectname string) ([]UserProjectMember, error) { // Define the SQL query to fetch users and their roles for a given project query := ` From dce91943b329eb3a308d7bcbd72625959f59269a Mon Sep 17 00:00:00 2001 From: borean Date: Wed, 20 Mar 2024 11:22:33 +0100 Subject: [PATCH 038/219] Added DeleteProject handler, untested --- backend/internal/database/db.go | 2 +- .../internal/handlers/handlers_project_related.go | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 92cbc71..680d7e2 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -20,7 +20,7 @@ type Database interface { PromoteToAdmin(username string) error GetUserId(username string) (int, error) AddProject(name string, description string, username string) error - // DeleteProject(name string, username string) error + DeleteProject(name string, username string) error Migrate() error MigrateSampleData() error GetProjectId(projectname string) (int, error) diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index a92616d..be4ffef 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -30,6 +30,18 @@ func (gs *GState) CreateProject(c *fiber.Ctx) error { return c.Status(200).SendString("Project added") } +func (gs *GState) DeleteProject(c *fiber.Ctx) error { + + projectID := c.Params("projectID") + username := c.Params("username") + + if err := gs.Db.DeleteProject(projectID, username); err != nil { + return c.Status(500).SendString((err.Error())) + } + + return c.Status(200).SendString("Project deleted") +} + // GetUserProjects returns all projects that the user is a member of func (gs *GState) GetUserProjects(c *fiber.Ctx) error { // First we get the username from the token From c01dd21b0a202220fb9cb8a6ef12d9e619fed2f9 Mon Sep 17 00:00:00 2001 From: borean Date: Wed, 20 Mar 2024 11:23:24 +0100 Subject: [PATCH 039/219] uncommented --- backend/internal/handlers/global_state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 1492a09..7a4213b 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -23,7 +23,7 @@ type GlobalState interface { GetWeeklyReportsUserHandler(c *fiber.Ctx) error IsProjectManagerHandler(c *fiber.Ctx) error // UpdateProject(c *fiber.Ctx) error // To update a project // WIP - // DeleteProject(c *fiber.Ctx) error // To delete a project // WIP + DeleteProject(c *fiber.Ctx) error // To delete a project // WIP // CreateTask(c *fiber.Ctx) error // To create a new task // WIP // GetTasks(c *fiber.Ctx) error // To get all tasks // WIP // GetTask(c *fiber.Ctx) error // To get a specific task // WIP From bb93cef1d3fbda86d34fb3053b4dd8ceeb4d305c Mon Sep 17 00:00:00 2001 From: borean Date: Wed, 20 Mar 2024 11:43:47 +0100 Subject: [PATCH 040/219] New deleteproject handler, WIP --- backend/internal/handlers/handlers_project_related.go | 5 +++++ backend/main.go | 1 + 2 files changed, 6 insertions(+) diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index be4ffef..7b95c26 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -226,3 +226,8 @@ func (gs *GState) IsProjectManagerHandler(c *fiber.Ctx) error { // Return the result as JSON return c.JSON(map[string]bool{"isProjectManager": isManager}) } + +func (gs *GState) CreateTask(c *fiber.Ctx) error { + + return nil +} diff --git a/backend/main.go b/backend/main.go index b907177..d0ccdb1 100644 --- a/backend/main.go +++ b/backend/main.go @@ -87,6 +87,7 @@ func main() { server.Get("/api/getUserProjects", gs.GetUserProjects) server.Post("/api/loginrenew", gs.LoginRenew) server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches + server.Delete("api/project", gs.DeleteProject) // WIP server.Post("/api/project", gs.CreateProject) server.Get("/api/project/:projectId", gs.GetProject) server.Get("/api/getWeeklyReport", gs.GetWeeklyReport) From a5adec82e24b959af3d9c0982e66f94987fe4d20 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Wed, 20 Mar 2024 11:51:04 +0100 Subject: [PATCH 041/219] Modul for showing projectinfo *not finished* --- frontend/src/Components/ProjectInfoModal.tsx | 44 ++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 frontend/src/Components/ProjectInfoModal.tsx diff --git a/frontend/src/Components/ProjectInfoModal.tsx b/frontend/src/Components/ProjectInfoModal.tsx new file mode 100644 index 0000000..11d29c1 --- /dev/null +++ b/frontend/src/Components/ProjectInfoModal.tsx @@ -0,0 +1,44 @@ +import Button from "./Button"; + +function UserInfoModal(props: { + isVisible: boolean; + projectname: string; + onClose: () => void; +}): JSX.Element { + if (!props.isVisible) return <>; + + return ( +
    +
    +

    {props.projectname}

    +
    +

    + Members of this project: +

    +
    {/*Show all members in project*/}
    +
    +
    +
    +
    +
    + ); +} + +export default UserInfoModal; From e271794b57faf12b1635ddc6fde73adcb5784196 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Wed, 20 Mar 2024 11:51:50 +0100 Subject: [PATCH 042/219] List for showing all projects as admin --- frontend/src/Components/ProjectListAdmin.tsx | 56 ++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 frontend/src/Components/ProjectListAdmin.tsx diff --git a/frontend/src/Components/ProjectListAdmin.tsx b/frontend/src/Components/ProjectListAdmin.tsx new file mode 100644 index 0000000..dbc7403 --- /dev/null +++ b/frontend/src/Components/ProjectListAdmin.tsx @@ -0,0 +1,56 @@ +import { useState } from "react"; +import { NewProject } from "../Types/goTypes"; +import ProjectInfoModal from "./ProjectInfoModal"; + +/** + * A list of projects for admin manage projects page, that sets an onClick + * function for eact project
  • element, which displays a modul with + * user info. + * @param props - An array of projects to display + * @returns {JSX.Element} The project list + * @example + * const projects: NewProject[] = [{ name: "Project", description: "New" }]; + * return + */ + +export function ProjectListAdmin(props: { + projects: NewProject[]; +}): JSX.Element { + const [modalVisible, setModalVisible] = useState(false); + const [projectname, setProjectname] = useState(""); + + const handleClick = (username: string): void => { + setProjectname(username); + setModalVisible(true); + }; + + const handleClose = (): void => { + setProjectname(""); + setModalVisible(false); + }; + + return ( + <> + +
    +
      + {props.projects.map((project) => ( +
    • { + handleClick(project.name); + }} + > + {project.name} +
    • + ))} +
    +
    + + ); +} From 6317c7674c0957a9b3eed01d7ade1143a57b38c7 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Wed, 20 Mar 2024 11:53:27 +0100 Subject: [PATCH 043/219] Now uses projectListAdmin to show projects --- .../src/Pages/AdminPages/AdminManageProjects.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/src/Pages/AdminPages/AdminManageProjects.tsx b/frontend/src/Pages/AdminPages/AdminManageProjects.tsx index 177f55b..14e44be 100644 --- a/frontend/src/Pages/AdminPages/AdminManageProjects.tsx +++ b/frontend/src/Pages/AdminPages/AdminManageProjects.tsx @@ -2,9 +2,20 @@ import { Link } from "react-router-dom"; import BackButton from "../../Components/BackButton"; import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; +import { ProjectListAdmin } from "../../Components/ProjectListAdmin"; +import { NewProject } from "../../Types/goTypes"; function AdminManageProjects(): JSX.Element { - const content = <>; + //TODO: Use fetch to get all projects + const projects: NewProject[] = [{ name: "Project", description: "New" }]; + const content = ( + <> +

    Manage Projects

    +
    + +
    + + ); const buttons = ( <> From 1919b7cb990d11f4769e713bcb5189b13f5fb0c7 Mon Sep 17 00:00:00 2001 From: borean Date: Wed, 20 Mar 2024 12:11:05 +0100 Subject: [PATCH 044/219] Added ChangeUserName in db --- backend/internal/database/db.go | 16 +++++++++ backend/internal/handlers/global_state.go | 1 + .../handlers/handlers_project_related.go | 34 +++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 680d7e2..8b0c8c4 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -27,6 +27,7 @@ type Database interface { AddWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error AddUserToProject(username string, projectname string, role string) error ChangeUserRole(username string, projectname string, role string) error + ChangeUserName(username string, newname string) error GetAllUsersProject(projectname string) ([]UserProjectMember, error) GetAllUsersApplication() ([]string, error) GetProjectsForUser(username string) ([]types.Project, error) @@ -67,6 +68,7 @@ const addWeeklyReport = `WITH UserLookup AS (SELECT id FROM users WHERE username VALUES ((SELECT id FROM ProjectLookup), (SELECT id FROM UserLookup),?, ?, ?, ?, ?, ?, ?);` const addUserToProject = "INSERT INTO user_roles (user_id, project_id, p_role) VALUES (?, ?, ?)" // WIP const changeUserRole = "UPDATE user_roles SET p_role = ? WHERE user_id = ? AND project_id = ?" +const changeUserName = "UPDATE user SET username = ? WHERE user_id = ?" // WIP const getProjectsForUser = `SELECT p.id, p.name, p.description FROM projects p JOIN user_roles ur ON p.id = ur.project_id @@ -169,6 +171,20 @@ func (d *Db) ChangeUserRole(username string, projectname string, role string) er return err3 } +// ChangeUserRole changes the role of a user within a project. +func (d *Db) ChangeUserName(username string, newname string) error { + // Get the user ID + var userid int + userid, err := d.GetUserId(username) + if err != nil { + panic(err) + } + + // Execute the SQL query to change the user's role + _, err2 := d.Exec(changeUserName, username, userid) + return err2 +} + // GetUserRole retrieves the role of a user within a project. func (d *Db) GetUserRole(username string, projectname string) (string, error) { var role string diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 7a4213b..1934b21 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -38,6 +38,7 @@ type GlobalState interface { ListAllUsers(c *fiber.Ctx) error // To get a list of all users in the application database ListAllUsersProject(c *fiber.Ctx) error // To get a list of all users for a specific project ProjectRoleChange(c *fiber.Ctx) error // To change a users role in a project + // ProjectNameChange(c * fiber.Ctx) error // WIP } // "Constructor" diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index 7b95c26..055328b 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -93,6 +93,40 @@ func (gs *GState) ProjectRoleChange(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) } +// ProjectRoleChange is a handler that changes a user's role within a project +func (gs *GState) ProjectNameChange(c *fiber.Ctx) error { + + //check token and get username of current user + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + projectManagerUsername := claims["name"].(string) + log.Info(projectManagerUsername) + // Extract the necessary parameters from the request + data := new(types.RoleChange) + if err := c.BodyParser(data); err != nil { + log.Info("error parsing username, project or role") + return c.Status(400).SendString(err.Error()) + } + + // dubble diping and checcking if current user is + + if ismanager, err := gs.Db.IsProjectManager(projectManagerUsername, data.Projectname); err != nil { + log.Warn("Error checking if projectmanager:", err) + return c.Status(500).SendString(err.Error()) + } else if !ismanager { + log.Warn("tried chaning name when not projectmanager:", err) + return c.Status(401).SendString("you can not change name when not projectManager") + } + + // Change the user's role within the project in the database + if err := gs.Db.ChangeUserRole(data.Username, data.Projectname, data.Role); err != nil { + return c.Status(500).SendString(err.Error()) + } + + // Return a success message + return c.SendStatus(fiber.StatusOK) +} + // GetProject retrieves a specific project by its ID func (gs *GState) GetProject(c *fiber.Ctx) error { // Extract the project ID from the request parameters or body From cea2b6c03cba35a49a421a93420009fb59ebc9cf Mon Sep 17 00:00:00 2001 From: Peter KW Date: Wed, 20 Mar 2024 12:25:07 +0100 Subject: [PATCH 045/219] GetProjects compo --- frontend/src/Components/GetProjects.tsx | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 frontend/src/Components/GetProjects.tsx diff --git a/frontend/src/Components/GetProjects.tsx b/frontend/src/Components/GetProjects.tsx new file mode 100644 index 0000000..d6ab1f7 --- /dev/null +++ b/frontend/src/Components/GetProjects.tsx @@ -0,0 +1,37 @@ +import { Dispatch, useEffect } from "react"; +import { Project } from "../Types/goTypes"; +import { api } from "../API/API"; + +/** + * Gets all projects that user is a member of + * @param props - A setStateAction for the array you want to put projects in + * @returns {void} Nothing + * @example + * const [projects, setProjects] = useState([]); + * GetAllUsers({ setProjectsProp: setProjects }); + */ +function GetProjects(props: { + setProjectsProp: Dispatch>; +}): void { + const setProjects: Dispatch> = + props.setProjectsProp; + useEffect(() => { + const fetchUsers = async (): Promise => { + try { + const token = localStorage.getItem("accessToken") ?? ""; + const response = await api.getUserProjects(token); + if (response.success) { + setProjects(response.data ?? []); + } else { + console.error("Failed to fetch projects:", response.message); + } + } catch (error) { + console.error("Error fetching projects:", error); + } + }; + + void fetchUsers(); + }, [setProjects]); +} + +export default GetProjects; From 33b269e0c987faacfe8e487c516ae772cb880736 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Wed, 20 Mar 2024 12:25:59 +0100 Subject: [PATCH 046/219] Now uses GetProjects --- frontend/src/Pages/AdminPages/AdminManageProjects.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/Pages/AdminPages/AdminManageProjects.tsx b/frontend/src/Pages/AdminPages/AdminManageProjects.tsx index 14e44be..7ea45df 100644 --- a/frontend/src/Pages/AdminPages/AdminManageProjects.tsx +++ b/frontend/src/Pages/AdminPages/AdminManageProjects.tsx @@ -3,11 +3,13 @@ import BackButton from "../../Components/BackButton"; import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; import { ProjectListAdmin } from "../../Components/ProjectListAdmin"; -import { NewProject } from "../../Types/goTypes"; +import { Project } from "../../Types/goTypes"; +import GetProjects from "../../Components/GetProjects"; +import { useState } from "react"; function AdminManageProjects(): JSX.Element { - //TODO: Use fetch to get all projects - const projects: NewProject[] = [{ name: "Project", description: "New" }]; + const [projects, setProjects] = useState([]); + GetProjects({ setProjectsProp: setProjects }); const content = ( <>

    Manage Projects

    From 1b21b2574aa51dc16443792fe084a6dae55fd572 Mon Sep 17 00:00:00 2001 From: Mattias Date: Wed, 20 Mar 2024 12:36:30 +0100 Subject: [PATCH 047/219] Comp for displaying projectmembers and changed path in main --- frontend/src/Components/ProjectMembers.tsx | 99 +++++++++++++++++++ .../ProjectManagerPages/PMProjectMembers.tsx | 13 ++- frontend/src/main.tsx | 8 +- 3 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 frontend/src/Components/ProjectMembers.tsx diff --git a/frontend/src/Components/ProjectMembers.tsx b/frontend/src/Components/ProjectMembers.tsx new file mode 100644 index 0000000..e1f261e --- /dev/null +++ b/frontend/src/Components/ProjectMembers.tsx @@ -0,0 +1,99 @@ +import { useEffect, useState } from "react"; +import { Link, useParams } from "react-router-dom"; + +function ProjectMembers(): JSX.Element { + const { projectName } = useParams(); + const [projectMembers, setProjectMembers] = useState([]); + + // const getProjectMembers = async (): Promise => { + // const token = localStorage.getItem("accessToken") ?? ""; + // const response = await api.getProjectMembers(projectName ?? "", token); + // console.log(response); + // if (response.success) { + // setProjectMembers(response.data ?? []); + // } else { + // console.error(response.message); + // } + // }; + + interface ProjectMember { + username: string; + role: string; + } + + const mockProjectMembers = [ + { + username: "username1", + role: "Project Manager", + }, + { + username: "username2", + role: "System Manager", + }, + { + username: "username3", + role: "Developer", + }, + { + username: "username4", + role: "Tester", + }, + { + username: "username5", + role: "Tester", + }, + { + username: "username6", + role: "Tester", + }, + ]; + + const getProjectMembers = async (): Promise => { + // Use the mock data + setProjectMembers(mockProjectMembers); + + await Promise.resolve(); + }; + + useEffect(() => { + void getProjectMembers(); + }, []); + + return ( + <> +
    + {projectMembers.map((projectMember, index) => ( +

    +
    +
    +

    {projectMember.username}

    + Role: +

    {projectMember.role}

    +
    +
    +
    + +

    + View Reports +

    + + +

    + Change Role +

    + +
    +
    +
    +

    + ))} +
    + + ); +} + +export default ProjectMembers; diff --git a/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx b/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx index 9fe96cf..11b8636 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx @@ -1,10 +1,19 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; import BackButton from "../../Components/BackButton"; -import { Link } from "react-router-dom"; +import { Link, useParams } from "react-router-dom"; +import ProjectMembers from "../../Components/ProjectMembers"; function PMProjectMembers(): JSX.Element { - const content = <>; + const { projectName } = useParams(); + const content = ( + <> +

    + All Members In: {projectName}{" "} +

    + + + ); const buttons = ( <> diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 25c7a35..bac2292 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -64,7 +64,7 @@ const router = createBrowserRouter([ element: , }, { - path: "/changeRole", + path: "/changeRole/:projectName/:username", element: , }, { @@ -72,11 +72,11 @@ const router = createBrowserRouter([ element: , }, { - path: "/projectMembers", + path: "/projectMembers/:projectName", element: , }, { - path: "/PMProjectPage", + path: "/PMProjectPage/:projectName", element: , }, { @@ -88,7 +88,7 @@ const router = createBrowserRouter([ element: , }, { - path: "/unsignedReports", + path: "/unsignedReports/:projectName", element: , }, { From 3515a86bbb7dd6062efc9e94dae3c6252a99270b Mon Sep 17 00:00:00 2001 From: borean Date: Wed, 20 Mar 2024 12:50:04 +0100 Subject: [PATCH 048/219] Added ChangeUserName handler, untested and WIP, --- backend/internal/handlers/global_state.go | 2 +- .../handlers/handlers_project_related.go | 34 ----------- .../handlers/handlers_user_related.go | 58 +++++++++++++++---- backend/internal/types/project.go | 5 ++ backend/main.go | 1 + 5 files changed, 53 insertions(+), 47 deletions(-) diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 1934b21..4a4b73f 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -38,7 +38,7 @@ type GlobalState interface { ListAllUsers(c *fiber.Ctx) error // To get a list of all users in the application database ListAllUsersProject(c *fiber.Ctx) error // To get a list of all users for a specific project ProjectRoleChange(c *fiber.Ctx) error // To change a users role in a project - // ProjectNameChange(c * fiber.Ctx) error // WIP + ChangeUserName(c *fiber.Ctx) error // WIP } // "Constructor" diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index 055328b..7b95c26 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -93,40 +93,6 @@ func (gs *GState) ProjectRoleChange(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) } -// ProjectRoleChange is a handler that changes a user's role within a project -func (gs *GState) ProjectNameChange(c *fiber.Ctx) error { - - //check token and get username of current user - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - projectManagerUsername := claims["name"].(string) - log.Info(projectManagerUsername) - // Extract the necessary parameters from the request - data := new(types.RoleChange) - if err := c.BodyParser(data); err != nil { - log.Info("error parsing username, project or role") - return c.Status(400).SendString(err.Error()) - } - - // dubble diping and checcking if current user is - - if ismanager, err := gs.Db.IsProjectManager(projectManagerUsername, data.Projectname); err != nil { - log.Warn("Error checking if projectmanager:", err) - return c.Status(500).SendString(err.Error()) - } else if !ismanager { - log.Warn("tried chaning name when not projectmanager:", err) - return c.Status(401).SendString("you can not change name when not projectManager") - } - - // Change the user's role within the project in the database - if err := gs.Db.ChangeUserRole(data.Username, data.Projectname, data.Role); err != nil { - return c.Status(500).SendString(err.Error()) - } - - // Return a success message - return c.SendStatus(fiber.StatusOK) -} - // GetProject retrieves a specific project by its ID func (gs *GState) GetProject(c *fiber.Ctx) error { // Extract the project ID from the request parameters or body diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index 75b8953..862416c 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -105,7 +105,7 @@ func (gs *GState) Login(c *fiber.Ctx) error { if err != nil { log.Info("Error checking admin status:", err) return c.Status(500).SendString(err.Error()) -} + } // Create the Claims claims := jwt.MapClaims{ "name": u.Username, @@ -187,17 +187,17 @@ func (gs *GState) ListAllUsers(c *fiber.Ctx) error { return c.JSON(users) } -// @Summary PromoteToAdmin -// @Description promote chosen user to admin -// @Tags User -// @Accept json -// @Produce plain -// @Param NewUser body types.NewUser true "user info" -// @Success 200 {json} json "Successfully prometed user" -// @Failure 400 {string} string "bad request" -// @Failure 401 {string} string "Unauthorized" -// @Failure 500 {string} string "Internal server error" -// @Router /promoteToAdmin [post] +// @Summary PromoteToAdmin +// @Description promote chosen user to admin +// @Tags User +// @Accept json +// @Produce plain +// @Param NewUser body types.NewUser true "user info" +// @Success 200 {json} json "Successfully prometed user" +// @Failure 400 {string} string "bad request" +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" +// @Router /promoteToAdmin [post] func (gs *GState) PromoteToAdmin(c *fiber.Ctx) error { // Extract the username from the request body var newUser types.NewUser @@ -219,3 +219,37 @@ func (gs *GState) PromoteToAdmin(c *fiber.Ctx) error { // Return a success message return c.SendStatus(fiber.StatusOK) } + +// Changes a users name in the database +func (gs *GState) ChangeUserName(c *fiber.Ctx) error { + + //check token and get username of current user + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + projectManagerUsername := claims["name"].(string) + log.Info(projectManagerUsername) + // Extract the necessary parameters from the request + data := new(types.NameChange) + if err := c.BodyParser(data); err != nil { + log.Info("error parsing username, project or role") + return c.Status(400).SendString(err.Error()) + } + + // dubble diping and checcking if current user is + + if ismanager, err := gs.Db.IsProjectManager(projectManagerUsername, c.Params(data.Name)); err != nil { + log.Warn("Error checking if projectmanager:", err) + return c.Status(500).SendString(err.Error()) + } else if !ismanager { + log.Warn("tried changing name when not projectmanager:", err) + return c.Status(401).SendString("you can not change name when not projectManager") + } + + // Change the user's name within the project in the database + if err := gs.Db.ChangeUserName(projectManagerUsername, data.Name); err != nil { + return c.Status(500).SendString(err.Error()) + } + + // Return a success message + return c.SendStatus(fiber.StatusOK) +} diff --git a/backend/internal/types/project.go b/backend/internal/types/project.go index c336bcb..6a7c91a 100644 --- a/backend/internal/types/project.go +++ b/backend/internal/types/project.go @@ -19,3 +19,8 @@ type RoleChange struct { Username string `json:"username"` Projectname string `json:"projectname"` } + +type NameChange struct { + ID int `json:"id" db:"id"` + Name string `json:"name" db:"name"` +} diff --git a/backend/main.go b/backend/main.go index d0ccdb1..8a70186 100644 --- a/backend/main.go +++ b/backend/main.go @@ -93,6 +93,7 @@ func main() { server.Get("/api/getWeeklyReport", gs.GetWeeklyReport) server.Post("/api/signReport", gs.SignReport) server.Put("/api/addUserToProject", gs.AddUserToProjectHandler) + server.Put("/api/changeUserName", gs.ChangeUserName) server.Post("/api/promoteToAdmin", gs.PromoteToAdmin) server.Get("/api/users/all", gs.ListAllUsers) server.Get("/api/getWeeklyReportsUser", gs.GetWeeklyReportsUserHandler) From a18ce465de6639b994b22d55d0c28ed5d44da252 Mon Sep 17 00:00:00 2001 From: borean Date: Wed, 20 Mar 2024 13:03:43 +0100 Subject: [PATCH 049/219] Added a handler for getting all users for a specific project, WIP and untested --- backend/internal/handlers/global_state.go | 1 + backend/internal/handlers/handlers_user_related.go | 14 ++++++++++++++ backend/main.go | 3 ++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 4a4b73f..a3e43ce 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -39,6 +39,7 @@ type GlobalState interface { ListAllUsersProject(c *fiber.Ctx) error // To get a list of all users for a specific project ProjectRoleChange(c *fiber.Ctx) error // To change a users role in a project ChangeUserName(c *fiber.Ctx) error // WIP + GetAllUsersProject(c *fiber.Ctx) error // WIP } // "Constructor" diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index 862416c..ea511ac 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -187,6 +187,20 @@ func (gs *GState) ListAllUsers(c *fiber.Ctx) error { return c.JSON(users) } +func (gs *GState) GetAllUsersProject(c *fiber.Ctx) error { + // Get all users from a project + projectName := c.Params("projectName") + users, err := gs.Db.GetAllUsersProject(projectName) + if err != nil { + log.Info("Error getting users from project:", err) // Debug print + return c.Status(500).SendString(err.Error()) + } + + log.Info("Returning all users") + // Return the list of users as JSON + return c.JSON(users) +} + // @Summary PromoteToAdmin // @Description promote chosen user to admin // @Tags User diff --git a/backend/main.go b/backend/main.go index 8a70186..888cb51 100644 --- a/backend/main.go +++ b/backend/main.go @@ -88,8 +88,9 @@ func main() { server.Post("/api/loginrenew", gs.LoginRenew) server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches server.Delete("api/project", gs.DeleteProject) // WIP - server.Post("/api/project", gs.CreateProject) + server.Post("/api/project", gs.CreateProject) // WIP server.Get("/api/project/:projectId", gs.GetProject) + server.Get("/api/project/getAllUsers", gs.GetAllUsersProject) server.Get("/api/getWeeklyReport", gs.GetWeeklyReport) server.Post("/api/signReport", gs.SignReport) server.Put("/api/addUserToProject", gs.AddUserToProjectHandler) From d3bb6f49e5d597b7c73816de2cbffd54dd766978 Mon Sep 17 00:00:00 2001 From: Mattias Date: Wed, 20 Mar 2024 13:51:49 +0100 Subject: [PATCH 050/219] New comp changeRoles --- frontend/src/Components/ChangeRoles.tsx | 83 +++++++++++++++++++ .../ProjectManagerPages/PMChangeRole.tsx | 15 ++-- 2 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 frontend/src/Components/ChangeRoles.tsx diff --git a/frontend/src/Components/ChangeRoles.tsx b/frontend/src/Components/ChangeRoles.tsx new file mode 100644 index 0000000..e11d623 --- /dev/null +++ b/frontend/src/Components/ChangeRoles.tsx @@ -0,0 +1,83 @@ +import { useState } from "react"; +import { useParams } from "react-router-dom"; +import Button from "./Button"; + +export default function ChangeRoles(): JSX.Element { + const [selectedRole, setSelectedRole] = useState(""); + const { username } = useParams(); + + const handleRoleChange = ( + event: React.ChangeEvent, + ): void => { + setSelectedRole(event.target.value); + }; + + // const handleSubmit = async (event: React.FormEvent) => { + // event.preventDefault(); + + // const response = await api.changeRole(username, selectedRole, token); + // if (response.success) { + // console.log("Role changed successfully"); + // } else { + // console.error("Failed to change role:", response.message); + // } + // }; + + return ( + <> +

    + Change roll for: {username} +

    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    ; -} From aa5c4017bb3087d475e4fd85a1f94c7a9d5463c6 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 13:54:13 +0100 Subject: [PATCH 057/219] Docs/Comments to DisplayUserProjects component --- frontend/src/Components/DisplayUserProjects.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/Components/DisplayUserProjects.tsx b/frontend/src/Components/DisplayUserProjects.tsx index 266f1ce..f4fd782 100644 --- a/frontend/src/Components/DisplayUserProjects.tsx +++ b/frontend/src/Components/DisplayUserProjects.tsx @@ -3,6 +3,10 @@ import { Project } from "../Types/goTypes"; import { Link } from "react-router-dom"; import { api } from "../API/API"; +/** + * Renders a component that displays the projects a user is a part of and links to the projects start-page. + * @returns The JSX element representing the component. + */ function DisplayUserProject(): JSX.Element { const [projects, setProjects] = useState([]); @@ -16,6 +20,7 @@ function DisplayUserProject(): JSX.Element { console.error(response.message); } }; + // Call getProjects when the component mounts useEffect(() => { void getProjects(); From dbeeb609b322d0572abff578e00ac7c0ad44d020 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 13:54:32 +0100 Subject: [PATCH 058/219] Docs/Comments to GetWeeklyReport component --- frontend/src/Components/EditWeeklyReport.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/Components/EditWeeklyReport.tsx b/frontend/src/Components/EditWeeklyReport.tsx index 3017204..e824b19 100644 --- a/frontend/src/Components/EditWeeklyReport.tsx +++ b/frontend/src/Components/EditWeeklyReport.tsx @@ -4,6 +4,10 @@ import { api } from "../API/API"; import { useNavigate, useParams } from "react-router-dom"; import Button from "./Button"; +/** + * Renders the component for editing a weekly report. + * @returns JSX.Element + */ export default function GetWeeklyReport(): JSX.Element { const [week, setWeek] = useState(0); const [developmentTime, setDevelopmentTime] = useState(0); From cf6c8cd34c779b86ffa07a96427aa66a1be7d1a8 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 13:54:59 +0100 Subject: [PATCH 059/219] Docs/Comments to Footer component --- frontend/src/Components/Footer.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frontend/src/Components/Footer.tsx b/frontend/src/Components/Footer.tsx index a3b7469..192926f 100644 --- a/frontend/src/Components/Footer.tsx +++ b/frontend/src/Components/Footer.tsx @@ -1,5 +1,13 @@ +//info: Footer component to display the footer of a page where the buttons are placed import React from "react"; +/** + * Footer component. + * + * @param {Object} props - The component props. + * @param {React.ReactNode} props.children - The children elements to render inside the footer (buttons). + * @returns {JSX.Element} The rendered footer component. + */ function Footer({ children }: { children: React.ReactNode }): JSX.Element { return (