diff --git a/backend/Makefile b/backend/Makefile index 15a550c..0ffc557 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -47,7 +47,7 @@ itest: make build ./bin/$(PROC_NAME) >/dev/null 2>&1 & sleep 1 # Adjust if needed - python ../testing/testing.py || pkill $(PROC_NAME) + python ../testing.py pkill $(PROC_NAME) # Get dependencies target diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 22e11e9..0bd67bc 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -44,7 +44,6 @@ type Database interface { GetProjectTimes(projectName string) (map[string]int, error) UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error RemoveProject(projectname string) error - GetUserName(id int) (string, error) } // This struct is a wrapper type that holds the database connection @@ -350,14 +349,9 @@ func (d *Db) SignWeeklyReport(reportId int, projectManagerId int) error { return err } - managerQuery := `SELECT project_id FROM user_roles - WHERE user_id = ? - AND project_id = (SELECT project_id FROM weekly_reports WHERE report_id = ?) - AND p_role = 'project_manager'` - // Retrieve the project ID associated with the project manager var managerProjectID int - err = d.Get(&managerProjectID, managerQuery, projectManagerId, reportId) + err = d.Get(&managerProjectID, "SELECT project_id FROM user_roles WHERE user_id = ? AND p_role = 'project_manager'", projectManagerId) if err != nil { return err } @@ -617,9 +611,3 @@ func (d *Db) RemoveProject(projectname string) error { _, err := d.Exec("DELETE FROM projects WHERE name = ?", projectname) return err } - -func (d *Db) GetUserName(id int) (string, error) { - var username string - err := d.Get(&username, "SELECT username FROM users WHERE id = ?", id) - return username, err -} diff --git a/backend/internal/handlers/projects/PromoteToPm.go b/backend/internal/handlers/projects/PromoteToPm.go index c587b65..ffe2215 100644 --- a/backend/internal/handlers/projects/PromoteToPm.go +++ b/backend/internal/handlers/projects/PromoteToPm.go @@ -44,10 +44,6 @@ func PromoteToPm(c *fiber.Ctx) error { // Add the user to the project with the specified role err = db.GetDb(c).ChangeUserRole(new_pm_name, project, "project_manager") - if err != nil { - log.Info("Error promoting user to project manager:", err) - return c.Status(500).SendString(err.Error()) - } // Return success message log.Info("User : ", new_pm_name, " promoted to project manager in project: ", project) diff --git a/backend/internal/handlers/reports/GetAllWeeklyReports.go b/backend/internal/handlers/reports/GetAllWeeklyReports.go index d9778b8..ee81c82 100644 --- a/backend/internal/handlers/reports/GetAllWeeklyReports.go +++ b/backend/internal/handlers/reports/GetAllWeeklyReports.go @@ -38,7 +38,7 @@ func GetAllWeeklyReports(c *fiber.Ctx) error { return c.Status(500).SendString(err.Error()) } - if !(pm || target_user == username) { + if pm == false && target_user != username { log.Info("Unauthorized access") return c.Status(403).SendString("Unauthorized access") } diff --git a/backend/internal/handlers/reports/GetWeeklyReport.go b/backend/internal/handlers/reports/GetWeeklyReport.go index 2b6827e..04bdc0d 100644 --- a/backend/internal/handlers/reports/GetWeeklyReport.go +++ b/backend/internal/handlers/reports/GetWeeklyReport.go @@ -47,7 +47,7 @@ func GetWeeklyReport(c *fiber.Ctx) error { return c.Status(500).SendString(err.Error()) } - if !(pm || target_user == username) { + if pm == false && target_user != username { log.Info("Unauthorized access") return c.Status(403).SendString("Unauthorized access") } diff --git a/backend/internal/handlers/users/GetUserName.go b/backend/internal/handlers/users/GetUserName.go deleted file mode 100644 index 82b6cc8..0000000 --- a/backend/internal/handlers/users/GetUserName.go +++ /dev/null @@ -1,32 +0,0 @@ -package users - -import ( - "strconv" - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" -) - -// Return the username of a user given their user id -func GetUserName(c *fiber.Ctx) error { - // Check the query params for userId - user_id_string := c.Query("userId") - if user_id_string == "" { - return c.Status(400).SendString("Missing user id") - } - - // Convert to int - user_id, err := strconv.Atoi(user_id_string) - if err != nil { - return c.Status(400).SendString("Invalid user id") - } - - // Get the username from the database - username, err := db.GetDb(c).GetUserName(user_id) - if err != nil { - return c.Status(500).SendString(err.Error()) - } - - // Send the nuclear launch codes to north korea - return c.JSON(fiber.Map{"username": username}) -} diff --git a/backend/main.go b/backend/main.go index 7b19dd9..42daa5c 100644 --- a/backend/main.go +++ b/backend/main.go @@ -103,7 +103,6 @@ func main() { // userGroup := api.Group("/user") // Not currently in use api.Get("/users/all", users.ListAllUsers) api.Get("/project/getAllUsers", users.GetAllUsersProject) - api.Get("/username", users.GetUserName) api.Post("/login", users.Login) api.Post("/register", users.Register) api.Post("/loginrenew", users.LoginRenew) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 86ad6dc..3c0f0e9 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -1,4 +1,4 @@ -import { AddMemberInfo } from "../Components/AddMember"; +import { NewProjMember } from "../Components/AddMember"; import { ProjectRoleChange } from "../Components/ChangeRole"; import { projectTimes } from "../Components/GetProjectTimes"; import { ProjectMember } from "../Components/GetUsersInProject"; @@ -197,7 +197,7 @@ interface API { ): Promise>; addUserToProject( - addMemberInfo: AddMemberInfo, + user: NewProjMember, token: string, ): Promise>; @@ -233,12 +233,6 @@ interface API { projectName: string, token: string, ): Promise>; - /** - * Get the username from the id - * @param {number} id The id of the user - * @param {string} token Your token - */ - getUsername(id: number, token: string): Promise>; } /** An instance of the API */ @@ -348,20 +342,18 @@ export const api: API = { }, async addUserToProject( - addMemberInfo: AddMemberInfo, + user: NewProjMember, token: string, ): Promise> { try { - const response = await fetch( - `/api/addUserToProject/${addMemberInfo.projectName}/?userName=${addMemberInfo.userName}`, - { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, + const response = await fetch("/api/addUserToProject", { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, }, - ); + body: JSON.stringify(user), + }); if (!response.ok) { return { success: false, message: "Failed to add member" }; @@ -876,25 +868,4 @@ export const api: API = { } return { success: true, message: "User promoted to project manager" }; }, - - async getUsername(id: number, token: string): Promise> { - try { - const response = await fetch(`/api/username?userId=${id}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return { success: false, message: "Failed to get username" }; - } else { - const data = (await response.json()) as string; - return { success: true, data }; - } - } catch (e) { - return { success: false, message: "Failed to get username" }; - } - }, }; diff --git a/frontend/src/Components/AddMember.tsx b/frontend/src/Components/AddMember.tsx index d8036b7..194afe8 100644 --- a/frontend/src/Components/AddMember.tsx +++ b/frontend/src/Components/AddMember.tsx @@ -1,35 +1,44 @@ -import { api } from "../API/API"; +import { APIResponse, api } from "../API/API"; -export interface AddMemberInfo { - userName: string; - projectName: string; +export interface NewProjMember { + username: string; + role: string; + projectname: string; } /** * Tries to add a member to a project - * @param {AddMemberInfo} props.membertoAdd - Contains user's name and project's name - * @returns {Promise} + * @param {Object} props - A NewProjMember + * @returns {boolean} True if added, false if not */ -async function AddMember(props: { memberToAdd: AddMemberInfo }): Promise { - if (props.memberToAdd.userName === "") { - alert("You must choose at least one user to add"); - return; +function AddMember(props: { memberToAdd: NewProjMember }): boolean { + let added = false; + if ( + props.memberToAdd.username === "" || + props.memberToAdd.role === "" || + props.memberToAdd.projectname === "" + ) { + alert("All fields must be filled before adding"); + return added; } - try { - const response = await api.addUserToProject( + api + .addUserToProject( props.memberToAdd, localStorage.getItem("accessToken") ?? "", - ); - if (response.success) { - alert(`[${props.memberToAdd.userName}] added`); - } else { - alert(`[${props.memberToAdd.userName}] not added`); - console.error(response.message); - } - } catch (error) { - alert(`[${props.memberToAdd.userName}] not added`); - console.error("An error occurred during member add:", error); - } + ) + .then((response: APIResponse) => { + if (response.success) { + alert("Member added"); + added = true; + } else { + alert("Member not added"); + console.error(response.message); + } + }) + .catch((error) => { + console.error("An error occurred during member add:", error); + }); + return added; } export default AddMember; diff --git a/frontend/src/Components/AddProject.tsx b/frontend/src/Components/AddProject.tsx index c8a1c66..e2ad8b9 100644 --- a/frontend/src/Components/AddProject.tsx +++ b/frontend/src/Components/AddProject.tsx @@ -1,10 +1,37 @@ import { useState } from "react"; -import { api } from "../API/API"; +import { APIResponse, api } from "../API/API"; import { NewProject } from "../Types/goTypes"; import InputField from "./InputField"; import Logo from "../assets/Logo.svg"; import Button from "./Button"; +/** + * Tries to add a project to the system + * @param {Object} props - Project name and description + * @returns {boolean} True if created, false if not + */ +function CreateProject(props: { name: string; description: string }): void { + const project: NewProject = { + name: props.name, + description: props.description, + }; + + api + .createProject(project, localStorage.getItem("accessToken") ?? "") + .then((response: APIResponse) => { + if (response.success) { + alert("Project added!"); + } else { + alert("Project NOT added!"); + console.error(response.message); + } + }) + .catch((error) => { + alert("Project NOT added!"); + console.error("An error occurred during creation:", error); + }); +} + /** * Provides UI for adding a project to the system. * @returns {JSX.Element} - Returns the component UI for adding a project @@ -13,33 +40,6 @@ function AddProject(): JSX.Element { const [name, setName] = useState(""); const [description, setDescription] = useState(""); - /** - * Tries to add a project to the system - */ - const handleCreateProject = async (): Promise => { - const project: NewProject = { - name: name.replace(/ /g, ""), - description: description.trim(), - }; - try { - const response = await api.createProject( - project, - localStorage.getItem("accessToken") ?? "", - ); - if (response.success) { - alert(`${project.name} added!`); - setDescription(""); - setName(""); - } else { - alert("Project not added, name could be taken"); - console.error(response.message); - } - } catch (error) { - alert("Project not added"); - console.error(error); - } - }; - return (
@@ -47,7 +47,10 @@ function AddProject(): JSX.Element { className="bg-white rounded px-8 pt-6 pb-8 mb-4 items-center justify-center flex flex-col w-fit h-fit" onSubmit={(e) => { e.preventDefault(); - void handleCreateProject(); + CreateProject({ + name: name, + description: description, + }); }} > ([]); + const [name, setName] = useState(""); const [users, setUsers] = useState([]); - const [usersProj, setUsersProj] = useState([]); - - // Gets all users and project members for filtering + const [role, setRole] = useState(""); GetAllUsers({ setUsersProp: setUsers }); - GetUsersInProject({ - setUsersProp: setUsersProj, - projectName: props.projectName, - }); - /* - * Filters the members from users so that users who are already - * members are not shown - */ - useEffect(() => { - setUsers((prevUsers) => { - const filteredUsers = prevUsers.filter( - (user) => - !usersProj.some((projectUser) => projectUser.Username === user), - ); - return filteredUsers; - }); - }, [usersProj]); - // Attempts to add all of the selected users to the project - const handleAddClick = async (): Promise => { - if (names.length === 0) - alert("You have to choose at least one user to add"); - for (const name of names) { - const newMember: AddMemberInfo = { - userName: name, - projectName: props.projectName, - }; - await AddMember({ memberToAdd: newMember }); - } - setNames([]); - location.reload(); - }; - - // Updates the names that have been selected - const handleUserClick = (user: string): void => { - setNames((prevNames): string[] => { - if (!prevNames.includes(user)) { - return [...prevNames, user]; - } - return prevNames.filter((name) => name !== user); - }); + const handleClick = (): boolean => { + const newMember: NewProjMember = { + username: name, + projectname: props.projectName, + role: role, + }; + return AddMember({ memberToAdd: newMember }); }; return ( -
-

- {props.projectName} -

-

- Choose users to add: +

+

+ User chosen: [{name}]

-
+

+ Role chosen: [{role}] +

+

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

+

Choose role:

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

Choose user:

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

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

-
+
diff --git a/frontend/src/Components/AllTimeReportsInProject.tsx b/frontend/src/Components/AllTimeReportsInProject.tsx index 0d5916b..4fa9ad8 100644 --- a/frontend/src/Components/AllTimeReportsInProject.tsx +++ b/frontend/src/Components/AllTimeReportsInProject.tsx @@ -17,7 +17,7 @@ function AllTimeReportsInProject(): JSX.Element { useEffect(() => { const getWeeklyReports = async (): Promise => { const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getAllWeeklyReportsForUser( + const response = await api.getWeeklyReportsForUser( projectName ?? "", token, ); diff --git a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx index 4218f0a..ef78642 100644 --- a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx +++ b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx @@ -17,10 +17,10 @@ function AllTimeReportsInProject(): JSX.Element { useEffect(() => { const getWeeklyReports = async (): Promise => { const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getAllWeeklyReportsForUser( + const response = await api.getWeeklyReportsForDifferentUser( projectName ?? "", - token, username ?? "", + token, ); console.log(response); if (response.success) { @@ -31,7 +31,7 @@ function AllTimeReportsInProject(): JSX.Element { }; void getWeeklyReports(); - }, [projectName, username]); + }, []); return ( <> @@ -60,7 +60,7 @@ function AllTimeReportsInProject(): JSX.Element {

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

diff --git a/frontend/src/Components/ChangeRoleView.tsx b/frontend/src/Components/ChangeRoleView.tsx index 782ad8d..30dce3c 100644 --- a/frontend/src/Components/ChangeRoleView.tsx +++ b/frontend/src/Components/ChangeRoleView.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import Button from "./Button"; import ChangeRole, { ProjectRoleChange } from "./ChangeRole"; -export default function ChangeRoleView(props: { +export default function ChangeRoles(props: { projectName: string; username: string; }): JSX.Element { diff --git a/frontend/src/Components/ChangeUsername.tsx b/frontend/src/Components/ChangeUsername.tsx index 2f73bb6..78d7da9 100644 --- a/frontend/src/Components/ChangeUsername.tsx +++ b/frontend/src/Components/ChangeUsername.tsx @@ -2,11 +2,8 @@ import { APIResponse, api } from "../API/API"; import { StrNameChange } from "../Types/goTypes"; function ChangeUsername(props: { nameChange: StrNameChange }): void { - if ( - props.nameChange.newName === "" || - props.nameChange.newName === props.nameChange.prevName - ) { - alert("You have to give a new name\n\nName not changed"); + if (props.nameChange.newName === "") { + alert("You have to select a new name"); return; } api @@ -16,7 +13,7 @@ function ChangeUsername(props: { nameChange: StrNameChange }): void { alert("Name changed successfully"); location.reload(); } else { - alert("Name not changed, name could be taken"); + alert("Name not changed"); console.error(response.message); } }) diff --git a/frontend/src/Components/DisplayUnsignedReports.tsx b/frontend/src/Components/DisplayUnsignedReports.tsx index 25a1da3..232cb31 100644 --- a/frontend/src/Components/DisplayUnsignedReports.tsx +++ b/frontend/src/Components/DisplayUnsignedReports.tsx @@ -3,14 +3,17 @@ import { Link, useParams } from "react-router-dom"; import { api } from "../API/API"; import { WeeklyReport } from "../Types/goTypes"; +/** + * 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 { projectName } = useParams(); const [unsignedReports, setUnsignedReports] = useState([]); - const [usernames, setUsernames] = useState([]); - const token = localStorage.getItem("accessToken") ?? ""; - + //const navigate = useNavigate(); useEffect(() => { const getUnsignedReports = async (): Promise => { + const token = localStorage.getItem("accessToken") ?? ""; const response = await api.getUnsignedReportsInProject( projectName ?? "", token, @@ -18,21 +21,13 @@ function DisplayUserProject(): JSX.Element { console.log(response); if (response.success) { setUnsignedReports(response.data ?? []); - const usernamesPromises = (response.data ?? []).map((report) => - api.getUsername(report.userId, token), - ); - const usernamesResponses = await Promise.all(usernamesPromises); - const usernames = usernamesResponses.map( - (res) => (res.data as { username?: string }).username ?? "", - ); - setUsernames(usernames); } else { console.error(response.message); } }; void getUnsignedReports(); - }, [projectName, token]); + }, [projectName]); // Include 'projectName' in the dependency array return ( <> @@ -44,8 +39,8 @@ function DisplayUserProject(): JSX.Element {

- Username: -

{usernames[index]}

{" "} + UserID: +

{unsignedReport.userId}

Week:

{unsignedReport.week}

Total Time: @@ -63,7 +58,7 @@ function DisplayUserProject(): JSX.Element {

View Report diff --git a/frontend/src/Components/Header.tsx b/frontend/src/Components/Header.tsx index 9be2f4b..eb4fa5a 100644 --- a/frontend/src/Components/Header.tsx +++ b/frontend/src/Components/Header.tsx @@ -1,6 +1,6 @@ //info: Header component to display the header of the page including the logo and user information where thr user can logout import { useState } from "react"; -import { Link, useNavigate } from "react-router-dom"; +import { Link } from "react-router-dom"; import backgroundImage from "../assets/1.jpg"; /** @@ -9,33 +9,23 @@ import backgroundImage from "../assets/1.jpg"; */ function Header(): JSX.Element { const [isOpen, setIsOpen] = useState(false); - const username = localStorage.getItem("username"); - const navigate = useNavigate(); const handleLogout = (): void => { localStorage.clear(); }; - const handleNavigation = (): void => { - if (username === "admin") { - navigate("/admin"); - } else { - navigate("/yourProjects"); - } - }; - return (
-
+ TTIME Logo -
+
@@ -42,16 +42,13 @@ function MemberInfoModal(props: {