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: diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index ad408a7..bc6e1e8 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -204,15 +204,22 @@ func (d *Db) AddProject(name string, description string, username string) error tx := d.MustBegin() _, err := tx.Exec(projectInsert, name, description, username) if err != nil { - tx.Rollback() + if err := tx.Rollback(); err != nil { + return err + } return err } _, err = tx.Exec(changeUserRole, "project_manager", username, name) if err != nil { - tx.Rollback() + if err := tx.Rollback(); err != nil { + return err + } return err } - tx.Commit() + if err := tx.Commit(); err != nil { + return err + } + return err } diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 6078513..8fd66d3 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -42,10 +42,7 @@ interface API { token: string, ): Promise>; /** Gets all the projects of a user*/ - getUserProjects( - username: string, - token: string, - ): Promise>; + getUserProjects(token: string): Promise>; /** Gets a project from id*/ getProject(id: number): Promise>; } @@ -150,10 +147,7 @@ export const api: API = { } }, - async getUserProjects( - username: string, - token: string, - ): Promise> { + async getUserProjects(token: string): Promise> { try { const response = await fetch("/api/getUserProjects", { method: "GET", @@ -161,7 +155,6 @@ export const api: API = { "Content-Type": "application/json", Authorization: "Bearer " + token, }, - body: JSON.stringify({ username }), }); if (!response.ok) { @@ -176,7 +169,7 @@ export const api: API = { } catch (e) { return Promise.resolve({ success: false, - message: "Failed to get user projects", + message: "API fucked", }); } }, diff --git a/frontend/src/Components/AdminUserList.tsx b/frontend/src/Components/AdminUserList.tsx new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/Components/DeleteUser.tsx b/frontend/src/Components/DeleteUser.tsx new file mode 100644 index 0000000..db49724 --- /dev/null +++ b/frontend/src/Components/DeleteUser.tsx @@ -0,0 +1,34 @@ +import { User } from "../Types/goTypes"; +import { api, APIResponse } from "../API/API"; + +/** + * Use to remove a user from the system + * @param props - The username of user to remove + * @returns {boolean} True if removed, false if not + * @example + * const exampleUsername = "user"; + * DeleteUser({ usernameToDelete: exampleUsername }); + */ + +function DeleteUser(props: { usernameToDelete: string }): boolean { + //console.log(props.usernameToDelete); FOR DEBUG + let removed = false; + api + .removeUser( + props.usernameToDelete, + localStorage.getItem("accessToken") ?? "", + ) + .then((response: APIResponse) => { + if (response.success) { + removed = true; + } else { + console.error(response.message); + } + }) + .catch((error) => { + console.error("An error occurred during creation:", error); + }); + return removed; +} + +export default DeleteUser; diff --git a/frontend/src/Components/Header.tsx b/frontend/src/Components/Header.tsx index 819c5de..5cdb421 100644 --- a/frontend/src/Components/Header.tsx +++ b/frontend/src/Components/Header.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; import { Link } from "react-router-dom"; +import backgroundImage from "../assets/1.jpg"; function Header(): JSX.Element { const [isOpen, setIsOpen] = useState(false); @@ -11,7 +12,7 @@ function Header(): JSX.Element { return (
(); + const [developmentTime, setDevelopmentTime] = useState(); + const [meetingTime, setMeetingTime] = useState(); + const [adminTime, setAdminTime] = useState(); + const [ownWorkTime, setOwnWorkTime] = useState(); + const [studyTime, setStudyTime] = useState(); + const [testingTime, setTestingTime] = useState(); - const projectName = useContext(ProjectNameContext); + const { projectName } = useParams(); const token = localStorage.getItem("accessToken") ?? ""; const handleNewWeeklyReport = async (): Promise => { const newWeeklyReport: NewWeeklyReport = { - projectName, - week, - developmentTime, - meetingTime, - adminTime, - ownWorkTime, - studyTime, - testingTime, + projectName: projectName ?? "", + week: week ?? 0, + developmentTime: developmentTime ?? 0, + meetingTime: meetingTime ?? 0, + adminTime: adminTime ?? 0, + ownWorkTime: ownWorkTime ?? 0, + studyTime: studyTime ?? 0, + testingTime: testingTime ?? 0, }; await api.submitWeeklyReport(newWeeklyReport, token); @@ -59,7 +58,9 @@ export default function NewWeeklyReport(): JSX.Element { setWeek(weekNumber); }} onKeyDown={(event) => { - event.preventDefault(); + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); }} onPaste={(event) => { event.preventDefault(); diff --git a/frontend/src/Components/UserInfoModal.tsx b/frontend/src/Components/UserInfoModal.tsx new file mode 100644 index 0000000..a22ef01 --- /dev/null +++ b/frontend/src/Components/UserInfoModal.tsx @@ -0,0 +1,54 @@ +import { Link } from "react-router-dom"; +import Button from "./Button"; +import DeleteUser from "./DeleteUser"; +import UserProjectListAdmin from "./UserProjectListAdmin"; + +function UserInfoModal(props: { + isVisible: boolean; + username: string; + onClose: () => void; +}): JSX.Element { + if (!props.isVisible) return <>; + + return ( +
+
+

{props.username}

+ +

+ (Change Username) +

+ +
+

+ Member of these projects: +

+
+ +
+
+
+
+
+
+ ); +} + +export default UserInfoModal; diff --git a/frontend/src/Components/UserListAdmin.tsx b/frontend/src/Components/UserListAdmin.tsx index b86076a..3d2bcae 100644 --- a/frontend/src/Components/UserListAdmin.tsx +++ b/frontend/src/Components/UserListAdmin.tsx @@ -1,5 +1,6 @@ -import { Link } from "react-router-dom"; +import { useState } from "react"; import { PublicUser } from "../Types/goTypes"; +import UserInfoModal from "./UserInfoModal"; /** * The props for the UserProps component @@ -9,27 +10,52 @@ interface UserProps { } /** - * A list of users for admin manage users page, that links admin to the right user page - * thanks to the state property - * @param props - The users to display + * A list of users for admin manage users page, that sets an onClick + * function for eact user
  • element, which displays a modul with + * user info. + * @param props - An array of users users to display * @returns {JSX.Element} The user list * @example - * const users = [{ id: 1, userName: "Random name" }]; + * const users = [{ id: 1, userName: "ExampleName" }]; * return ; */ export function UserListAdmin(props: UserProps): JSX.Element { + const [modalVisible, setModalVisible] = useState(false); + const [username, setUsername] = useState(""); + + const handleClick = (username: string): void => { + setUsername(username); + setModalVisible(true); + }; + + const handleClose = (): void => { + setUsername(""); + setModalVisible(false); + }; + return ( -
    -
      - {props.users.map((user) => ( - -
    • + <> + +
      +
        + {props.users.map((user) => ( +
      • { + handleClick(user.username); + }} + > {user.username}
      • - - ))} -
      -
      + ))} +
    +
    + ); } diff --git a/frontend/src/Components/UserProjectListAdmin.tsx b/frontend/src/Components/UserProjectListAdmin.tsx index 69258a1..1b7b923 100644 --- a/frontend/src/Components/UserProjectListAdmin.tsx +++ b/frontend/src/Components/UserProjectListAdmin.tsx @@ -1,17 +1,17 @@ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { api } from "../API/API"; import { Project } from "../Types/goTypes"; -const UserProjectListAdmin: React.FC = () => { +function UserProjectListAdmin(): JSX.Element { const [projects, setProjects] = useState([]); useEffect(() => { const fetchProjects = async (): Promise => { try { const token = localStorage.getItem("accessToken") ?? ""; - const username = "NoUser"; // getUsernameFromContext(); // Assuming you have a function to get the username from your context + // const username = props.username; - const response = await api.getUserProjects(username, token); + const response = await api.getUserProjects(token); if (response.success) { setProjects(response.data ?? []); } else { @@ -26,18 +26,16 @@ const UserProjectListAdmin: React.FC = () => { }, []); return ( -
    -

    User Projects

    +
      {projects.map((project) => (
    • {project.name} - {/* Add any additional project details you want to display */}
    • ))}
    ); -}; +} export default UserProjectListAdmin; diff --git a/frontend/src/Pages/AdminPages/AdminChangeUsername.tsx b/frontend/src/Pages/AdminPages/AdminChangeUsername.tsx index 1756433..7eb2e2e 100644 --- a/frontend/src/Pages/AdminPages/AdminChangeUsername.tsx +++ b/frontend/src/Pages/AdminPages/AdminChangeUsername.tsx @@ -1,3 +1,4 @@ +import BackButton from "../../Components/BackButton"; import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; @@ -13,13 +14,7 @@ function AdminChangeUsername(): JSX.Element { }} type="button" /> -
    + ); +} diff --git a/frontend/src/Pages/UserPages/UserProjectPage.tsx b/frontend/src/Pages/UserPages/UserProjectPage.tsx index 20fe6d7..80a0035 100644 --- a/frontend/src/Pages/UserPages/UserProjectPage.tsx +++ b/frontend/src/Pages/UserPages/UserProjectPage.tsx @@ -1,18 +1,20 @@ -import { Link, useLocation } from "react-router-dom"; +import { Link, useLocation, useParams } from "react-router-dom"; import BasicWindow from "../../Components/BasicWindow"; import BackButton from "../../Components/BackButton"; function UserProjectPage(): JSX.Element { + const { projectName } = useParams(); + const content = ( <>

    {useLocation().state}

    - +

    Your Time Reports

    - +

    New Time Report

    diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index 32ba0ed..a3cd47a 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -11,9 +11,8 @@ function UserProjectPage(): JSX.Element { const [selectedProject, setSelectedProject] = useState(""); const getProjects = async (): Promise => { - 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); + const token = localStorage.getItem("accessToken") ?? ""; + const response = await api.getUserProjects(token); console.log(response); if (response.success) { setProjects(response.data ?? []); @@ -36,7 +35,7 @@ function UserProjectPage(): JSX.Element {
    {projects.map((project, index) => ( { handleProjectClick(project.name); }} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 193b692..1c39ae9 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -29,12 +29,14 @@ import AdminProjectManageMembers from "./Pages/AdminPages/AdminProjectManageMemb import AdminProjectStatistics from "./Pages/AdminPages/AdminProjectStatistics.tsx"; import AdminProjectViewMemberInfo from "./Pages/AdminPages/AdminProjectViewMemberInfo.tsx"; import AdminProjectPage from "./Pages/AdminPages/AdminProjectPage.tsx"; +import NotFoundPage from "./Pages/NotFoundPage.tsx"; // This is where the routes are mounted const router = createBrowserRouter([ { path: "/", element: , + errorElement: , }, { path: "/admin", @@ -44,30 +46,26 @@ const router = createBrowserRouter([ path: "/pm", element: , }, - { - path: "/user", - element: , - }, { path: "/yourProjects", element: , }, { - path: "/editTimeReport", - element: , - }, - { - path: "/newTimeReport", - element: , - }, - { - path: "/project", + path: "/project/:projectName", element: , }, { - path: "/projectPage", + path: "/newTimeReport/:projectName", + element: , + }, + { + path: "/projectPage/:projectName", element: , }, + { + path: "/editTimeReport", + element: , + }, { path: "/changeRole", element: ,