Compare commits

...

78 commits

Author SHA1 Message Date
al8763be
805d05f8a5 Merge branch 'gruppPP' into BumBranch 2024-03-18 17:31:03 +01:00
al8763be
e5904253e3 Merge branch 'gruppdm' into BumBranch 2024-03-18 17:28:57 +01:00
Peter KW
d692165f99 Removed username prop from basicwindow in all pages 2024-03-18 17:27:32 +01:00
Peter KW
388a430613 Tiny fix 2024-03-18 17:22:40 +01:00
Peter KW
2493932f77 Removed username prop, no longer used 2024-03-18 17:20:29 +01:00
al8763be
fbf46b7cd0 Merge branch 'frontend' into BumBranch 2024-03-18 17:14:23 +01:00
pavel Hamawand
b93ef48500 minor fix 2024-03-18 17:07:48 +01:00
pavel Hamawand
0f7f866cde minor fix 2024-03-18 17:06:05 +01:00
pavel Hamawand
3ec0d168eb minor fix 2024-03-18 17:05:37 +01:00
pavel Hamawand
e47b251c14 implement component 2024-03-18 17:03:02 +01:00
pavel Hamawand
39983c7f6f Render Project List 2024-03-18 17:01:11 +01:00
pavel Hamawand
f61ef87d5e Fetch Projects from API - needs fixing 2024-03-18 16:58:46 +01:00
pavel Hamawand
90afe80408 Implement State management 2024-03-18 16:54:16 +01:00
pavel Hamawand
4173003d32 initial component Setup 2024-03-18 16:53:13 +01:00
pavel Hamawand
b8f669e454 new component UserProjectListAdmin 2024-03-18 16:52:40 +01:00
pavel Hamawand
22b9580f51 fix backbutton 2024-03-18 16:52:40 +01:00
pavel Hamawand
8291f4caf3 delete component 2024-03-18 16:52:40 +01:00
pavel Hamawand
576a137038 new component 2024-03-18 16:52:40 +01:00
Peter KW
ace11570a5 Merge branch 'gruppDM' into gruppPP 2024-03-18 16:49:02 +01:00
Peter KW
2bd9878359 Merge branch 'frontend' into gruppPP 2024-03-18 16:44:13 +01:00
dDogge
9ad89d6063 Handler for SignReport added and corresponding test in testing.pu added 2024-03-18 16:01:51 +01:00
Mattias
437520183b removed import 2024-03-18 15:46:49 +01:00
Mattias
b962a856f4 fixes 2024-03-18 15:46:23 +01:00
Mattias
3bcb7a89b8 edited paths 2024-03-18 15:39:29 +01:00
Mattias
43f13dc534 Old paths in main are back 2024-03-18 15:33:19 +01:00
Mattias
1893340c63 Changed name of new component and minor fix 2024-03-18 15:17:53 +01:00
borean
3a3690e3da ignore go.work.sum 2024-03-18 15:12:11 +01:00
dDogge
4979378779 Handler for AddUserToProject and promoteToAdmin, successfully tested 2024-03-18 14:47:15 +01:00
Mattias
3106e0f445 Fixes for viewing-report-component 2024-03-18 14:46:32 +01:00
al8763be
d6ce4a3c57 errMessage fixed 2024-03-18 14:43:28 +01:00
Mattias
b82f73d192 Removed button 2024-03-18 14:42:21 +01:00
Mattias
0634f83689 Now displaying the right component 2024-03-18 14:40:32 +01:00
Mattias
69d4067209 Added new component for viewing weeklyreport 2024-03-18 14:40:20 +01:00
pavel Hamawand
409d973d8a minor fix 2024-03-18 14:35:56 +01:00
dDogge
76fefd2b24 Added function to check if someone is admin 2024-03-18 13:32:55 +01:00
Davenludd
3f8d56963b Add ProjectNameContext to NewWeeklyReport and handle project selection in YourProjectsPage 2024-03-18 12:49:58 +01:00
Davenludd
164ff781b3 Add useEffect hook to handle authority navigation and log response in YourProjectsPage 2024-03-18 10:44:15 +01:00
Davenludd
f6b2d17b97 Update Header component to use localStorage for username 2024-03-18 10:44:15 +01:00
Mattias
7a6b875aeb Token is now fetched from local storage 2024-03-18 10:28:20 +01:00
Davenludd
68b25350ef Add username to local storage and retrieve it in YourProjectsPage 2024-03-18 10:22:38 +01:00
Davenludd
3c8b7f9da2 Merge branch 'gruppPP' into gruppDM 2024-03-18 10:10:40 +01:00
Davenludd
5bef01396b Add user-specific project list and API integration 2024-03-18 10:09:12 +01:00
Mattias
711f46f960 fixed import 2024-03-18 09:56:07 +01:00
Davenludd
ba58fea1e3 Merge branch 'frontend' into gruppDM 2024-03-18 09:01:05 +01:00
Peter KW
92119dd49e Added path + fixed import 2024-03-18 01:56:04 +01:00
Peter KW
8a2152395f Small fixes 2024-03-18 01:31:58 +01:00
Peter KW
516784c6bb Merge branch 'frontend' into gruppPP 2024-03-18 00:51:34 +01:00
Peter KW
4876038613 Merge remote-tracking branch 'origin/frontend' into gruppPP 2024-03-18 00:48:21 +01:00
Peter KW
f7b8ea7d97 Path fix 2024-03-18 00:46:04 +01:00
Peter KW
bd588048dd Import fix 2024-03-18 00:45:49 +01:00
Peter KW
070e9cc1e5 Link to button 2024-03-18 00:45:22 +01:00
Peter KW
844e94ed26 Small fixes to layout and added 2024-03-18 00:44:56 +01:00
Peter KW
0044b61ac8 Uses input field component instead now 2024-03-18 00:44:11 +01:00
Peter KW
3b49faec2d Import fix 2024-03-18 00:42:22 +01:00
Peter KW
8249678488 Using local storage for token 2024-03-18 00:42:05 +01:00
Peter KW
24e7e68ea0 Logout functionality 2024-03-18 00:41:46 +01:00
Peter KW
200da51633 AddProject component 2024-03-18 00:41:18 +01:00
Peter KW
d97516e0cd Added some paths again 2024-03-18 00:40:42 +01:00
al8763be
953c06212d API Changes 2024-03-18 00:22:32 +01:00
al8763be
7cc74866fc Merge branch 'dev' into frontend 2024-03-18 00:13:04 +01:00
al8763be
b45a20c9f5 Merge branch 'dev' into frontend 2024-03-18 00:06:20 +01:00
Imbus
741ad50ccf Merge branch 'dev' of github.com:imbus64/TTime into dev 2024-03-17 23:51:52 +01:00
dDogge
f6e4603603 Added handler for SignWeeklyReport named SignReport in handlers_report_related 2024-03-17 23:31:52 +01:00
Davenludd
ead1482e50 Merge branch 'frontend' into gruppDM 2024-03-17 21:39:19 +01:00
Davenludd
07aaab5530 Merge branch 'frontend' into gruppDM 2024-03-17 21:22:10 +01:00
Peter KW
888256e9f6 Changed logincheck type and error clarification 2024-03-17 19:38:51 +01:00
Mattias
17a081e816 minor fixes 2024-03-17 16:55:51 +01:00
Mattias
81893ae3e8 Minor fixes 2024-03-17 16:24:09 +01:00
Davenludd
9dd47f3d71 Update import paths for Types 2024-03-17 16:23:37 +01:00
Davenludd
2ba10e837a Refactor API.ts to handle error response in submitWeeklyReport 2024-03-17 16:14:50 +01:00
Davenludd
830c234325 Update login response type in API.ts 2024-03-17 15:50:50 +01:00
Davenludd
c708ec47ca Merge branch 'frontend' into gruppDM 2024-03-17 15:36:41 +01:00
Davenludd
b1f57e5ed8 Merge branch 'frontend' into gruppDM 2024-03-17 15:28:20 +01:00
Davenludd
6d5c25be5c Refactor Register component to use InputField component 2024-03-17 14:49:42 +01:00
Mattias
d6d2b6d170 Visual fixes 2024-03-17 14:49:42 +01:00
Davenludd
46eebee84f Refactor API.ts for improved readability and maintainability 2024-03-17 14:21:38 +01:00
Mattias
f3931f905a Minor changes 2024-03-17 14:19:57 +01:00
Mattias
62f1926305 Corrected NewWeeklyReport (prev. TimeReports) and changed imports 2024-03-17 13:39:16 +01:00
48 changed files with 1003 additions and 247 deletions

