Compare commits

..

No commits in common. "e0de61dd94c97600b551ddf10d0061a4362f03f9" and "d6ce4a3c57851f9f537f3432a581329a9a17d8a0" have entirely different histories.

46 changed files with 160 additions and 890 deletions

1
.gitignore vendored
View file

@ -36,7 +36,6 @@ dist/
.vscode/ .vscode/
.idea/ .idea/
.DS_Store .DS_Store
.go.work.sum
# Ignore configuration files # Ignore configuration files
.env .env

View file

@ -32,7 +32,6 @@ type Database interface {
GetUserRole(username string, projectname string) (string, error) GetUserRole(username string, projectname string) (string, error)
GetWeeklyReport(username string, projectName string, week int) (types.WeeklyReport, error) GetWeeklyReport(username string, projectName string, week int) (types.WeeklyReport, error)
SignWeeklyReport(reportId int, projectManagerId int) error SignWeeklyReport(reportId int, projectManagerId int) error
IsSiteAdmin(username string) (bool, error)
} }
// This struct is a wrapper type that holds the database connection // This struct is a wrapper type that holds the database connection
@ -107,10 +106,7 @@ func (d *Db) GetAllProjects() ([]types.Project, error) {
// GetProject retrieves a specific project by its ID. // GetProject retrieves a specific project by its ID.
func (d *Db) GetProject(projectId int) (types.Project, error) { func (d *Db) GetProject(projectId int) (types.Project, error) {
var project types.Project var project types.Project
err := d.Get(&project, "SELECT * FROM projects WHERE id = ?", projectId) err := d.Select(&project, "SELECT * FROM projects WHERE id = ?")
if err != nil {
println("Error getting project: ", err)
}
return project, err return project, err
} }
@ -317,26 +313,6 @@ func (d *Db) SignWeeklyReport(reportId int, projectManagerId int) error {
return err 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. // Reads a directory of migration files and applies them to the database.
// This will eventually be used on an embedded directory // This will eventually be used on an embedded directory
func (d *Db) Migrate() error { func (d *Db) Migrate() error {

View file

@ -536,33 +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") 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)
}
}

View file

@ -17,9 +17,6 @@ type GlobalState interface {
SubmitWeeklyReport(c *fiber.Ctx) error SubmitWeeklyReport(c *fiber.Ctx) error
GetWeeklyReport(c *fiber.Ctx) error GetWeeklyReport(c *fiber.Ctx) error
SignReport(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 // GetProject(c *fiber.Ctx) error // To get a specific project
// UpdateProject(c *fiber.Ctx) error // To update a project // UpdateProject(c *fiber.Ctx) error // To update a project
// DeleteProject(c *fiber.Ctx) error // To delete a project // DeleteProject(c *fiber.Ctx) error // To delete a project

View file

@ -66,10 +66,6 @@ func (gs *GState) ProjectRoleChange(c *fiber.Ctx) error {
func (gs *GState) GetProject(c *fiber.Ctx) error { func (gs *GState) GetProject(c *fiber.Ctx) error {
// Extract the project ID from the request parameters or body // Extract the project ID from the request parameters or body
projectID := c.Params("projectID") 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 // Parse the project ID into an integer
projectIDInt, err := strconv.Atoi(projectID) projectIDInt, err := strconv.Atoi(projectID)
@ -84,7 +80,6 @@ func (gs *GState) GetProject(c *fiber.Ctx) error {
} }
// Return the project as JSON // Return the project as JSON
println("Returning project: ", project.Name)
return c.JSON(project) return c.JSON(project)
} }
@ -101,45 +96,3 @@ func (gs *GState) ListAllUsersProject(c *fiber.Ctx) error {
// Return the list of users as JSON // Return the list of users as JSON
return c.JSON(users) 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)
}

View file

@ -64,42 +64,30 @@ func (gs *GState) GetWeeklyReport(c *fiber.Ctx) error {
return c.JSON(report) return c.JSON(report)
} }
type ReportId struct {
ReportId int
}
func (gs *GState) SignReport(c *fiber.Ctx) error { func (gs *GState) SignReport(c *fiber.Ctx) error {
println("Signing report...")
// Extract the necessary parameters from the token // Extract the necessary parameters from the token
user := c.Locals("user").(*jwt.Token) user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims) claims := user.Claims.(jwt.MapClaims)
projectManagerUsername := claims["name"].(string) managerUsername := claims["name"].(string)
// Extract report ID from the request query parameters // Extract the report ID and project manager ID from request parameters
// reportID := c.Query("reportId") reportID, err := strconv.Atoi(c.Params("reportId"))
rid := new(ReportId) if err != nil {
if err := c.BodyParser(rid); err != nil { return c.Status(400).SendString("Invalid report ID")
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 // Call the database function to get the project manager ID
projectManagerID, err := gs.Db.GetUserId(projectManagerUsername) managerID, err := gs.Db.GetUserId(managerUsername)
if err != nil { if err != nil {
return c.Status(500).SendString("Failed to get project manager ID") return c.Status(500).SendString("Failed to get project manager ID")
} }
println("blabla", projectManagerID)
// Call the database function to sign the weekly report // Call the database function to sign the weekly report
err = gs.Db.SignWeeklyReport(rid.ReportId, projectManagerID) err = gs.Db.SignWeeklyReport(reportID, managerID)
if err != nil { if err != nil {
return c.Status(500).SendString(err.Error()) return c.Status(500).SendString("Failed to sign the weekly report: " + err.Error())
} }
// Return success response
return c.Status(200).SendString("Weekly report signed successfully") return c.Status(200).SendString("Weekly report signed successfully")
} }

View file

@ -1,7 +1,6 @@
package handlers package handlers
import ( import (
"fmt"
"time" "time"
"ttime/internal/types" "ttime/internal/types"
@ -123,25 +122,3 @@ func (gs *GState) ListAllUsers(c *fiber.Ctx) error {
// Return the list of users as JSON // Return the list of users as JSON
return c.JSON(users) 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)
}

View file

@ -78,11 +78,7 @@ func main() {
server.Post("/api/loginrenew", gs.LoginRenew) server.Post("/api/loginrenew", gs.LoginRenew)
server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches
server.Post("/api/project", gs.CreateProject) server.Post("/api/project", gs.CreateProject)
server.Get("/api/project/:projectId", gs.GetProject)
server.Get("/api/getWeeklyReport", gs.GetWeeklyReport) 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 // Announce the port we are listening on and start the server
err = server.Listen(fmt.Sprintf(":%d", conf.Port)) err = server.Listen(fmt.Sprintf(":%d", conf.Port))

View file

@ -29,6 +29,11 @@ interface API {
project: NewProject, project: NewProject,
token: string, token: string,
): Promise<APIResponse<Project>>; ): Promise<APIResponse<Project>>;
/** Gets all the projects of a user*/
getUserProjects(
username: string,
token: string,
): Promise<APIResponse<Project[]>>;
/** Submit a weekly report */ /** Submit a weekly report */
submitWeeklyReport( submitWeeklyReport(
project: NewWeeklyReport, project: NewWeeklyReport,
@ -41,13 +46,6 @@ interface API {
week: string, week: string,
token: string, token: string,
): Promise<APIResponse<NewWeeklyReport>>; ): Promise<APIResponse<NewWeeklyReport>>;
/** Gets all the projects of a user*/
getUserProjects(
username: string,
token: string,
): Promise<APIResponse<Project[]>>;
/** Gets a project from id*/
getProject(id: number): Promise<APIResponse<Project>>;
} }
// Export an instance of the API // Export an instance of the API
@ -150,10 +148,7 @@ export const api: API = {
} }
}, },
async getUserProjects( async getUserProjects(token: string): Promise<APIResponse<Project[]>> {
username: string,
token: string,
): Promise<APIResponse<Project[]>> {
try { try {
const response = await fetch("/api/getUserProjects", { const response = await fetch("/api/getUserProjects", {
method: "GET", method: "GET",
@ -161,7 +156,6 @@ export const api: API = {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: "Bearer " + token, Authorization: "Bearer " + token,
}, },
body: JSON.stringify({ username }),
}); });
if (!response.ok) { if (!response.ok) {
@ -259,30 +253,4 @@ export const api: API = {
return Promise.resolve({ success: false, message: "Failed to login" }); 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<APIResponse<Project>> {
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(),
};
}
},
}; };

View file

@ -2,15 +2,17 @@ import Header from "./Header";
import Footer from "./Footer"; import Footer from "./Footer";
function BasicWindow({ function BasicWindow({
username,
content, content,
buttons, buttons,
}: { }: {
username: string;
content: React.ReactNode; content: React.ReactNode;
buttons: React.ReactNode; buttons: React.ReactNode;
}): JSX.Element { }): JSX.Element {
return ( return (
<div className="font-sans flex flex-col h-screen bg-white border-2 border-black overflow-auto pt-[110px]"> <div className="font-sans flex flex-col h-screen bg-white border-2 border-black overflow-auto pt-[110px]">
<Header /> <Header username={username} />
<div className="flex flex-col items-center flex-grow">{content}</div> <div className="flex flex-col items-center flex-grow">{content}</div>
<Footer>{buttons}</Footer> <Footer>{buttons}</Footer>
</div> </div>

View file

@ -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<void> => {
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<void> => {
const newWeeklyReport: NewWeeklyReport = {
projectName,
week,
developmentTime,
meetingTime,
adminTime,
ownWorkTime,
studyTime,
testingTime,
};
await api.submitWeeklyReport(newWeeklyReport, token);
};
const navigate = useNavigate();
return (
<>
<div className="border-4 border-black bg-white flex flex-col justify-start min-h-[65vh] h-fit w-[50vw] rounded-3xl overflow-scroll space-y-[2vh] p-[30px] items-center">
<form
onSubmit={(e) => {
if (week === 0) {
alert("Please enter a week number");
e.preventDefault();
return;
}
e.preventDefault();
void handleNewWeeklyReport();
navigate("/project");
}}
>
<div className="flex flex-col items-center">
<input
className="w-fill h-[5vh] font-sans text-[3vh] pl-[1vw] rounded-full text-center pt-[1vh] pb-[1vh] border-2 border-black"
type="week"
placeholder="Week"
value={
week === 0 ? "" : `2024-W${week.toString().padStart(2, "0")}`
}
onChange={(e) => {
const weekNumber = parseInt(e.target.value.split("-W")[1]);
setWeek(weekNumber);
}}
onKeyDown={(event) => {
event.preventDefault();
}}
onPaste={(event) => {
event.preventDefault();
}}
/>
<table className="w-full text-center divide-y divide-x divide-white text-[30px]">
<thead>
<tr>
<th className="w-1/2 py-2 border-b-2 border-black">
Activity
</th>
<th className="w-1/2 py-2 border-b-2 border-black">
Total Time (min)
</th>
</tr>
</thead>
<tbody className="divide-y divide-black">
<tr className="h-[10vh]">
<td>Development</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={developmentTime}
onChange={(e) => {
setDevelopmentTime(parseInt(e.target.value));
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
<tr className="h-[10vh]">
<td>Meeting</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={meetingTime}
onChange={(e) => {
setMeetingTime(parseInt(e.target.value));
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
<tr className="h-[10vh]">
<td>Administration</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={adminTime}
onChange={(e) => {
setAdminTime(parseInt(e.target.value));
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
<tr className="h-[10vh]">
<td>Own Work</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={ownWorkTime}
onChange={(e) => {
setOwnWorkTime(parseInt(e.target.value));
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
<tr className="h-[10vh]">
<td>Studies</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={studyTime}
onChange={(e) => {
setStudyTime(parseInt(e.target.value));
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
<tr className="h-[10vh]">
<td>Testing</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={testingTime}
onChange={(e) => {
setTestingTime(parseInt(e.target.value));
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
</tbody>
</table>
<Button
text="Submit"
onClick={(): void => {
return;
}}
type="submit"
/>
</div>
</form>
</div>
</>
);
}

View file

@ -1,7 +1,7 @@
import { useState } from "react"; import { useState } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
function Header(): JSX.Element { function Header({ username }: { username: string }): JSX.Element {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const handleLogout = (): void => { const handleLogout = (): void => {
@ -31,7 +31,7 @@ function Header(): JSX.Element {
}} }}
> >
<button className="mr-4 underline font-bold text-white"> <button className="mr-4 underline font-bold text-white">
{localStorage.getItem("username")} {username}
</button> </button>
{isOpen && ( {isOpen && (

View file

@ -25,7 +25,6 @@ function LoginCheck(props: {
if (response.data !== undefined) { if (response.data !== undefined) {
const token = response.data; const token = response.data;
localStorage.setItem("accessToken", token); localStorage.setItem("accessToken", token);
localStorage.setItem("username", props.username);
//TODO: change so that it checks for user type (admin, user, pm) instead //TODO: change so that it checks for user type (admin, user, pm) instead
if (token !== "" && props.username === "admin") { if (token !== "" && props.username === "admin") {
props.setAuthority((prevAuth) => { props.setAuthority((prevAuth) => {

View file

@ -48,7 +48,7 @@ export default function Register(): JSX.Element {
<InputField <InputField
label="Username" label="Username"
type="text" type="text"
value={username ?? ""} value={username}
onChange={(e) => { onChange={(e) => {
setUsername(e.target.value); setUsername(e.target.value);
}} }}
@ -56,7 +56,7 @@ export default function Register(): JSX.Element {
<InputField <InputField
label="Password" label="Password"
type="password" type="password"
value={password ?? ""} value={password}
onChange={(e) => { onChange={(e) => {
setPassword(e.target.value); setPassword(e.target.value);
}} }}

View file

@ -1,51 +1,50 @@
import { useState, useContext } from "react"; import { useState } from "react";
import type { NewWeeklyReport } from "../Types/goTypes";
import { api } from "../API/API"; import { api } from "../API/API";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import Button from "./Button"; import Button from "./Button";
import { ProjectNameContext } from "../Pages/YourProjectsPage"; import { NewWeeklyReport } from "../Types/goTypes";
export default function NewWeeklyReport(): JSX.Element { export default function NewTimeReport(): JSX.Element {
const [week, setWeek] = useState(0); const [projectName, setProjectName] = useState<string>("projectName"); // TODO: Get from backend
const [developmentTime, setDevelopmentTime] = useState(0); const [week, setWeek] = useState<number>(NaN);
const [meetingTime, setMeetingTime] = useState(0); const [development, setDevelopment] = useState<number>(NaN);
const [adminTime, setAdminTime] = useState(0); const [meeting, setMeeting] = useState<number>(NaN);
const [ownWorkTime, setOwnWorkTime] = useState(0); const [administration, setAdministration] = useState<number>(NaN);
const [studyTime, setStudyTime] = useState(0); const [ownwork, setOwnWork] = useState<number>(NaN);
const [testingTime, setTestingTime] = useState(0); const [studies, setStudies] = useState<number>(NaN);
const [testing, setTesting] = useState<number>(NaN);
const projectName = useContext(ProjectNameContext); const handleNewTimeReport = async (): Promise<void> => {
const token = localStorage.getItem("accessToken") ?? ""; const newTimeReport: NewWeeklyReport = {
const handleNewWeeklyReport = async (): Promise<void> => {
const newWeeklyReport: NewWeeklyReport = {
projectName, projectName,
week, week,
developmentTime, developmentTime: development,
meetingTime, meetingTime: meeting,
adminTime, adminTime: administration,
ownWorkTime, ownWorkTime: ownwork,
studyTime, studyTime: studies,
testingTime, testingTime: testing,
}; };
await Promise.resolve();
await api.submitWeeklyReport(newWeeklyReport, token); await api.submitWeeklyReport(newTimeReport, "token");
}; };
const navigate = useNavigate(); const navigate = useNavigate();
setProjectName("Something Reasonable"); // This should obviously not be used here
return ( return (
<> <>
<div className="border-4 border-black bg-white flex flex-col justify-start min-h-[65vh] h-fit w-[50vw] rounded-3xl overflow-scroll space-y-[2vh] p-[30px] items-center"> <div className="border-4 border-black bg-white flex flex-col justify-start min-h-[65vh] h-fit w-[50vw] rounded-3xl overflow-scroll space-y-[2vh] p-[30px] items-center">
<form <form
onSubmit={(e) => { onSubmit={(e) => {
if (week === 0) { if (!week) {
alert("Please enter a week number"); alert("Please enter a week number");
e.preventDefault(); e.preventDefault();
return; return;
} }
e.preventDefault(); e.preventDefault();
void handleNewWeeklyReport(); void handleNewTimeReport();
navigate("/project"); navigate("/project");
}} }}
> >
@ -84,9 +83,9 @@ export default function NewWeeklyReport(): JSX.Element {
type="number" type="number"
min="0" min="0"
className="border-2 border-black rounded-md text-center w-1/2" className="border-2 border-black rounded-md text-center w-1/2"
value={developmentTime} value={development}
onChange={(e) => { onChange={(e) => {
setDevelopmentTime(parseInt(e.target.value)); setDevelopment(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;
@ -103,9 +102,9 @@ export default function NewWeeklyReport(): JSX.Element {
type="number" type="number"
min="0" min="0"
className="border-2 border-black rounded-md text-center w-1/2" className="border-2 border-black rounded-md text-center w-1/2"
value={meetingTime} value={meeting}
onChange={(e) => { onChange={(e) => {
setMeetingTime(parseInt(e.target.value)); setMeeting(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;
@ -122,9 +121,9 @@ export default function NewWeeklyReport(): JSX.Element {
type="number" type="number"
min="0" min="0"
className="border-2 border-black rounded-md text-center w-1/2" className="border-2 border-black rounded-md text-center w-1/2"
value={adminTime} value={administration}
onChange={(e) => { onChange={(e) => {
setAdminTime(parseInt(e.target.value)); setAdministration(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;
@ -141,9 +140,9 @@ export default function NewWeeklyReport(): JSX.Element {
type="number" type="number"
min="0" min="0"
className="border-2 border-black rounded-md text-center w-1/2" className="border-2 border-black rounded-md text-center w-1/2"
value={ownWorkTime} value={ownwork}
onChange={(e) => { onChange={(e) => {
setOwnWorkTime(parseInt(e.target.value)); setOwnWork(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;
@ -160,9 +159,9 @@ export default function NewWeeklyReport(): JSX.Element {
type="number" type="number"
min="0" min="0"
className="border-2 border-black rounded-md text-center w-1/2" className="border-2 border-black rounded-md text-center w-1/2"
value={studyTime} value={studies}
onChange={(e) => { onChange={(e) => {
setStudyTime(parseInt(e.target.value)); setStudies(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;
@ -179,9 +178,9 @@ export default function NewWeeklyReport(): JSX.Element {
type="number" type="number"
min="0" min="0"
className="border-2 border-black rounded-md text-center w-1/2" className="border-2 border-black rounded-md text-center w-1/2"
value={testingTime} value={testing}
onChange={(e) => { onChange={(e) => {
setTestingTime(parseInt(e.target.value)); setTesting(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;

View file

@ -1,43 +0,0 @@
import React, { useEffect, useState } from "react";
import { api } from "../API/API";
import { Project } from "../Types/goTypes";
const UserProjectListAdmin: React.FC = () => {
const [projects, setProjects] = useState<Project[]>([]);
useEffect(() => {
const fetchProjects = async (): Promise<void> => {
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 (
<div>
<h2>User Projects</h2>
<ul>
{projects.map((project) => (
<li key={project.id}>
<span>{project.name}</span>
{/* Add any additional project details you want to display */}
</li>
))}
</ul>
</div>
);
};
export default UserProjectListAdmin;

View file

@ -11,6 +11,6 @@ function AdminAddProject(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminAddProject; export default AdminAddProject;

View file

@ -1,5 +1,5 @@
import BackButton from "../../Components/BackButton";
import BasicWindow from "../../Components/BasicWindow"; import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
import Register from "../../Components/Register"; import Register from "../../Components/Register";
function AdminAddUser(): JSX.Element { function AdminAddUser(): JSX.Element {
@ -11,10 +11,16 @@ function AdminAddUser(): JSX.Element {
const buttons = ( const buttons = (
<> <>
<BackButton /> <Button
text="Back"
onClick={(): void => {
return;
}}
type="button"
/>
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminAddUser; export default AdminAddUser;

View file

@ -23,6 +23,6 @@ function AdminChangeUsername(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminChangeUsername; export default AdminChangeUsername;

View file

@ -21,6 +21,6 @@ function AdminManageProjects(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminManageProjects; export default AdminManageProjects;

View file

@ -36,6 +36,6 @@ function AdminManageUsers(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminManageUsers; export default AdminManageUsers;

View file

@ -22,6 +22,6 @@ function AdminMenuPage(): JSX.Element {
const buttons = <></>; const buttons = <></>;
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminMenuPage; export default AdminMenuPage;

View file

@ -23,6 +23,6 @@ function AdminProjectAddMember(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminProjectAddMember; export default AdminProjectAddMember;

View file

@ -23,6 +23,6 @@ function AdminProjectChangeUserRole(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminProjectChangeUserRole; export default AdminProjectChangeUserRole;

View file

@ -23,6 +23,6 @@ function AdminProjectManageMembers(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminProjectManageMembers; export default AdminProjectManageMembers;

View file

@ -23,6 +23,6 @@ function AdminProjectPage(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminProjectPage; export default AdminProjectPage;

View file

@ -16,6 +16,6 @@ function AdminProjectStatistics(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminProjectStatistics; export default AdminProjectStatistics;

View file

@ -23,6 +23,6 @@ function AdminProjectViewMemberInfo(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminProjectViewMemberInfo; export default AdminProjectViewMemberInfo;

View file

@ -1,12 +1,15 @@
import { useLocation } from "react-router-dom";
import BasicWindow from "../../Components/BasicWindow"; import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button"; import Button from "../../Components/Button";
import BackButton from "../../Components/BackButton"; import BackButton from "../../Components/BackButton";
import UserProjectListAdmin from "../../Components/UserProjectListAdmin";
function AdminViewUserInfo(): JSX.Element { function AdminViewUserInfo(): JSX.Element {
const content = ( const content = (
<> <>
<UserProjectListAdmin /> <h1 className="font-bold text-[30px] mb-[20px]">{useLocation().state}</h1>
<div className="border-4 border-black bg-white flex flex-col items-center h-[65vh] w-[50vw] rounded-3xl content-center overflow-scroll space-y-[10vh] p-[30px]">
<p>Put relevant info on user from database here</p>
</div>
</> </>
); );
@ -23,6 +26,6 @@ function AdminViewUserInfo(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default AdminViewUserInfo; export default AdminViewUserInfo;

View file

@ -1,4 +1,4 @@
import { useState, useEffect } from "react"; import { useState } from "react";
import LoginPage from "./LoginPage"; import LoginPage from "./LoginPage";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@ -6,16 +6,13 @@ import { useNavigate } from "react-router-dom";
function App(): JSX.Element { function App(): JSX.Element {
const navigate = useNavigate(); const navigate = useNavigate();
const [authority, setAuthority] = useState(0); const [authority, setAuthority] = useState(0);
if (authority === 1) {
useEffect(() => { navigate("/admin");
if (authority === 1) { } else if (authority === 2) {
navigate("/admin"); navigate("/pm");
} else if (authority === 2) { } else if (authority === 3) {
navigate("/pm"); navigate("/user");
} else if (authority === 3) { }
navigate("/user");
}
}, [authority, navigate]);
return <LoginPage setAuthority={setAuthority} />; return <LoginPage setAuthority={setAuthority} />;
} }

View file

@ -18,6 +18,6 @@ function ChangeRole(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default ChangeRole; export default ChangeRole;

View file

@ -10,6 +10,6 @@ function PMOtherUsersTR(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default PMOtherUsersTR; export default PMOtherUsersTR;

View file

@ -30,6 +30,6 @@ function PMProjectMembers(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default PMProjectMembers; export default PMProjectMembers;

View file

@ -31,6 +31,6 @@ function PMProjectPage(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={undefined} />; return <BasicWindow username="Admin" content={content} buttons={undefined} />;
} }
export default PMProjectPage; export default PMProjectPage;

View file

@ -1,6 +1,6 @@
import BackButton from "../../Components/BackButton";
import BasicWindow from "../../Components/BasicWindow"; import BasicWindow from "../../Components/BasicWindow";
import TimeReport from "../../Components/NewWeeklyReport"; import TimeReport from "../../Components/TimeReport";
import BackButton from "../../Components/BackButton";
function PMTotalTimeActivity(): JSX.Element { function PMTotalTimeActivity(): JSX.Element {
const content = ( const content = (
@ -18,6 +18,6 @@ function PMTotalTimeActivity(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default PMTotalTimeActivity; export default PMTotalTimeActivity;

View file

@ -10,6 +10,6 @@ function PMTotalTimeRole(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default PMTotalTimeRole; export default PMTotalTimeRole;

View file

@ -10,6 +10,6 @@ function PMUnsignedReports(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default PMUnsignedReports; export default PMUnsignedReports;

View file

@ -1,7 +1,7 @@
import BackButton from "../../Components/BackButton";
import BasicWindow from "../../Components/BasicWindow"; import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button"; import Button from "../../Components/Button";
import TimeReport from "../../Components/NewWeeklyReport"; import TimeReport from "../../Components/TimeReport";
import BackButton from "../../Components/BackButton";
function PMViewUnsignedReport(): JSX.Element { function PMViewUnsignedReport(): JSX.Element {
const content = ( const content = (
@ -33,6 +33,6 @@ function PMViewUnsignedReport(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default PMViewUnsignedReport; export default PMViewUnsignedReport;

View file

@ -1,21 +1,29 @@
import BasicWindow from "../../Components/BasicWindow"; import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
import NewTimeReport from "../../Components/TimeReport";
import BackButton from "../../Components/BackButton"; import BackButton from "../../Components/BackButton";
import EditWeeklyReport from "../../Components/EditWeeklyReport";
function UserEditTimeReportPage(): JSX.Element { function UserEditTimeReportPage(): JSX.Element {
const content = ( const content = (
<> <>
<h1 className="font-bold text-[30px] mb-[20px]">Edit Time Report</h1> <h1 className="font-bold text-[30px] mb-[20px]">Edit Time Report</h1>
<EditWeeklyReport /> <NewTimeReport />
</> </>
); );
const buttons = ( const buttons = (
<> <>
<Button
text="Save"
onClick={(): void => {
return;
}}
type="button"
/>
<BackButton /> <BackButton />
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default UserEditTimeReportPage; export default UserEditTimeReportPage;

View file

@ -1,13 +1,13 @@
import BasicWindow from "../../Components/BasicWindow"; import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button"; import Button from "../../Components/Button";
import NewWeeklyReport from "../../Components/NewWeeklyReport"; import NewTimeReport from "../../Components/TimeReport";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
function UserNewTimeReportPage(): JSX.Element { function UserNewTimeReportPage(): JSX.Element {
const content = ( const content = (
<> <>
<h1 className="font-bold text-[30px] mb-[20px]">New Time Report</h1> <h1 className="font-bold text-[30px] mb-[20px]">New Time Report</h1>
<NewWeeklyReport /> <NewTimeReport />
</> </>
); );
@ -25,6 +25,6 @@ function UserNewTimeReportPage(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default UserNewTimeReportPage; export default UserNewTimeReportPage;

View file

@ -27,6 +27,6 @@ function UserProjectPage(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default UserProjectPage; export default UserProjectPage;

View file

@ -15,6 +15,6 @@ function UserViewTimeReportsPage(): JSX.Element {
</> </>
); );
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default UserViewTimeReportsPage; export default UserViewTimeReportsPage;

View file

@ -1,59 +1,31 @@
import { 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"; import BasicWindow from "../Components/BasicWindow";
import { ProjectListUser } from "../Components/ProjectListUser";
import { Project } from "../Types/Project";
export const ProjectNameContext = createContext(""); function YourProjectsPage(): JSX.Element {
//TODO: Change so that it reads projects from database
function UserProjectPage(): JSX.Element { const projects: Project[] = [];
const [projects, setProjects] = useState<Project[]>([]); for (let i = 1; i <= 20; i++) {
const [selectedProject, setSelectedProject] = useState(""); projects.push({
id: i,
const getProjects = async (): Promise<void> => { name: "Example Project " + i,
const username = localStorage.getItem("username") ?? ""; // replace with actual username description: "good",
const token = localStorage.getItem("accessToken") ?? ""; // replace with actual token created: "now",
const response = await api.getUserProjects(username, token); owner: "me",
console.log(response); });
if (response.success) { }
setProjects(response.data ?? []);
} else {
console.error(response.message);
}
};
// Call getProjects when the component mounts
useEffect(() => {
void getProjects();
}, []);
const handleProjectClick = (projectName: string): void => {
setSelectedProject(projectName);
};
const content = ( const content = (
<ProjectNameContext.Provider value={selectedProject}> <>
<h1 className="font-bold text-[30px] mb-[20px]">Your Projects</h1> <h1 className="font-bold text-[30px] mb-[20px]">Your Projects</h1>
<div className="border-4 border-black bg-white flex flex-col items-center justify-center min-h-[65vh] h-fit w-[50vw] rounded-3xl content-center overflow-scroll space-y-[10vh] p-[30px]"> <div className="border-4 border-black bg-white flex flex-col items-center h-[65vh] w-[50vw] rounded-3xl content-center overflow-scroll space-y-[10vh] p-[30px]">
{projects.map((project, index) => ( <ProjectListUser projects={projects} />
<Link
to={`/project/${project.id}`}
onClick={() => {
handleProjectClick(project.name);
}}
key={index}
>
<h1 className="font-bold underline text-[30px] cursor-pointer">
{project.name}
</h1>
</Link>
))}
</div> </div>
</ProjectNameContext.Provider> </>
); );
const buttons = <></>; const buttons = <></>;
return <BasicWindow content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default YourProjectsPage;
export default UserProjectPage;

View file

@ -3,32 +3,13 @@ import ReactDOM from "react-dom/client";
import "./index.css"; import "./index.css";
import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { createBrowserRouter, RouterProvider } from "react-router-dom";
import App from "./Pages/App"; import App from "./Pages/App";
import YourProjectsPage from "./Pages/YourProjectsPage.tsx"; import AdminMenuPage from "./Pages/AdminPages/AdminMenuPage";
import UserProjectPage from "./Pages/UserPages/UserProjectPage.tsx"; import YourProjectsPage from "./Pages/YourProjectsPage";
import AdminMenuPage from "./Pages/AdminPages/AdminMenuPage.tsx"; import AdminAddProject from "./Pages/AdminPages/AdminAddProject";
import UserEditTimeReportPage from "./Pages/UserPages/UserEditTimeReportPage.tsx"; import AdminManageProjects from "./Pages/AdminPages/AdminManageProjects";
import UserNewTimeReportPage from "./Pages/UserPages/UserNewTimeReportPage.tsx"; import AdminManageUsers from "./Pages/AdminPages/AdminManageUsers";
import UserViewTimeReportsPage from "./Pages/UserPages/UserViewTimeReportsPage.tsx"; import AdminAddUser from "./Pages/AdminPages/AdminAddUser";
import PMChangeRole from "./Pages/ProjectManagerPages/PMChangeRole.tsx"; import AdminViewUserInfo from "./Pages/AdminPages/AdminViewUserInfo";
import PMOtherUsersTR from "./Pages/ProjectManagerPages/PMOtherUsersTR.tsx";
import PMProjectMembers from "./Pages/ProjectManagerPages/PMProjectMembers.tsx";
import PMProjectPage from "./Pages/ProjectManagerPages/PMProjectPage.tsx";
import PMTotalTimeActivity from "./Pages/ProjectManagerPages/PMTotalTimeActivity.tsx";
import PMTotalTimeRole from "./Pages/ProjectManagerPages/PMTotalTimeRole.tsx";
import PMUnsignedReports from "./Pages/ProjectManagerPages/PMUnsignedReports.tsx";
import PMViewUnsignedReport from "./Pages/ProjectManagerPages/PMViewUnsignedReport.tsx";
import AdminManageUsers from "./Pages/AdminPages/AdminManageUsers.tsx";
import AdminViewUserInfo from "./Pages/AdminPages/AdminViewUserInfo.tsx";
import AdminManageProjects from "./Pages/AdminPages/AdminManageProjects.tsx";
import AdminAddProject from "./Pages/AdminPages/AdminAddProject.tsx";
import AdminAddUser from "./Pages/AdminPages/AdminAddUser.tsx";
import AdminChangeUsername from "./Pages/AdminPages/AdminChangeUsername.tsx";
import AdminProjectAddMember from "./Pages/AdminPages/AdminProjectAddMember.tsx";
import AdminProjectChangeUserRole from "./Pages/AdminPages/AdminProjectChangeUserRole.tsx";
import AdminProjectManageMembers from "./Pages/AdminPages/AdminProjectManageMembers.tsx";
import AdminProjectStatistics from "./Pages/AdminPages/AdminProjectStatistics.tsx";
import AdminProjectViewMemberInfo from "./Pages/AdminPages/AdminProjectViewMemberInfo.tsx";
import AdminProjectPage from "./Pages/AdminPages/AdminProjectPage.tsx";
// This is where the routes are mounted // This is where the routes are mounted
const router = createBrowserRouter([ const router = createBrowserRouter([
@ -48,86 +29,6 @@ const router = createBrowserRouter([
path: "/user", path: "/user",
element: <YourProjectsPage />, element: <YourProjectsPage />,
}, },
{
path: "/yourProjects",
element: <YourProjectsPage />,
},
{
path: "/editTimeReport",
element: <UserEditTimeReportPage />,
},
{
path: "/newTimeReport",
element: <UserNewTimeReportPage />,
},
{
path: "/project",
element: <UserProjectPage />,
},
{
path: "/projectPage",
element: <UserViewTimeReportsPage />,
},
{
path: "/changeRole",
element: <PMChangeRole />,
},
{
path: "/otherUsersTimeReports",
element: <PMOtherUsersTR />,
},
{
path: "/projectMembers",
element: <PMProjectMembers />,
},
{
path: "/PMProjectPage",
element: <PMProjectPage />,
},
{
path: "/PMTimeActivity",
element: <PMTotalTimeActivity />,
},
{
path: "/PMTimeRole",
element: <PMTotalTimeRole />,
},
{
path: "/PMUnsignedReports",
element: <PMUnsignedReports />,
},
{
path: "/PMViewUnsignedReport",
element: <PMViewUnsignedReport />,
},
{
path: "/adminChangeUsername",
element: <AdminChangeUsername />,
},
{
path: "/adminProjectAddMember",
element: <AdminProjectAddMember />,
},
{
path: "/adminProjectChangeUserRole",
element: <AdminProjectChangeUserRole />,
},
{
path: "/adminProjectManageMembers",
element: <AdminProjectManageMembers />,
},
{
path: "/adminProjectPage",
element: <AdminProjectPage />,
},
{
path: "/adminProjectStatistics",
element: <AdminProjectStatistics />,
},
{
path: "/adminProjectViewMembers",
element: <AdminProjectViewMemberInfo />,
},
{ {
path: "/addProject", path: "/addProject",
element: <AdminAddProject />, element: <AdminAddProject />,

View file

@ -1,28 +1,15 @@
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=

View file

@ -22,11 +22,6 @@ loginPath = base_url + "/api/login"
addProjectPath = base_url + "/api/project" addProjectPath = base_url + "/api/project"
submitReportPath = base_url + "/api/submitReport" submitReportPath = base_url + "/api/submitReport"
getWeeklyReportPath = base_url + "/api/getWeeklyReport" getWeeklyReportPath = base_url + "/api/getWeeklyReport"
getProjectPath = base_url + "/api/project"
signReportPath = base_url + "/api/signReport"
addUserToProjectPath = base_url + "/api/addUserToProject"
promoteToAdminPath = base_url + "/api/promoteToAdmin"
getUserProjectsPath = base_url + "/api/getUserProjects"
# Posts the username and password to the register endpoint # Posts the username and password to the register endpoint
@ -48,20 +43,20 @@ def login(username: string, password: string):
print(response.text) print(response.text)
return response return response
# Test function to login
def test_login(): def test_login():
response = login(username, "always_same") response = login(username, "always_same")
assert response.status_code == 200, "Login failed" assert response.status_code == 200, "Login failed"
print("Login successful") print("Login successful")
return response.json()["token"] return response.json()["token"]
# Test function to create a new user
def test_create_user(): def test_create_user():
response = register(username, "always_same") response = register(username, "always_same")
assert response.status_code == 200, "Registration failed" assert response.status_code == 200, "Registration failed"
print("Registration successful") print("Registration successful")
# Test function to add a project
def test_add_project(): def test_add_project():
loginResponse = login(username, "always_same") loginResponse = login(username, "always_same")
token = loginResponse.json()["token"] token = loginResponse.json()["token"]
@ -74,7 +69,7 @@ def test_add_project():
assert response.status_code == 200, "Add project failed" assert response.status_code == 200, "Add project failed"
print("Add project successful") print("Add project successful")
# Test function to submit a report
def test_submit_report(): def test_submit_report():
token = login(username, "always_same").json()["token"] token = login(username, "always_same").json()["token"]
response = requests.post( response = requests.post(
@ -95,7 +90,6 @@ def test_submit_report():
assert response.status_code == 200, "Submit report failed" assert response.status_code == 200, "Submit report failed"
print("Submit report successful") print("Submit report successful")
# Test function to get a weekly report
def test_get_weekly_report(): def test_get_weekly_report():
token = login(username, "always_same").json()["token"] token = login(username, "always_same").json()["token"]
response = requests.get( response = requests.get(
@ -104,135 +98,6 @@ def test_get_weekly_report():
params={"username": username, "projectName": projectName , "week": 1} params={"username": username, "projectName": projectName , "week": 1}
) )
print(response.text) print(response.text)
assert response.status_code == 200, "Get weekly report failed"
# Tests getting a project by id
def test_get_project():
token = login(username, "always_same").json()["token"]
response = requests.get(
getProjectPath + "/1", # Assumes that the project with id 1 exists
headers={"Authorization": "Bearer " + token},
)
print(response.text)
assert response.status_code == 200, "Get project failed"
# Test function to add a user to a project
def test_add_user_to_project():
# Log in as a site admin
admin_username = randomString()
admin_password = "admin_password"
print("Registering with username: ", admin_username, " and password: ", admin_password)
response = requests.post(
registerPath, json={"username": admin_username, "password": admin_password}
)
print(response.text)
admin_token = login(admin_username, admin_password).json()["token"]
response = requests.post(promoteToAdminPath, json={"username": admin_username}, headers={"Authorization": "Bearer " + admin_token})
print(response.text)
assert response.status_code == 200, "Promote to site admin failed"
print("Admin promoted to site admin successfully")
# Create a new user to add to the project
new_user = randomString()
register(new_user, "new_user_password")
# Add the new user to the project as a member
response = requests.put(
addUserToProjectPath,
json={"projectName": projectName, "username": new_user, "role": "member"},
headers={"Authorization": "Bearer " + admin_token},
)
print(response.text)
assert response.status_code == 200, "Add user to project failed"
print("Add user to project successful")
# Check if the user is added to the project
response = requests.get(
getUserProjectsPath,
json={"username": new_user},
headers={"Authorization": "Bearer " + admin_token},
)
print(response.text)
assert response.status_code == 200, "Get user projects failed"
print("got user projects successfully")
# Test function to sign a report
def test_sign_report():
# Create a project manager user
project_manager = randomString()
register(project_manager, "project_manager_password")
# Register an admin
admin_username = randomString()
admin_password = "admin_password2"
print("Registering with username: ", admin_username, " and password: ", admin_password)
response = requests.post(
registerPath, json={"username": admin_username, "password": admin_password}
)
print(response.text)
# Log in as the admin
admin_token = login(admin_username, admin_password).json()["token"]
response = requests.post(promoteToAdminPath, json={"username": admin_username}, headers={"Authorization": "Bearer " + admin_token})
response = requests.put(
addUserToProjectPath,
json={"projectName": projectName, "username": project_manager, "role": "project_manager"},
headers={"Authorization": "Bearer " + admin_token},
)
assert response.status_code == 200, "Add project manager to project failed"
print("Project manager added to project successfully")
# Log in as the project manager
project_manager_token = login(project_manager, "project_manager_password").json()["token"]
# Submit a report for the project
token = login(username, "always_same").json()["token"]
response = requests.post(
submitReportPath,
json={
"projectName": projectName,
"week": 1,
"developmentTime": 10,
"meetingTime": 5,
"adminTime": 5,
"ownWorkTime": 10,
"studyTime": 10,
"testingTime": 10,
},
headers={"Authorization": "Bearer " + token},
)
assert response.status_code == 200, "Submit report failed"
print("Submit report successful")
# Retrieve the report ID
response = requests.get(
getWeeklyReportPath,
headers={"Authorization": "Bearer " + token},
params={"username": username, "projectName": projectName , "week": 1}
)
print(response.text)
report_id = response.json()["reportId"]
# Sign the report as the project manager
response = requests.post(
signReportPath,
json={"reportId": report_id},
headers={"Authorization": "Bearer " + project_manager_token},
)
assert response.status_code == 200, "Sign report failed"
print("Sign report successful")
# Retrieve the report ID again for confirmation
response = requests.get(
getWeeklyReportPath,
headers={"Authorization": "Bearer " + token},
params={"username": username, "projectName": projectName , "week": 1}
)
print(response.text)
if __name__ == "__main__": if __name__ == "__main__":
test_create_user() test_create_user()
@ -240,6 +105,3 @@ if __name__ == "__main__":
test_add_project() test_add_project()
test_submit_report() test_submit_report()
test_get_weekly_report() test_get_weekly_report()
test_get_project()
test_sign_report()
test_add_user_to_project()