diff --git a/.gitignore b/.gitignore index 313b735..bdbfff8 100644 --- a/.gitignore +++ b/.gitignore @@ -36,7 +36,6 @@ 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 82a3551..5cbb13f 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -32,7 +32,6 @@ 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 @@ -314,26 +313,6 @@ 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 2378b3d..09de45b 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -536,46 +536,3 @@ 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") - } -} diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index ef7e48d..57a1969 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -16,9 +16,6 @@ type GlobalState interface { GetUserProjects(c *fiber.Ctx) error // To get all projects SubmitWeeklyReport(c *fiber.Ctx) error GetWeeklyReport(c *fiber.Ctx) error - SignReport(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 74f57f7..6a430e9 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -96,45 +96,3 @@ 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 291d068..506225b 100644 --- a/backend/internal/handlers/handlers_report_related.go +++ b/backend/internal/handlers/handlers_report_related.go @@ -63,43 +63,3 @@ func (gs *GState) GetWeeklyReport(c *fiber.Ctx) error { // Return the retrieved weekly report 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) - projectManagerUsername := claims["name"].(string) - - // 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") - // } - - // 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(rid.ReportId, projectManagerID) - if err != nil { - return c.Status(500).SendString(err.Error()) - } - - 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 0f7c047..0619ea5 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -1,7 +1,6 @@ package handlers import ( - "fmt" "time" "ttime/internal/types" @@ -123,25 +122,3 @@ 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 7cf6c9f..bc33942 100644 --- a/backend/main.go +++ b/backend/main.go @@ -79,9 +79,6 @@ func main() { server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches server.Post("/api/project", gs.CreateProject) 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..ac0f531 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,11 @@ interface API { week: string, token: string, ): Promise>; + /** Gets all the projects of a user*/ + getUserProjects( + username: string, + token: string, + ): Promise>; } // Export an instance of the API diff --git a/frontend/src/Components/AddProject.tsx b/frontend/src/Components/AddProject.tsx deleted file mode 100644 index 45814e3..0000000 --- a/frontend/src/Components/AddProject.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { useState } from "react"; -import { APIResponse, api } from "../API/API"; -import { NewProject, Project } 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 props - Project name and description - * @returns {boolean} True if created, false if not - */ -function CreateProject(props: { name: string; description: string }): boolean { - const project: NewProject = { - name: props.name, - description: props.description, - }; - - let created = false; - - api - .createProject(project, localStorage.getItem("accessToken") ?? "") - .then((response: APIResponse) => { - if (response.success) { - created = true; - } else { - console.error(response.message); - } - }) - .catch((error) => { - console.error("An error occurred during creation:", error); - }); - return created; -} - -/** - * Tries to add a project to the system - * @returns {JSX.Element} UI for project adding - */ -function AddProject(): JSX.Element { - const [name, setName] = useState(""); - const [description, setDescription] = useState(""); - - return ( -
-
-
{ - e.preventDefault(); - CreateProject({ name: name, description: description }); - }} - > - TTIME Logo -

- Create a new project -

- { - setName(e.target.value); - }} - /> - { - setDescription(e.target.value); - }} - /> -
-
- -

-
-
- ); -} - -export default AddProject; diff --git a/frontend/src/Components/BasicWindow.tsx b/frontend/src/Components/BasicWindow.tsx index d5fd3b6..1835d6a 100644 --- a/frontend/src/Components/BasicWindow.tsx +++ b/frontend/src/Components/BasicWindow.tsx @@ -2,15 +2,17 @@ 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 deleted file mode 100644 index 9321d73..0000000 --- a/frontend/src/Components/EditWeeklyReport.tsx +++ /dev/null @@ -1,247 +0,0 @@ -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); - } - }; - - 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(); - }} - /> -
-
-
-
- - ); -} diff --git a/frontend/src/Components/Header.tsx b/frontend/src/Components/Header.tsx index 819c5de..ba0a939 100644 --- a/frontend/src/Components/Header.tsx +++ b/frontend/src/Components/Header.tsx @@ -1,11 +1,11 @@ import { useState } from "react"; import { Link } from "react-router-dom"; -function Header(): JSX.Element { +function Header({ username }: { username: string }): JSX.Element { const [isOpen, setIsOpen] = useState(false); const handleLogout = (): void => { - localStorage.clear(); + // Add any logout logic here }; return ( @@ -31,7 +31,7 @@ function Header(): JSX.Element { }} > {isOpen && ( diff --git a/frontend/src/Components/LoginCheck.tsx b/frontend/src/Components/LoginCheck.tsx index ce7d52c..3658cbf 100644 --- a/frontend/src/Components/LoginCheck.tsx +++ b/frontend/src/Components/LoginCheck.tsx @@ -10,22 +10,17 @@ function LoginCheck(props: { username: string; password: string; setAuthority: Dispatch>; -}): void { +}): number { const user: NewUser = { username: props.username, password: props.password, }; - - localStorage.clear(); - api .login(user) .then((response: APIResponse) => { if (response.success) { 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) => { @@ -47,12 +42,14 @@ function LoginCheck(props: { console.error("Token was undefined"); } } else { - console.error("Token could not be fetched/No such user"); + console.error("Token could not be fetched"); } }) .catch((error) => { console.error("An error occurred during login:", error); }); + + return 0; } export default LoginCheck; diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx index facca39..af77d36 100644 --- a/frontend/src/Components/Register.tsx +++ b/frontend/src/Components/Register.tsx @@ -3,9 +3,34 @@ import { NewUser } from "../Types/goTypes"; import { api } from "../API/API"; import Logo from "../assets/Logo.svg"; import Button from "./Button"; -import InputField from "./InputField"; import { useNavigate } from "react-router-dom"; +function InputField(props: { + label: string; + type: string; + value: string; + onChange: (e: React.ChangeEvent) => void; +}): JSX.Element { + return ( +
+ + +
+ ); +} + export default function Register(): JSX.Element { const [username, setUsername] = useState(); const [password, setPassword] = useState(); @@ -23,7 +48,6 @@ export default function Register(): JSX.Element { nav("/"); // Instantly navigate to the login page } else { setErrMessage(response.message ?? "Unknown error"); - console.error(errMessage); } }; @@ -61,6 +85,43 @@ export default function Register(): JSX.Element { setPassword(e.target.value); }} /> +
+ + { + setUsername(e.target.value); + }} + /> +
+
+ + { + setPassword(e.target.value); + }} + /> +
+ {errMessage &&

{errMessage}

}