From 6efc96177460b7da6874b24480f5ed57c8aca645 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Tue, 2 Apr 2024 13:08:55 +0200 Subject: [PATCH 01/10] Added getProjectTimes --- backend/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/main.go b/backend/main.go index e4cffae..6e65386 100644 --- a/backend/main.go +++ b/backend/main.go @@ -112,6 +112,7 @@ func main() { // All project related routes // projectGroup := api.Group("/project") // Not currently in use + api.Get("/getProjectTimes/:projectName", projects.GetProjectTimesHandler) api.Get("/getUserProjects/:username", projects.GetUserProjects) api.Get("/project/:projectId", projects.GetProject) api.Get("/checkIfProjectManager/:projectName", projects.IsProjectManagerHandler) From 644d0ee12c3dd4bcd660eafe8810858bd46c7961 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Tue, 2 Apr 2024 13:09:28 +0200 Subject: [PATCH 02/10] getProjectTimes API --- frontend/src/API/API.ts | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 748c64b..c872a9d 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -1,5 +1,6 @@ import { NewProjMember } from "../Components/AddMember"; import { ProjectRoleChange } from "../Components/ChangeRole"; +import { projectTimes } from "../Components/GetProjectTimes"; import { ProjectMember } from "../Components/GetUsersInProject"; import { NewWeeklyReport, @@ -126,6 +127,16 @@ interface API { */ getProject(id: number): Promise>; + /** Gets a projects reported time + * @param {string} projectName The name of the project. + * @param {string} token The usertoken. + * @returns {Promise>} A promise resolving to an API response containing the project times. + */ + getProjectTimes( + projectName: string, + token: string, + ): Promise>; + /** Gets a list of all users. * @param {string} token The authentication token of the requesting user. * @returns {Promise>} A promise resolving to an API response containing the list of users. @@ -385,6 +396,37 @@ export const api: API = { } }, + async getProjectTimes( + projectName: string, + token: string, + ): Promise> { + try { + const response = await fetch(`/api/getProjectTimes/${projectName}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, + }); + + if (!response.ok) { + return Promise.resolve({ + success: false, + message: + "Fetch error: " + response.status + ", failed to get project times", + }); + } else { + const data = (await response.json()) as projectTimes; + return Promise.resolve({ success: true, data }); + } + } catch (e) { + return Promise.resolve({ + success: false, + message: "API error! Could not get times.", + }); + } + }, + async submitWeeklyReport( weeklyReport: NewWeeklyReport, token: string, From ca88daf493b2afc3add192b40e1799b520569dfb Mon Sep 17 00:00:00 2001 From: Peter KW Date: Tue, 2 Apr 2024 13:10:04 +0200 Subject: [PATCH 03/10] GetProjectTimes component --- frontend/src/Components/GetProjectTimes.tsx | 59 +++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 frontend/src/Components/GetProjectTimes.tsx diff --git a/frontend/src/Components/GetProjectTimes.tsx b/frontend/src/Components/GetProjectTimes.tsx new file mode 100644 index 0000000..38288ec --- /dev/null +++ b/frontend/src/Components/GetProjectTimes.tsx @@ -0,0 +1,59 @@ +import { Dispatch, SetStateAction, useEffect } from "react"; +import { api } from "../API/API"; + +/** + * Interface for reported time per category + total time reported + */ +export interface projectTimes { + admin: number; + development: number; + meeting: number; + own_work: number; + study: number; + testing: number; + totalTime?: number; +} + +/** + * Gets all reported times for this project + * @param {Dispatch} props.setTimesProp - A setStateAction for the map you want to put times in + * @param {string} props.projectName - Username + * @returns {void} Nothing + * @example + * const projectName = "Example"; + * const [times, setTimes] = useState(); + * GetProjectTimes({ setTimesProp: setTimes, projectName: projectName }); + */ +function GetProjectTimes(props: { + setTimesProp: Dispatch>; + projectName: string; +}): void { + const setTimes: Dispatch> = + props.setTimesProp; + useEffect(() => { + const fetchUsers = async (): Promise => { + try { + const token = localStorage.getItem("accessToken") ?? ""; + const response = await api.getProjectTimes(props.projectName, token); + if (response.success && response.data) { + // Calculates total time reported + response.data.totalTime = response.data.admin; + response.data.totalTime += response.data.development; + response.data.totalTime += response.data.meeting; + response.data.totalTime += response.data.own_work; + response.data.totalTime += response.data.study; + response.data.totalTime += response.data.testing; + setTimes(response.data); + } else { + console.error("Failed to fetch project times:", response.message); + } + } catch (error) { + console.error("Error fetching times:", error); + } + }; + + void fetchUsers(); + }, [props.projectName, setTimes]); +} + +export default GetProjectTimes; From cb68a6323bc7bf6e061c93d9ea3123d63cf39691 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Tue, 2 Apr 2024 13:11:33 +0200 Subject: [PATCH 04/10] Now shows project statistics --- frontend/src/Components/ProjectInfoModal.tsx | 79 +++++++++++++------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/frontend/src/Components/ProjectInfoModal.tsx b/frontend/src/Components/ProjectInfoModal.tsx index 27d4e6e..875e5d6 100644 --- a/frontend/src/Components/ProjectInfoModal.tsx +++ b/frontend/src/Components/ProjectInfoModal.tsx @@ -1,30 +1,48 @@ -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; import Button from "./Button"; import GetUsersInProject, { ProjectMember } from "./GetUsersInProject"; import { Link } from "react-router-dom"; +import GetProjectTimes, { projectTimes } from "./GetProjectTimes"; function ProjectInfoModal(props: { - isVisible: boolean; projectname: string; onClose: () => void; onClick: (username: string) => void; }): JSX.Element { const [users, setUsers] = useState([]); + const [times, setTimes] = useState(); + const totalTime = useRef(0); GetUsersInProject({ projectName: props.projectname, setUsersProp: setUsers }); - if (!props.isVisible) return <>; + + GetProjectTimes({ setTimesProp: setTimes, projectName: props.projectname }); + + useEffect(() => { + if (times?.totalTime !== undefined) { + totalTime.current = times.totalTime; + } + }, [times]); return (
-
-
+
+

- {localStorage.getItem("projectName") ?? ""} + {props.projectname}

-

Project members:

-
+
+

Statistics:

+
+
+

Number of members: {users.length}

+

Total time reported: {totalTime.current}

+
+
+

Project members:

+
+
    {users.map((user) => ( @@ -44,31 +62,36 @@ function ProjectInfoModal(props: { ))}
-
-
-
From 948dcce1caf4c42f08f3168c54b3ac21cdb6cb14 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Tue, 2 Apr 2024 13:14:08 +0200 Subject: [PATCH 05/10] Some fixes to design and comments --- frontend/src/Components/AddProject.tsx | 38 +++---- frontend/src/Components/AddUserToProject.tsx | 12 +-- frontend/src/Components/ChangeRoleView.tsx | 14 +-- frontend/src/Components/GetProjects.tsx | 6 +- frontend/src/Components/GetUsersInProject.tsx | 13 +-- frontend/src/Components/InputField.tsx | 2 +- frontend/src/Components/MemberInfoModal.tsx | 85 +++++++-------- frontend/src/Components/ProjectListAdmin.tsx | 34 +++--- frontend/src/Components/Register.tsx | 34 +++--- frontend/src/Components/UserInfoModal.tsx | 100 +++++++++--------- .../src/Components/UserProjectListAdmin.tsx | 6 +- .../AdminPages/AdminProjectAddMember.tsx | 6 +- 12 files changed, 173 insertions(+), 177 deletions(-) diff --git a/frontend/src/Components/AddProject.tsx b/frontend/src/Components/AddProject.tsx index c157b04..e2ad8b9 100644 --- a/frontend/src/Components/AddProject.tsx +++ b/frontend/src/Components/AddProject.tsx @@ -61,24 +61,26 @@ function AddProject(): JSX.Element {

Create a new project

- { - e.preventDefault(); - setName(e.target.value); - }} - /> - { - e.preventDefault(); - setDescription(e.target.value); - }} - /> +
+ { + e.preventDefault(); + setName(e.target.value); + }} + /> + { + e.preventDefault(); + setDescription(e.target.value); + }} + /> +
-
-
); diff --git a/frontend/src/Components/ProjectListAdmin.tsx b/frontend/src/Components/ProjectListAdmin.tsx index 7305ea4..294a131 100644 --- a/frontend/src/Components/ProjectListAdmin.tsx +++ b/frontend/src/Components/ProjectListAdmin.tsx @@ -18,7 +18,7 @@ export function ProjectListAdmin(props: { projects: NewProject[]; }): JSX.Element { const [projectModalVisible, setProjectModalVisible] = useState(false); - const [projectname, setProjectname] = useState(""); + const [projectName, setProjectName] = useState(""); const [userModalVisible, setUserModalVisible] = useState(false); const [username, setUsername] = useState(""); @@ -28,34 +28,36 @@ export function ProjectListAdmin(props: { }; const handleClickProject = (projectname: string): void => { - setProjectname(projectname); - localStorage.setItem("projectName", projectname); + setProjectName(projectname); setProjectModalVisible(true); }; const handleCloseProject = (): void => { - setProjectname(""); + setProjectName(""); setProjectModalVisible(false); }; const handleCloseUser = (): void => { - setProjectname(""); + setUsername(""); setUserModalVisible(false); }; return ( <> - - + {projectModalVisible && ( + + )} + {userModalVisible && ( + + )}
    {props.projects.map((project) => ( diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx index 8a22806..68e0979 100644 --- a/frontend/src/Components/Register.tsx +++ b/frontend/src/Components/Register.tsx @@ -49,22 +49,24 @@ export default function Register(): JSX.Element {

    Register New User

    - { - setUsername(e.target.value); - }} - /> - { - setPassword(e.target.value); - }} - /> +
    + { + setUsername(e.target.value); + }} + /> + { + setPassword(e.target.value); + }} + /> +
    + )} +

    Member of these projects:

    + +
    +
    - )} -
    -

    - Member of these projects: -

    -
    - -
    -
    -
    -