1
.gitignore vendored
View file

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

View file

@ -32,6 +32,7 @@ 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
@ -313,6 +314,26 @@ 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,3 +536,46 @@ func TestSignWeeklyReportByAnotherProjectManager(t *testing.T) {
t.Error("Expected SignWeeklyReport to fail with a project manager who is not in the project, but it didn't") 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")
}
}

View file

@ -16,6 +16,9 @@ type GlobalState interface {
GetUserProjects(c *fiber.Ctx) error // To get all projects GetUserProjects(c *fiber.Ctx) error // To get all projects
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
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

@ -96,3 +96,45 @@ 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

@ -63,3 +63,43 @@ func (gs *GState) GetWeeklyReport(c *fiber.Ctx) error {
// Return the retrieved weekly report // Return the retrieved weekly report
return c.JSON(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")
}

View file

@ -1,6 +1,7 @@
package handlers package handlers
import ( import (
"fmt"
"time" "time"
"ttime/internal/types" "ttime/internal/types"
@ -122,3 +123,25 @@ 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

@ -79,6 +79,9 @@ func main() {
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/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,11 +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[]>>;
} }
// Export an instance of the API // Export an instance of the API

View file

@ -0,0 +1,94 @@
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<Project>) => {
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 (
<div className="flex flex-col h-fit w-screen items-center justify-center">
<div className="border-4 border-black bg-white flex flex-col items-center justify-center h-fit w-fit rounded-3xl content-center pl-20 pr-20">
<form
className="bg-white rounded px-8 pt-6 pb-8 mb-4 items-center justify-center flex flex-col w-fit h-fit"
onSubmit={(e) => {
e.preventDefault();
CreateProject({ name: name, description: description });
}}
>
<img
src={Logo}
className="logo w-[7vw] mb-10 mt-10"
alt="TTIME Logo"
/>
<h3 className="pb-4 mb-2 text-center font-bold text-[18px]">
Create a new project
</h3>
<InputField
label="Name"
type="text"
value={name}
onChange={(e) => {
setName(e.target.value);
}}
/>
<InputField
label="Description"
type="text"
value={description}
onChange={(e) => {
setDescription(e.target.value);
}}
/>
<div className="flex items-center justify-between">
<Button
text="Create"
onClick={(): void => {
return;
}}
type="submit"
/>
</div>
</form>
<p className="text-center text-gray-500 text-xs"></p>
</div>
</div>
);
}
export default AddProject;

View file

@ -2,17 +2,15 @@ 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 username={username} /> <Header />
<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

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

View file

@ -10,17 +10,22 @@ function LoginCheck(props: {
username: string; username: string;
password: string; password: string;
setAuthority: Dispatch<SetStateAction<number>>; setAuthority: Dispatch<SetStateAction<number>>;
}): number { }): void {
const user: NewUser = { const user: NewUser = {
username: props.username, username: props.username,
password: props.password, password: props.password,
}; };
localStorage.clear();
api api
.login(user) .login(user)
.then((response: APIResponse<string>) => { .then((response: APIResponse<string>) => {
if (response.success) { if (response.success) {
if (response.data !== undefined) { if (response.data !== undefined) {
const token = response.data; 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 //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) => {
@ -42,14 +47,12 @@ function LoginCheck(props: {
console.error("Token was undefined"); console.error("Token was undefined");
} }
} else { } else {
console.error("Token could not be fetched"); console.error("Token could not be fetched/No such user");
} }
}) })
.catch((error) => { .catch((error) => {
console.error("An error occurred during login:", error); console.error("An error occurred during login:", error);
}); });
return 0;
} }
export default LoginCheck; export default LoginCheck;

View file

@ -1,50 +1,51 @@
import { useState } from "react"; import { useState, useContext } from "react";
import { 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 { NewWeeklyReport } from "../Types/goTypes"; import { ProjectNameContext } from "../Pages/YourProjectsPage";
export default function NewTimeReport(): JSX.Element { export default function NewWeeklyReport(): JSX.Element {
const [projectName, setProjectName] = useState<string>("projectName"); // TODO: Get from backend const [week, setWeek] = useState(0);
const [week, setWeek] = useState<number>(NaN); const [developmentTime, setDevelopmentTime] = useState(0);
const [development, setDevelopment] = useState<number>(NaN); const [meetingTime, setMeetingTime] = useState(0);
const [meeting, setMeeting] = useState<number>(NaN); const [adminTime, setAdminTime] = useState(0);
const [administration, setAdministration] = useState<number>(NaN); const [ownWorkTime, setOwnWorkTime] = useState(0);
const [ownwork, setOwnWork] = useState<number>(NaN); const [studyTime, setStudyTime] = useState(0);
const [studies, setStudies] = useState<number>(NaN); const [testingTime, setTestingTime] = useState(0);
const [testing, setTesting] = useState<number>(NaN);
const handleNewTimeReport = async (): Promise<void> => { const projectName = useContext(ProjectNameContext);
const newTimeReport: NewWeeklyReport = { const token = localStorage.getItem("accessToken") ?? "";
const handleNewWeeklyReport = async (): Promise<void> => {
const newWeeklyReport: NewWeeklyReport = {
projectName, projectName,
week, week,
developmentTime: development, developmentTime,
meetingTime: meeting, meetingTime,
adminTime: administration, adminTime,
ownWorkTime: ownwork, ownWorkTime,
studyTime: studies, studyTime,
testingTime: testing, testingTime,
}; };
await Promise.resolve();
await api.submitWeeklyReport(newTimeReport, "token"); await api.submitWeeklyReport(newWeeklyReport, 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) { if (week === 0) {
alert("Please enter a week number"); alert("Please enter a week number");
e.preventDefault(); e.preventDefault();
return; return;
} }
e.preventDefault(); e.preventDefault();
void handleNewTimeReport(); void handleNewWeeklyReport();
navigate("/project"); navigate("/project");
}} }}
> >
@ -83,9 +84,9 @@ export default function NewTimeReport(): 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={development} value={developmentTime}
onChange={(e) => { onChange={(e) => {
setDevelopment(parseInt(e.target.value)); setDevelopmentTime(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;
@ -102,9 +103,9 @@ export default function NewTimeReport(): 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={meeting} value={meetingTime}
onChange={(e) => { onChange={(e) => {
setMeeting(parseInt(e.target.value)); setMeetingTime(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;
@ -121,9 +122,9 @@ export default function NewTimeReport(): 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={administration} value={adminTime}
onChange={(e) => { onChange={(e) => {
setAdministration(parseInt(e.target.value)); setAdminTime(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;
@ -140,9 +141,9 @@ export default function NewTimeReport(): 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={ownwork} value={ownWorkTime}
onChange={(e) => { onChange={(e) => {
setOwnWork(parseInt(e.target.value)); setOwnWorkTime(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;
@ -159,9 +160,9 @@ export default function NewTimeReport(): 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={studies} value={studyTime}
onChange={(e) => { onChange={(e) => {
setStudies(parseInt(e.target.value)); setStudyTime(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;
@ -178,9 +179,9 @@ export default function NewTimeReport(): 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={testing} value={testingTime}
onChange={(e) => { onChange={(e) => {
setTesting(parseInt(e.target.value)); setTestingTime(parseInt(e.target.value));
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
const keyValue = event.key; const keyValue = event.key;

View file

@ -3,34 +3,9 @@ import { NewUser } from "../Types/goTypes";
import { api } from "../API/API"; import { api } from "../API/API";
import Logo from "../assets/Logo.svg"; import Logo from "../assets/Logo.svg";
import Button from "./Button"; import Button from "./Button";
import InputField from "./InputField";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
function InputField(props: {
label: string;
type: string;
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}): JSX.Element {
return (
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-sans font-bold mb-2"
htmlFor={props.label}
>
{props.label}
</label>
<input
className="appearance-none border-2 border-black rounded-2xl w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id={props.label}
type={props.type}
placeholder={props.label}
value={props.value}
onChange={props.onChange}
/>
</div>
);
}
export default function Register(): JSX.Element { export default function Register(): JSX.Element {
const [username, setUsername] = useState<string>(); const [username, setUsername] = useState<string>();
const [password, setPassword] = useState<string>(); const [password, setPassword] = useState<string>();
@ -48,6 +23,7 @@ export default function Register(): JSX.Element {
nav("/"); // Instantly navigate to the login page nav("/"); // Instantly navigate to the login page
} else { } else {
setErrMessage(response.message ?? "Unknown error"); setErrMessage(response.message ?? "Unknown error");
console.error(errMessage);
} }
}; };
@ -85,43 +61,6 @@ export default function Register(): JSX.Element {
setPassword(e.target.value); setPassword(e.target.value);
}} }}
/> />
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-sans font-bold mb-2"
htmlFor="username"
>
Username
</label>
<input
className="appearance-none border-2 border-black rounded-2xl w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="username"
type="text"
placeholder="Username"
value={username}
onChange={(e) => {
setUsername(e.target.value);
}}
/>
</div>
<div className="mb-6">
<label
className="block text-gray-700 text-sm font-sans font-bold mb-2"
htmlFor="password"
>
Password
</label>
<input
className="appearance-none border-2 border-black rounded-2xl w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="password"
type="password"
placeholder="Choose your password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
}}
/>
</div>
{errMessage && <p className="text-red-500 text-xs">{errMessage}</p>}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Button <Button
text="Register" text="Register"

View file

@ -1,11 +1,11 @@
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { User } from "../Types/goTypes"; import { PublicUser } from "../Types/goTypes";
/** /**
* The props for the UserProps component * The props for the UserProps component
*/ */
interface UserProps { interface UserProps {
users: User[]; users: PublicUser[];
} }
/** /**
@ -23,7 +23,7 @@ export function UserListAdmin(props: UserProps): JSX.Element {
<div> <div>
<ul className="font-bold underline text-[30px] cursor-pointer padding"> <ul className="font-bold underline text-[30px] cursor-pointer padding">
{props.users.map((user) => ( {props.users.map((user) => (
<Link to="/admin-view-user" key={user.userId} state={user.username}> <Link to="/adminUserInfo" key={user.userId} state={user.username}>
<li className="pt-5" key={user.userId}> <li className="pt-5" key={user.userId}>
{user.username} {user.username}
</li> </li>

View file

@ -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<Project[]>([]);
useEffect(() => {
const fetchProjects = async (): Promise<void> => {
try {
const token = localStorage.getItem("accessToken") ?? "";
const username = 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

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

View file

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

View file

@ -1,3 +1,5 @@
import { Link } from "react-router-dom";
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";
@ -6,23 +8,19 @@ function AdminManageProjects(): JSX.Element {
const buttons = ( const buttons = (
<> <>
<Button <Link to="/addProject">
text="Add Project" <Button
onClick={(): void => { text="Add Project"
return; onClick={(): void => {
}} return;
type="button" }}
/> type="button"
<Button />
text="Back" </Link>
onClick={(): void => { <BackButton />
return;
}}
type="button"
/>
</> </>
); );
return <BasicWindow username="Admin" content={content} buttons={buttons} />; return <BasicWindow content={content} buttons={buttons} />;
} }
export default AdminManageProjects; export default AdminManageProjects;

View file

@ -2,14 +2,14 @@ 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 { UserListAdmin } from "../../Components/UserListAdmin"; import { UserListAdmin } from "../../Components/UserListAdmin";
import { User } from "../../Types/Users"; import { PublicUser } from "../../Types/goTypes";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
function AdminManageUsers(): JSX.Element { function AdminManageUsers(): JSX.Element {
//TODO: Change so that it reads users from database //TODO: Change so that it reads users from database
const users: User[] = []; const users: PublicUser[] = [];
for (let i = 1; i <= 20; i++) { for (let i = 1; i <= 20; i++) {
users.push({ id: i, userName: "Example User " + i }); users.push({ userId: "id" + i, username: "Example User " + i });
} }
const navigate = useNavigate(); const navigate = useNavigate();
@ -28,7 +28,7 @@ function AdminManageUsers(): JSX.Element {
<Button <Button
text="Add User" text="Add User"
onClick={(): void => { onClick={(): void => {
navigate("/admin-add-user"); navigate("/adminAddUser");
}} }}
type="button" type="button"
/> />
@ -36,6 +36,6 @@ function AdminManageUsers(): JSX.Element {
</> </>
); );
return <BasicWindow username="Admin" content={content} buttons={buttons} />; return <BasicWindow content={content} buttons={buttons} />;
} }
export default AdminManageUsers; export default AdminManageUsers;

View file

@ -6,12 +6,12 @@ function AdminMenuPage(): JSX.Element {
<> <>
<h1 className="font-bold text-[30px] mb-[20px]">Administrator Menu</h1> <h1 className="font-bold text-[30px] mb-[20px]">Administrator Menu</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 justify-center min-h-[65vh] h-fit w-[50vw] rounded-3xl content-center overflow-scroll space-y-[10vh] p-[30px]">
<Link to="/admin-manage-users"> <Link to="/adminManageUser">
<h1 className="font-bold underline text-[30px] cursor-pointer"> <h1 className="font-bold underline text-[30px] cursor-pointer">
Manage Users Manage Users
</h1> </h1>
</Link> </Link>
<Link to="/admin-manage-projects"> <Link to="/adminManageProject">
<h1 className="font-bold underline text-[30px] cursor-pointer"> <h1 className="font-bold underline text-[30px] cursor-pointer">
Manage Projects Manage Projects
</h1> </h1>
@ -22,6 +22,6 @@ function AdminMenuPage(): JSX.Element {
const buttons = <></>; const buttons = <></>;
return <BasicWindow username="Admin" content={content} buttons={buttons} />; return <BasicWindow content={content} buttons={buttons} />;
} }
export default AdminMenuPage; export default AdminMenuPage;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,15 +1,12 @@
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 = (
<> <>
<h1 className="font-bold text-[30px] mb-[20px]">{useLocation().state}</h1> <UserProjectListAdmin />
<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>
</> </>
); );
@ -26,6 +23,6 @@ function AdminViewUserInfo(): JSX.Element {
</> </>
); );
return <BasicWindow username="Admin" content={content} buttons={buttons} />; return <BasicWindow content={content} buttons={buttons} />;
} }
export default AdminViewUserInfo; export default AdminViewUserInfo;

View file

@ -1,4 +1,4 @@
import { useState } from "react"; import { useState, useEffect } from "react";
import LoginPage from "./LoginPage"; import LoginPage from "./LoginPage";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@ -6,13 +6,16 @@ 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) {
navigate("/admin"); useEffect(() => {
} else if (authority === 2) { if (authority === 1) {
navigate("/pm"); navigate("/admin");
} else if (authority === 3) { } else if (authority === 2) {
navigate("/user"); navigate("/pm");
} } 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 username="Admin" content={content} buttons={buttons} />; return <BasicWindow content={content} buttons={buttons} />;
} }
export default ChangeRole; export default ChangeRole;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,6 +10,6 @@ function PMUnsignedReports(): JSX.Element {
</> </>
); );
return <BasicWindow username="Admin" content={content} buttons={buttons} />; return <BasicWindow 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/TimeReport"; import TimeReport from "../../Components/NewWeeklyReport";
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 username="Admin" content={content} buttons={buttons} />; return <BasicWindow content={content} buttons={buttons} />;
} }
export default PMViewUnsignedReport; export default PMViewUnsignedReport;

View file

@ -1,29 +1,21 @@
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>
<NewTimeReport /> <EditWeeklyReport />
</> </>
); );
const buttons = ( const buttons = (
<> <>
<Button
text="Save"
onClick={(): void => {
return;
}}
type="button"
/>
<BackButton /> <BackButton />
</> </>
); );
return <BasicWindow username="Admin" content={content} buttons={buttons} />; return <BasicWindow 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 NewTimeReport from "../../Components/TimeReport"; import NewWeeklyReport from "../../Components/NewWeeklyReport";
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>
<NewTimeReport /> <NewWeeklyReport />
</> </>
); );
@ -25,6 +25,6 @@ function UserNewTimeReportPage(): JSX.Element {
</> </>
); );
return <BasicWindow username="Admin" content={content} buttons={buttons} />; return <BasicWindow content={content} buttons={buttons} />;
} }
export default UserNewTimeReportPage; export default UserNewTimeReportPage;

View file

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

View file

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

View file

@ -1,31 +1,59 @@
import React, { useState, createContext, useEffect } from "react";
import { Project } from "../Types/goTypes";
import { api } from "../API/API";
import { Link } from "react-router-dom";
import BasicWindow from "../Components/BasicWindow"; import BasicWindow from "../Components/BasicWindow";
import { ProjectListUser } from "../Components/ProjectListUser";
import { Project } from "../Types/Project";
function YourProjectsPage(): JSX.Element { export const ProjectNameContext = createContext("");
//TODO: Change so that it reads projects from database
const projects: Project[] = []; function UserProjectPage(): JSX.Element {
for (let i = 1; i <= 20; i++) { const [projects, setProjects] = useState<Project[]>([]);
projects.push({ const [selectedProject, setSelectedProject] = useState("");
id: i,
name: "Example Project " + i, const getProjects = async (): Promise<void> => {
description: "good", const username = localStorage.getItem("username") ?? ""; // replace with actual username
created: "now", const token = localStorage.getItem("accessToken") ?? ""; // replace with actual token
owner: "me", const response = await api.getUserProjects(username, token);
}); console.log(response);
} if (response.success) {
setProjects(response.data ?? []);
} else {
console.error(response.message);
}
};
// Call getProjects when the component mounts
useEffect(() => {
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 h-[65vh] 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 justify-center min-h-[65vh] h-fit w-[50vw] rounded-3xl content-center overflow-scroll space-y-[10vh] p-[30px]">
<ProjectListUser projects={projects} /> {projects.map((project, index) => (
<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 username="Admin" content={content} buttons={buttons} />; return <BasicWindow username="Admin" content={content} buttons={buttons} />;
} }
export default YourProjectsPage;
export default UserProjectPage;

View file

@ -3,8 +3,32 @@ 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 AdminMenuPage from "./Pages/AdminPages/AdminMenuPage"; import YourProjectsPage from "./Pages/YourProjectsPage.tsx";
import YourProjectsPage from "./Pages/YourProjectsPage"; import UserProjectPage from "./Pages/UserPages/UserProjectPage.tsx";
import AdminMenuPage from "./Pages/AdminPages/AdminMenuPage.tsx";
import UserEditTimeReportPage from "./Pages/UserPages/UserEditTimeReportPage.tsx";
import UserNewTimeReportPage from "./Pages/UserPages/UserNewTimeReportPage.tsx";
import UserViewTimeReportsPage from "./Pages/UserPages/UserViewTimeReportsPage.tsx";
import PMChangeRole from "./Pages/ProjectManagerPages/PMChangeRole.tsx";
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([
@ -20,6 +44,110 @@ const router = createBrowserRouter([
path: "/pm", path: "/pm",
element: <YourProjectsPage />, element: <YourProjectsPage />,
}, },
{
path: "/user",
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",
element: <AdminAddProject />,
},
{
path: "/adminAddUser",
element: <AdminAddUser />,
},
{
path: "/adminUserInfo",
element: <AdminViewUserInfo />,
},
{
path: "/adminManageProject",
element: <AdminManageProjects />,
},
{
path: "/adminManageUser",
element: <AdminManageUsers />,
},
]); ]);
// Semi-hacky way to get the root element // Semi-hacky way to get the root element

View file

@ -1,15 +1,28 @@
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
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/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
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/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
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=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
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,6 +22,9 @@ 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"
signReportPath = base_url + "/api/signReport"
addUserToProjectPath = base_url + "/api/addUserToProject"
promoteToAdminPath = base_url + "/api/promoteToAdmin"
# Posts the username and password to the register endpoint # Posts the username and password to the register endpoint
@ -43,20 +46,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"]
@ -69,7 +72,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(
@ -90,6 +93,7 @@ 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(
@ -99,9 +103,119 @@ def test_get_weekly_report():
) )
print(response.text) print(response.text)
# 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")
# 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()
test_login() test_login()
test_add_project() test_add_project()
test_submit_report() test_submit_report()
test_get_weekly_report() test_get_weekly_report()
test_sign_report()
test_add_user_to_project()