From 92cf36d1786a783445884aaee80211ab8cc24526 Mon Sep 17 00:00:00 2001 From: pavel Hamawand Date: Fri, 15 Mar 2024 01:42:43 +0100 Subject: [PATCH 001/641] increased responsiveness - outer div --- frontend/src/Pages/LoginPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Pages/LoginPage.tsx b/frontend/src/Pages/LoginPage.tsx index 7dabfbe..6ccf8fa 100644 --- a/frontend/src/Pages/LoginPage.tsx +++ b/frontend/src/Pages/LoginPage.tsx @@ -30,7 +30,7 @@ function LoginPage(): JSX.Element { <>
Date: Fri, 15 Mar 2024 02:03:22 +0100 Subject: [PATCH 002/641] links to all projects --- frontend/src/Pages/YourProjectsPage.tsx | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index d6f5743..7ec147d 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -11,15 +11,21 @@ function YourProjectsPage(): JSX.Element { ProjectNameExample -

- ProjectNameExample2 -

-

- ProjectNameExample3 -

-

- ProjectNameExample4 -

+ +

+ ProjectNameExample2 +

+ + +

+ ProjectNameExample3 +

+ + +

+ ProjectNameExample4 +

+
); From 2ad7146588bc6a0ab3b45c0416fd45b54a810538 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Fri, 15 Mar 2024 02:28:28 +0100 Subject: [PATCH 003/641] Added some functionality to login page. Checks username + password and compares with "fake" users to determine which page to get --- frontend/src/Pages/LoginPage.tsx | 68 +++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/frontend/src/Pages/LoginPage.tsx b/frontend/src/Pages/LoginPage.tsx index 6ccf8fa..9b4290d 100644 --- a/frontend/src/Pages/LoginPage.tsx +++ b/frontend/src/Pages/LoginPage.tsx @@ -1,8 +1,9 @@ import Button from "../Components/Button"; import Logo from "/src/assets/Logo.svg"; import "./LoginPage.css"; -import { useEffect } from "react"; -import { Link } from "react-router-dom"; +import { FormEvent, useEffect, useState } from "react"; +import { NewUser } from "../Types/Users"; +import { useNavigate } from "react-router-dom"; const PreloadBackgroundAnimation = (): JSX.Element => { useEffect(() => { @@ -26,6 +27,39 @@ const PreloadBackgroundAnimation = (): JSX.Element => { }; function LoginPage(): JSX.Element { + //Example users for testing without backend, remove when using backend + const admin: NewUser = { + name: "admin", + password: "123", + }; + const pmanager: NewUser = { + name: "pmanager", + password: "123", + }; + const user: NewUser = { + name: "user", + password: "123", + }; + + const navigate = useNavigate(); + + /* On submit (enter or button click) check if username and password match any user + and if so, redirect to correct page */ + function handleSubmit(event: FormEvent): void { + event.preventDefault(); + //TODO: Compare with db instead when finished + if (username === admin.name && password === admin.password) { + navigate("/admin-menu"); + } else if (username === pmanager.name && password === pmanager.password) { + navigate("/PM-project-page"); + } else if (username === user.name && password === user.password) { + navigate("/your-projects"); + } + } + + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + return ( <> @@ -51,24 +85,30 @@ function LoginPage(): JSX.Element { {" "} Please log in to continue{" "} - - - +
+ { + setUsername(e.target.value); + }} + /> + { + setPassword(e.target.value); + }} + />
+ ); +} + +export default BackButton; From 6789cc97cea5fac14116639143fb8ff0a65228f2 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Fri, 15 Mar 2024 14:57:49 +0100 Subject: [PATCH 009/641] Added back button to page --- frontend/src/Pages/AdminPages/AdminManageUsers.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/frontend/src/Pages/AdminPages/AdminManageUsers.tsx b/frontend/src/Pages/AdminPages/AdminManageUsers.tsx index b25b120..610e8d2 100644 --- a/frontend/src/Pages/AdminPages/AdminManageUsers.tsx +++ b/frontend/src/Pages/AdminPages/AdminManageUsers.tsx @@ -1,5 +1,6 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; +import BackButton from "../../Components/BackButton"; function AdminManageUsers(): JSX.Element { const content = <>; @@ -12,12 +13,7 @@ function AdminManageUsers(): JSX.Element { return; }} /> - {isOpen && ( From 164ff781b3e4799f977872bbd1455644d83cd7c4 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Mon, 18 Mar 2024 10:43:52 +0100 Subject: [PATCH 136/641] Add useEffect hook to handle authority navigation and log response in YourProjectsPage --- frontend/src/Pages/App.tsx | 19 +++++++++++-------- frontend/src/Pages/YourProjectsPage.tsx | 1 + 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/frontend/src/Pages/App.tsx b/frontend/src/Pages/App.tsx index 7ded3b6..4263815 100644 --- a/frontend/src/Pages/App.tsx +++ b/frontend/src/Pages/App.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; import LoginPage from "./LoginPage"; import { useNavigate } from "react-router-dom"; @@ -6,13 +6,16 @@ import { useNavigate } from "react-router-dom"; function App(): JSX.Element { const navigate = useNavigate(); const [authority, setAuthority] = useState(0); - if (authority === 1) { - navigate("/admin"); - } else if (authority === 2) { - navigate("/pm"); - } else if (authority === 3) { - navigate("/user"); - } + + useEffect(() => { + if (authority === 1) { + navigate("/admin"); + } else if (authority === 2) { + navigate("/pm"); + } else if (authority === 3) { + navigate("/user"); + } + }, [authority, navigate]); return ; } diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index 4e43bb6..30912a6 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -11,6 +11,7 @@ function UserProjectPage(): JSX.Element { const username = localStorage.getItem("username") ?? ""; // replace with actual username const token = localStorage.getItem("accessToken") ?? ""; // replace with actual token const response = await api.getUserProjects(username, token); + console.log(response); if (response.success) { setProjects(response.data ?? []); } else { From 3f8d56963ba03b25c25cd30fd5a8fdd003680aff Mon Sep 17 00:00:00 2001 From: Davenludd Date: Mon, 18 Mar 2024 12:49:58 +0100 Subject: [PATCH 137/641] Add ProjectNameContext to NewWeeklyReport and handle project selection in YourProjectsPage --- frontend/src/Components/NewWeeklyReport.tsx | 3 ++- frontend/src/Pages/YourProjectsPage.tsx | 25 +++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx index f5d92da..4f919aa 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -3,6 +3,7 @@ import { NewWeeklyReport } from "../Types/goTypes"; import { api } from "../API/API"; import { useNavigate } from "react-router-dom"; import Button from "./Button"; +import { ProjectNameContext } from "../Pages/YourProjectsPage"; export default function NewWeeklyReport(): JSX.Element { const [week, setWeek] = useState(0); @@ -13,7 +14,7 @@ export default function NewWeeklyReport(): JSX.Element { const [studyTime, setStudyTime] = useState(0); const [testingTime, setTestingTime] = useState(0); - // const projectName = useContext(projectNameContext); + const projectName = useContext(ProjectNameContext); const token = localStorage.getItem("accessToken") ?? ""; const handleNewWeeklyReport = async (): Promise => { diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index 30912a6..aabc606 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -1,11 +1,14 @@ -import React, { useState } from "react"; +import React, { useState, createContext, useEffect } from "react"; import { Project } from "../Types/goTypes"; import { api } from "../API/API"; import { Link } from "react-router-dom"; import BasicWindow from "../Components/BasicWindow"; +export const ProjectNameContext = createContext(""); + function UserProjectPage(): JSX.Element { const [projects, setProjects] = useState([]); + const [selectedProject, setSelectedProject] = useState(""); const getProjects = async (): Promise => { const username = localStorage.getItem("username") ?? ""; // replace with actual username @@ -18,20 +21,34 @@ function UserProjectPage(): JSX.Element { console.error(response.message); } }; + // Call getProjects when the component mounts + useEffect(() => { + getProjects(); + }, []); + + const handleProjectClick = (projectName: string): void => { + setSelectedProject(projectName); + }; const content = ( - <> +

Your Projects

{projects.map((project, index) => ( - + { + handleProjectClick(project.name); + }} + key={index} + >

{project.name}

))}
- +
); const buttons = <>; From 76fefd2b2482a4aee882dff68e7a73fb26aa9980 Mon Sep 17 00:00:00 2001 From: dDogge Date: Mon, 18 Mar 2024 13:32:55 +0100 Subject: [PATCH 138/641] Added function to check if someone is admin --- backend/internal/database/db.go | 21 ++++++++++++++ backend/internal/database/db_test.go | 43 ++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 5cbb13f..82a3551 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -32,6 +32,7 @@ type Database interface { GetUserRole(username string, projectname string) (string, error) GetWeeklyReport(username string, projectName string, week int) (types.WeeklyReport, error) SignWeeklyReport(reportId int, projectManagerId int) error + IsSiteAdmin(username string) (bool, error) } // This struct is a wrapper type that holds the database connection @@ -313,6 +314,26 @@ func (d *Db) SignWeeklyReport(reportId int, projectManagerId int) error { return err } +// IsSiteAdmin checks if a given username is a site admin +func (d *Db) IsSiteAdmin(username string) (bool, error) { + // Define the SQL query to check if the user is a site admin + query := ` + SELECT COUNT(*) FROM site_admin + JOIN users ON site_admin.admin_id = users.id + WHERE users.username = ? + ` + + // Execute the query + var count int + err := d.Get(&count, query, username) + if err != nil { + return false, err + } + + // If count is greater than 0, the user is a site admin + return count > 0, nil +} + // Reads a directory of migration files and applies them to the database. // This will eventually be used on an embedded directory func (d *Db) Migrate() error { diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 09de45b..2378b3d 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -536,3 +536,46 @@ func TestSignWeeklyReportByAnotherProjectManager(t *testing.T) { t.Error("Expected SignWeeklyReport to fail with a project manager who is not in the project, but it didn't") } } + +func TestIsSiteAdmin(t *testing.T) { + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + + // Add a site admin + err = db.AddUser("admin", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Promote the user to site admin + err = db.PromoteToAdmin("admin") + if err != nil { + t.Error("PromoteToAdmin failed:", err) + } + + // Check if the user is a site admin + isAdmin, err := db.IsSiteAdmin("admin") + if err != nil { + t.Error("IsSiteAdmin failed:", err) + } + if !isAdmin { + t.Error("IsSiteAdmin failed: expected true, got false") + } + + // Add a regular user + err = db.AddUser("regularuser", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Check if the regular user is not a site admin + isRegularUserAdmin, err := db.IsSiteAdmin("regularuser") + if err != nil { + t.Error("IsSiteAdmin failed:", err) + } + if isRegularUserAdmin { + t.Error("IsSiteAdmin failed: expected false, got true") + } +} From 409d973d8acc9402d5f539155eb6d177bccbfa1c Mon Sep 17 00:00:00 2001 From: pavel Hamawand Date: Mon, 18 Mar 2024 14:35:56 +0100 Subject: [PATCH 139/641] minor fix --- frontend/src/Pages/YourProjectsPage.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index 2b3fd18..6de09ef 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -1,6 +1,6 @@ import BasicWindow from "../Components/BasicWindow"; import { ProjectListUser } from "../Components/ProjectListUser"; -import { Project } from "../Types/Project"; +import { Project } from "../Types/goTypes"; function YourProjectsPage(): JSX.Element { //TODO: Change so that it reads projects from database @@ -10,7 +10,6 @@ function YourProjectsPage(): JSX.Element { id: i, name: "Example Project " + i, description: "good", - created: "now", owner: "me", }); } From 69d406720980d55b5bface33a47d89b24d413a40 Mon Sep 17 00:00:00 2001 From: Mattias Date: Mon, 18 Mar 2024 14:40:20 +0100 Subject: [PATCH 140/641] Added new component for viewing weeklyreport --- frontend/src/Components/ViewWeeklyReport.tsx | 243 +++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 frontend/src/Components/ViewWeeklyReport.tsx diff --git a/frontend/src/Components/ViewWeeklyReport.tsx b/frontend/src/Components/ViewWeeklyReport.tsx new file mode 100644 index 0000000..d6d4692 --- /dev/null +++ b/frontend/src/Components/ViewWeeklyReport.tsx @@ -0,0 +1,243 @@ +import { useState, useEffect } from "react"; +import { NewWeeklyReport } from "../Types/goTypes"; +import { api } from "../API/API"; +import { useNavigate } from "react-router-dom"; +import Button from "./Button"; + +export default function GetWeeklyReport(): JSX.Element { + const [projectName, setProjectName] = useState(""); + const [week, setWeek] = useState(0); + const [developmentTime, setDevelopmentTime] = useState(0); + const [meetingTime, setMeetingTime] = useState(0); + const [adminTime, setAdminTime] = useState(0); + const [ownWorkTime, setOwnWorkTime] = useState(0); + const [studyTime, setStudyTime] = useState(0); + const [testingTime, setTestingTime] = useState(0); + + const token = localStorage.getItem("accessToken") ?? ""; + + useEffect(() => { + const fetchWeeklyReport = async (): Promise => { + const response = await api.getWeeklyReport( + "username", + projectName, + "week", + token, + ); + + if (response.success) { + const report: NewWeeklyReport = response.data ?? { + projectName: "", + week: 0, + developmentTime: 0, + meetingTime: 0, + adminTime: 0, + ownWorkTime: 0, + studyTime: 0, + testingTime: 0, + }; + setProjectName(report.projectName); + setWeek(report.week); + setDevelopmentTime(report.developmentTime); + setMeetingTime(report.meetingTime); + setAdminTime(report.adminTime); + setOwnWorkTime(report.ownWorkTime); + setStudyTime(report.studyTime); + setTestingTime(report.testingTime); + } else { + console.error("Failed to fetch weekly report:", response.message); + } + }; + + fetchWeeklyReport(); + }, []); + + const handleNewWeeklyReport = async (): Promise => { + const newWeeklyReport: NewWeeklyReport = { + projectName, + week, + developmentTime, + meetingTime, + adminTime, + ownWorkTime, + studyTime, + testingTime, + }; + + await api.submitWeeklyReport(newWeeklyReport, token); + }; + + const navigate = useNavigate(); + + return ( + <> +
+
{ + if (week === 0) { + alert("Please enter a week number"); + e.preventDefault(); + return; + } + e.preventDefault(); + void handleNewWeeklyReport(); + navigate("/project"); + }} + > +
+ { + const weekNumber = parseInt(e.target.value.split("-W")[1]); + setWeek(weekNumber); + }} + onKeyDown={(event) => { + event.preventDefault(); + }} + onPaste={(event) => { + event.preventDefault(); + }} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Activity + + Total Time (min) +
Development + { + setDevelopmentTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Meeting + { + setMeetingTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Administration + { + setAdminTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Own Work + { + setOwnWorkTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Studies + { + setStudyTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Testing + { + setTestingTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
+
+
+
+ + ); +} From 0634f836893470d4297cc8f0373b2e5d128f52b9 Mon Sep 17 00:00:00 2001 From: Mattias Date: Mon, 18 Mar 2024 14:40:32 +0100 Subject: [PATCH 141/641] Now displaying the right component --- frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx index d82ea8c..9da19fe 100644 --- a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx +++ b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx @@ -1,13 +1,13 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; import BackButton from "../../Components/BackButton"; -import NewTimeReport from "../../Components/NewWeeklyReport"; +import ViewWeeklyReport from "../../Components/ViewWeeklyReport"; function UserEditTimeReportPage(): JSX.Element { const content = ( <>

Edit Time Report

- + ); From b82f73d192f4c7a3f8f09229e02c2ab165258562 Mon Sep 17 00:00:00 2001 From: Mattias Date: Mon, 18 Mar 2024 14:42:21 +0100 Subject: [PATCH 142/641] Removed button --- frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx index 9da19fe..0acef75 100644 --- a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx +++ b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx @@ -1,5 +1,4 @@ import BasicWindow from "../../Components/BasicWindow"; -import Button from "../../Components/Button"; import BackButton from "../../Components/BackButton"; import ViewWeeklyReport from "../../Components/ViewWeeklyReport"; @@ -13,13 +12,6 @@ function UserEditTimeReportPage(): JSX.Element { const buttons = ( <> - ; -} From aa5c4017bb3087d475e4fd85a1f94c7a9d5463c6 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 13:54:13 +0100 Subject: [PATCH 306/641] 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 307/641] 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 308/641] 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 (