diff --git a/frontend/src/Components/UserProjectListAdmin.tsx b/frontend/src/Components/UserProjectListAdmin.tsx index 50ae054..bc85c5b 100644 --- a/frontend/src/Components/UserProjectListAdmin.tsx +++ b/frontend/src/Components/UserProjectListAdmin.tsx @@ -8,10 +8,10 @@ function UserProjectListAdmin(props: { username: string }): JSX.Element { GetProjects({ setProjectsProp: setProjects, username: props.username }); return ( -
-
    +
    +
      {projects.map((project) => ( -
    • +
    • {project.name}
    • ))} diff --git a/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx b/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx index 893bdad..fa592c9 100644 --- a/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx +++ b/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx @@ -1,11 +1,11 @@ +import { useLocation } from "react-router-dom"; import AddUserToProject from "../../Components/AddUserToProject"; import BasicWindow from "../../Components/BasicWindow"; function AdminProjectAddMember(): JSX.Element { - const content = ; - + const projectName = useLocation().search.slice(1); + const content = ; const buttons = <>; - return ; } export default AdminProjectAddMember; From 75876e43da8b28a989b3240849974ef09ea63199 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Tue, 2 Apr 2024 13:22:46 +0200 Subject: [PATCH 06/10] Minor design fix --- frontend/src/Components/LoginField.tsx | 34 ++++++++++++++------------ 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/frontend/src/Components/LoginField.tsx b/frontend/src/Components/LoginField.tsx index d7768c0..dda1714 100644 --- a/frontend/src/Components/LoginField.tsx +++ b/frontend/src/Components/LoginField.tsx @@ -25,22 +25,24 @@ function Login(props: { }): JSX.Element { return ( - { - props.setUsername(e.target.value); - }} - value={props.username} - /> - { - props.setPassword(e.target.value); - }} - value={props.password} - /> +
      + { + props.setUsername(e.target.value); + }} + value={props.username} + /> + { + props.setPassword(e.target.value); + }} + value={props.password} + /> +