diff --git a/.gitignore b/.gitignore index bdbfff8..313b735 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ dist/ .vscode/ .idea/ .DS_Store +.go.work.sum # Ignore configuration files .env diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 5cbb13f..e2aa366 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 @@ -106,7 +107,10 @@ func (d *Db) GetAllProjects() ([]types.Project, error) { // GetProject retrieves a specific project by its ID. func (d *Db) GetProject(projectId int) (types.Project, error) { var project types.Project - err := d.Select(&project, "SELECT * FROM projects WHERE id = ?") + err := d.Get(&project, "SELECT * FROM projects WHERE id = ?", projectId) + if err != nil { + println("Error getting project: ", err) + } return project, err } @@ -313,6 +317,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..a7f3878 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -536,3 +536,33 @@ 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 TestGetProject(t *testing.T) { + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + + // Add a user + err = db.AddUser("testuser", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Add a project + err = db.AddProject("testproject", "description", "testuser") + if err != nil { + t.Error("AddProject failed:", err) + } + + // Retrieve the added project + project, err := db.GetProject(1) + if err != nil { + t.Error("GetProject failed:", err) + } + + // Check if the retrieved project matches the expected values + if project.Name != "testproject" { + t.Errorf("Expected Name to be testproject, got %s", project.Name) + } +} diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index c8beb1c..566d549 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -17,6 +17,9 @@ type GlobalState interface { SubmitWeeklyReport(c *fiber.Ctx) error GetWeeklyReport(c *fiber.Ctx) error SignReport(c *fiber.Ctx) error + GetProject(c *fiber.Ctx) error + AddUserToProjectHandler(c *fiber.Ctx) error + PromoteToAdmin(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 6a430e9..3732249 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -66,6 +66,10 @@ func (gs *GState) ProjectRoleChange(c *fiber.Ctx) error { func (gs *GState) GetProject(c *fiber.Ctx) error { // Extract the project ID from the request parameters or body projectID := c.Params("projectID") + if projectID == "" { + return c.Status(400).SendString("No project ID provided") + } + println("Getting project with ID: ", projectID) // Parse the project ID into an integer projectIDInt, err := strconv.Atoi(projectID) @@ -80,6 +84,7 @@ func (gs *GState) GetProject(c *fiber.Ctx) error { } // Return the project as JSON + println("Returning project: ", project.Name) return c.JSON(project) } @@ -96,3 +101,45 @@ func (gs *GState) ListAllUsersProject(c *fiber.Ctx) error { // Return the list of users as JSON return c.JSON(users) } + +// AddUserToProjectHandler is a handler that adds a user to a project with a specified role +func (gs *GState) AddUserToProjectHandler(c *fiber.Ctx) error { + // Extract necessary parameters from the request + var requestData struct { + Username string `json:"username"` + ProjectName string `json:"projectName"` + Role string `json:"role"` + } + if err := c.BodyParser(&requestData); err != nil { + println("Error parsing request body:", err) + return c.Status(400).SendString("Bad request") + } + + // Check if the user adding another user to the project is a site admin + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + adminUsername := claims["name"].(string) + println("Admin username from claims:", adminUsername) + + isAdmin, err := gs.Db.IsSiteAdmin(adminUsername) + if err != nil { + println("Error checking admin status:", err) + return c.Status(500).SendString(err.Error()) + } + + if !isAdmin { + println("User is not a site admin:", adminUsername) + return c.Status(403).SendString("User is not a site admin") + } + + // Add the user to the project with the specified role + err = gs.Db.AddUserToProject(requestData.Username, requestData.ProjectName, requestData.Role) + if err != nil { + println("Error adding user to project:", err) + return c.Status(500).SendString(err.Error()) + } + + // Return success message + println("User added to project successfully:", requestData.Username) + return c.SendStatus(fiber.StatusOK) +} diff --git a/backend/internal/handlers/handlers_report_related.go b/backend/internal/handlers/handlers_report_related.go index 509bd67..291d068 100644 --- a/backend/internal/handlers/handlers_report_related.go +++ b/backend/internal/handlers/handlers_report_related.go @@ -64,30 +64,42 @@ func (gs *GState) GetWeeklyReport(c *fiber.Ctx) error { return c.JSON(report) } +type ReportId struct { + ReportId int +} + func (gs *GState) SignReport(c *fiber.Ctx) error { + println("Signing report...") // Extract the necessary parameters from the token user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) - managerUsername := claims["name"].(string) + projectManagerUsername := claims["name"].(string) - // Extract the report ID and project manager ID from request parameters - reportID, err := strconv.Atoi(c.Params("reportId")) - if err != nil { - return c.Status(400).SendString("Invalid report ID") + // Extract report ID from the request query parameters + // reportID := c.Query("reportId") + rid := new(ReportId) + if err := c.BodyParser(rid); err != nil { + return err } + println("Signing report for: ", rid.ReportId) + // reportIDInt, err := strconv.Atoi(rid.ReportId) + // println("Signing report for: ", rid.ReportId) + // if err != nil { + // return c.Status(400).SendString("Invalid report ID") + // } - // Call the database function to get the project manager ID - managerID, err := gs.Db.GetUserId(managerUsername) + // Get the project manager's ID + projectManagerID, err := gs.Db.GetUserId(projectManagerUsername) if err != nil { return c.Status(500).SendString("Failed to get project manager ID") } + println("blabla", projectManagerID) // Call the database function to sign the weekly report - err = gs.Db.SignWeeklyReport(reportID, managerID) + err = gs.Db.SignWeeklyReport(rid.ReportId, projectManagerID) if err != nil { - return c.Status(500).SendString("Failed to sign the weekly report: " + err.Error()) + return c.Status(500).SendString(err.Error()) } - // Return success response return c.Status(200).SendString("Weekly report signed successfully") } diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index 0619ea5..0f7c047 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -1,6 +1,7 @@ package handlers import ( + "fmt" "time" "ttime/internal/types" @@ -122,3 +123,25 @@ func (gs *GState) ListAllUsers(c *fiber.Ctx) error { // Return the list of users as JSON return c.JSON(users) } + +func (gs *GState) PromoteToAdmin(c *fiber.Ctx) error { + // Extract the username from the request body + var newUser types.NewUser + if err := c.BodyParser(&newUser); err != nil { + return c.Status(400).SendString("Bad request") + } + username := newUser.Username + + println("Promoting user to admin:", username) // Debug print + + // Promote the user to a site admin in the database + if err := gs.Db.PromoteToAdmin(username); err != nil { + fmt.Println("Error promoting user to admin:", err) // Debug print + return c.Status(500).SendString(err.Error()) + } + + println("User promoted to admin successfully:", username) // Debug print + + // Return a success message + return c.SendStatus(fiber.StatusOK) +} diff --git a/backend/main.go b/backend/main.go index bc33942..3e2fb75 100644 --- a/backend/main.go +++ b/backend/main.go @@ -78,7 +78,11 @@ func main() { server.Post("/api/loginrenew", gs.LoginRenew) server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches server.Post("/api/project", gs.CreateProject) + server.Get("/api/project/:projectId", gs.GetProject) server.Get("/api/getWeeklyReport", gs.GetWeeklyReport) + server.Post("/api/signReport", gs.SignReport) + server.Put("/api/addUserToProject", gs.AddUserToProjectHandler) + server.Post("/api/promoteToAdmin", gs.PromoteToAdmin) // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 7a1ccd0..6078513 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -29,11 +29,6 @@ interface API { project: NewProject, token: string, ): Promise>; - /** Gets all the projects of a user*/ - getUserProjects( - username: string, - token: string, - ): Promise>; /** Submit a weekly report */ submitWeeklyReport( project: NewWeeklyReport, @@ -46,6 +41,13 @@ interface API { week: string, token: string, ): Promise>; + /** Gets all the projects of a user*/ + getUserProjects( + username: string, + token: string, + ): Promise>; + /** Gets a project from id*/ + getProject(id: number): Promise>; } // Export an instance of the API @@ -148,7 +150,10 @@ export const api: API = { } }, - async getUserProjects(token: string): Promise> { + async getUserProjects( + username: string, + token: string, + ): Promise> { try { const response = await fetch("/api/getUserProjects", { method: "GET", @@ -156,6 +161,7 @@ export const api: API = { "Content-Type": "application/json", Authorization: "Bearer " + token, }, + body: JSON.stringify({ username }), }); if (!response.ok) { @@ -253,4 +259,30 @@ export const api: API = { return Promise.resolve({ success: false, message: "Failed to login" }); } }, + + // Gets a projet by id, currently untested since we have no javascript-based tests + async getProject(id: number): Promise> { + try { + const response = await fetch(`/api/project/${id}`, { + method: "GET", + }); + + if (!response.ok) { + return { + success: false, + message: "Failed to get project: Response code " + response.status, + }; + } else { + const data = (await response.json()) as Project; + return { success: true, data }; + } + // The code below is garbage but satisfies the linter + // This needs fixing, do not copy this pattern + } catch (e: unknown) { + return { + success: false, + message: "Failed to get project: " + (e as Error).toString(), + }; + } + }, }; diff --git a/frontend/src/Components/BasicWindow.tsx b/frontend/src/Components/BasicWindow.tsx index 1835d6a..d5fd3b6 100644 --- a/frontend/src/Components/BasicWindow.tsx +++ b/frontend/src/Components/BasicWindow.tsx @@ -2,17 +2,15 @@ import Header from "./Header"; import Footer from "./Footer"; function BasicWindow({ - username, content, buttons, }: { - username: string; content: React.ReactNode; buttons: React.ReactNode; }): JSX.Element { return (
-
+
{content}
{buttons}
diff --git a/frontend/src/Components/EditWeeklyReport.tsx b/frontend/src/Components/EditWeeklyReport.tsx new file mode 100644 index 0000000..b0e8771 --- /dev/null +++ b/frontend/src/Components/EditWeeklyReport.tsx @@ -0,0 +1,247 @@ +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") ?? ""; + const username = localStorage.getItem("username") ?? ""; + + useEffect(() => { + const fetchWeeklyReport = async (): Promise => { + const response = await api.getWeeklyReport( + username, + projectName, + week.toString(), + 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); + } + }; + + void fetchWeeklyReport(); + }, [projectName, token, username, week]); + + 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(); + }} + /> +
+
+
+
+ + ); +} diff --git a/frontend/src/Components/Header.tsx b/frontend/src/Components/Header.tsx index 7a1e8ba..819c5de 100644 --- a/frontend/src/Components/Header.tsx +++ b/frontend/src/Components/Header.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { Link } from "react-router-dom"; -function Header({ username }: { username: string }): JSX.Element { +function Header(): JSX.Element { const [isOpen, setIsOpen] = useState(false); const handleLogout = (): void => { @@ -31,7 +31,7 @@ function Header({ username }: { username: string }): JSX.Element { }} > {isOpen && ( diff --git a/frontend/src/Components/LoginCheck.tsx b/frontend/src/Components/LoginCheck.tsx index 786a96c..ce7d52c 100644 --- a/frontend/src/Components/LoginCheck.tsx +++ b/frontend/src/Components/LoginCheck.tsx @@ -25,6 +25,7 @@ function LoginCheck(props: { if (response.data !== undefined) { const token = response.data; localStorage.setItem("accessToken", token); + localStorage.setItem("username", props.username); //TODO: change so that it checks for user type (admin, user, pm) instead if (token !== "" && props.username === "admin") { props.setAuthority((prevAuth) => { diff --git a/frontend/src/Components/TimeReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx similarity index 77% rename from frontend/src/Components/TimeReport.tsx rename to frontend/src/Components/NewWeeklyReport.tsx index e7eb5b7..0a84b48 100644 --- a/frontend/src/Components/TimeReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -1,50 +1,51 @@ -import { useState } from "react"; +import { useState, useContext } from "react"; +import type { NewWeeklyReport } from "../Types/goTypes"; import { api } from "../API/API"; import { useNavigate } from "react-router-dom"; import Button from "./Button"; -import { NewWeeklyReport } from "../Types/goTypes"; +import { ProjectNameContext } from "../Pages/YourProjectsPage"; -export default function NewTimeReport(): JSX.Element { - const [projectName, setProjectName] = useState("projectName"); // TODO: Get from backend - const [week, setWeek] = useState(NaN); - const [development, setDevelopment] = useState(NaN); - const [meeting, setMeeting] = useState(NaN); - const [administration, setAdministration] = useState(NaN); - const [ownwork, setOwnWork] = useState(NaN); - const [studies, setStudies] = useState(NaN); - const [testing, setTesting] = useState(NaN); +export default function NewWeeklyReport(): JSX.Element { + 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 handleNewTimeReport = async (): Promise => { - const newTimeReport: NewWeeklyReport = { + const projectName = useContext(ProjectNameContext); + const token = localStorage.getItem("accessToken") ?? ""; + + const handleNewWeeklyReport = async (): Promise => { + const newWeeklyReport: NewWeeklyReport = { projectName, week, - developmentTime: development, - meetingTime: meeting, - adminTime: administration, - ownWorkTime: ownwork, - studyTime: studies, - testingTime: testing, + developmentTime, + meetingTime, + adminTime, + ownWorkTime, + studyTime, + testingTime, }; - await Promise.resolve(); - await api.submitWeeklyReport(newTimeReport, "token"); + + await api.submitWeeklyReport(newWeeklyReport, token); }; const navigate = useNavigate(); - setProjectName("Something Reasonable"); // This should obviously not be used here - return ( <>
{ - if (!week) { + if (week === 0) { alert("Please enter a week number"); e.preventDefault(); return; } e.preventDefault(); - void handleNewTimeReport(); + void handleNewWeeklyReport(); navigate("/project"); }} > @@ -83,9 +84,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={development} + value={developmentTime} onChange={(e) => { - setDevelopment(parseInt(e.target.value)); + setDevelopmentTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -102,9 +103,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={meeting} + value={meetingTime} onChange={(e) => { - setMeeting(parseInt(e.target.value)); + setMeetingTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -121,9 +122,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={administration} + value={adminTime} onChange={(e) => { - setAdministration(parseInt(e.target.value)); + setAdminTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -140,9 +141,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={ownwork} + value={ownWorkTime} onChange={(e) => { - setOwnWork(parseInt(e.target.value)); + setOwnWorkTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -159,9 +160,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={studies} + value={studyTime} onChange={(e) => { - setStudies(parseInt(e.target.value)); + setStudyTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -178,9 +179,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={testing} + value={testingTime} onChange={(e) => { - setTesting(parseInt(e.target.value)); + setTestingTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx index facca39..7b003cb 100644 --- a/frontend/src/Components/Register.tsx +++ b/frontend/src/Components/Register.tsx @@ -48,7 +48,7 @@ export default function Register(): JSX.Element { { setUsername(e.target.value); }} @@ -56,7 +56,7 @@ export default function Register(): JSX.Element { { setPassword(e.target.value); }} diff --git a/frontend/src/Components/UserProjectListAdmin.tsx b/frontend/src/Components/UserProjectListAdmin.tsx new file mode 100644 index 0000000..69258a1 --- /dev/null +++ b/frontend/src/Components/UserProjectListAdmin.tsx @@ -0,0 +1,43 @@ +import React, { useEffect, useState } from "react"; +import { api } from "../API/API"; +import { Project } from "../Types/goTypes"; + +const UserProjectListAdmin: React.FC = () => { + 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 response = await api.getUserProjects(username, 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 fetchProjects(); + }, []); + + 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/AdminAddProject.tsx b/frontend/src/Pages/AdminPages/AdminAddProject.tsx index 6df8851..aedbd3f 100644 --- a/frontend/src/Pages/AdminPages/AdminAddProject.tsx +++ b/frontend/src/Pages/AdminPages/AdminAddProject.tsx @@ -11,6 +11,6 @@ function AdminAddProject(): JSX.Element { ); - return ; + return ; } export default AdminAddProject; diff --git a/frontend/src/Pages/AdminPages/AdminAddUser.tsx b/frontend/src/Pages/AdminPages/AdminAddUser.tsx index c0f9492..4af2eb7 100644 --- a/frontend/src/Pages/AdminPages/AdminAddUser.tsx +++ b/frontend/src/Pages/AdminPages/AdminAddUser.tsx @@ -1,5 +1,5 @@ +import BackButton from "../../Components/BackButton"; import BasicWindow from "../../Components/BasicWindow"; -import Button from "../../Components/Button"; import Register from "../../Components/Register"; function AdminAddUser(): JSX.Element { @@ -11,16 +11,10 @@ function AdminAddUser(): JSX.Element { const buttons = ( <> -