Compare commits

..

No commits in common. "92119dd49e70810449c61192efdf5d412f76d6a4" and "5696310a687e7d59a5469fe870074034164b42fe" have entirely different histories.

16 changed files with 179 additions and 273 deletions

View file

@ -16,7 +16,6 @@ type GlobalState interface {
GetUserProjects(c *fiber.Ctx) error // To get all projects
SubmitWeeklyReport(c *fiber.Ctx) error
GetWeeklyReport(c *fiber.Ctx) error
SignReport(c *fiber.Ctx) error
// GetProject(c *fiber.Ctx) error // To get a specific project
// UpdateProject(c *fiber.Ctx) error // To update a project
// DeleteProject(c *fiber.Ctx) error // To delete a project

View file

@ -37,16 +37,13 @@ func (gs *GState) SubmitWeeklyReport(c *fiber.Ctx) error {
// Handler for retrieving weekly report
func (gs *GState) GetWeeklyReport(c *fiber.Ctx) error {
// Extract the necessary parameters from the request
println("GetWeeklyReport")
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
username := claims["name"].(string)
// Extract project name and week from query parameters
projectName := c.Query("projectName")
println(projectName)
week := c.Query("week")
println(week)
// Convert week to integer
weekInt, err := strconv.Atoi(week)
@ -63,31 +60,3 @@ func (gs *GState) GetWeeklyReport(c *fiber.Ctx) error {
// Return the retrieved weekly report
return c.JSON(report)
}
func (gs *GState) SignReport(c *fiber.Ctx) error {
// Extract the necessary parameters from the token
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
managerUsername := claims["name"].(string)
// Extract the report ID and project manager ID from request parameters
reportID, err := strconv.Atoi(c.Params("reportId"))
if err != nil {
return c.Status(400).SendString("Invalid report ID")
}
// Call the database function to get the project manager ID
managerID, err := gs.Db.GetUserId(managerUsername)
if err != nil {
return c.Status(500).SendString("Failed to get project manager ID")
}
// Call the database function to sign the weekly report
err = gs.Db.SignWeeklyReport(reportID, managerID)
if err != nil {
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")
}

View file

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

View file

@ -0,0 +1,87 @@
import { describe, expect, test } from "@jest/globals";
import { api } from "../API/API";
import { NewUser, NewWeeklyReport } from "../Types/goTypes";
describe("API", () => {
test("registerUser", async () => {
const user: NewUser = {
username: "lol", // Add the username property
password: "lol",
};
const response = await api.registerUser(user);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty("userId");
});
test("createProject", async () => {
const project = {
name: "Project X",
description: "This is a test project",
};
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t";
const response = await api.createProject(project, token);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty("projectId");
});
test("renewToken", async () => {
const refreshToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t";
const response = await api.renewToken(refreshToken);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty("accessToken");
expect(response.data).toHaveProperty("refreshToken");
});
test("getUserProjects", async () => {
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t";
const username = "rrgumdzpmc";
const response = await api.getUserProjects(username, token);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty("projects");
});
test("submitWeeklyReport", async () => {
const report: NewWeeklyReport = {
projectName: "vtmosxssst",
week: 2,
developmentTime: 40,
meetingTime: 5,
adminTime: 2,
ownWorkTime: 10,
studyTime: 12,
testingTime: 41,
};
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t";
const response = await api.submitWeeklyReport(report, token);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty(
"message",
"Report submitted successfully",
);
});
test("login", async () => {
const user: NewUser = {
username: "rrgumdzpmc", // Add an empty string value for the username property
password: "always_same",
};
const response = await api.login(user);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty("accessToken");
expect(response.data).toHaveProperty("refreshToken");
});
});

View file

@ -20,32 +20,25 @@ interface API {
registerUser(user: NewUser): Promise<APIResponse<User>>;
/** Remove a user */
removeUser(username: string, token: string): Promise<APIResponse<User>>;
/** Login */
login(NewUser: NewUser): Promise<APIResponse<string>>;
/** Renew the token */
renewToken(token: string): Promise<APIResponse<string>>;
/** Create a project */
createProject(
project: NewProject,
token: string,
): Promise<APIResponse<Project>>;
/** Submit a weekly report */
submitWeeklyReport(
project: NewWeeklyReport,
token: string,
): Promise<APIResponse<Project>>;
/** Renew the token */
renewToken(token: string): Promise<APIResponse<string>>;
/** Gets all the projects of a user*/
getUserProjects(
username: string,
token: string,
): Promise<APIResponse<Project[]>>;
/** Submit a weekly report */
submitWeeklyReport(
project: NewWeeklyReport,
token: string,
): Promise<APIResponse<NewWeeklyReport>>;
/**Gets a weekly report*/
getWeeklyReport(
username: string,
projectName: string,
week: string,
token: string,
): Promise<APIResponse<NewWeeklyReport>>;
/** Login */
login(NewUser: NewUser): Promise<APIResponse<string>>;
}
// Export an instance of the API
@ -61,19 +54,13 @@ export const api: API = {
});
if (!response.ok) {
return {
success: false,
message: "Failed to register user: " + response.status,
};
return { success: false, message: "Failed to register user" };
} else {
// const data = (await response.json()) as User; // The API does not currently return the user
return { success: true };
const data = (await response.json()) as User;
return { success: true, data };
}
} catch (e) {
return {
success: false,
message: "Unknown error while registering user",
};
return { success: false, message: "Failed to register user" };
}
},
@ -176,9 +163,9 @@ export const api: API = {
},
async submitWeeklyReport(
weeklyReport: NewWeeklyReport,
project: NewWeeklyReport,
token: string,
): Promise<APIResponse<NewWeeklyReport>> {
): Promise<APIResponse<Project>> {
try {
const response = await fetch("/api/submitWeeklyReport", {
method: "POST",
@ -186,7 +173,7 @@ export const api: API = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
body: JSON.stringify(weeklyReport),
body: JSON.stringify(project),
});
if (!response.ok) {
@ -196,7 +183,7 @@ export const api: API = {
};
}
const data = (await response.json()) as NewWeeklyReport;
const data = (await response.json()) as Project;
return { success: true, data };
} catch (e) {
return {
@ -206,33 +193,6 @@ export const api: API = {
}
},
async getWeeklyReport(
username: string,
projectName: string,
week: string,
token: string,
): Promise<APIResponse<NewWeeklyReport>> {
try {
const response = await fetch("/api/getWeeklyReport", {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
body: JSON.stringify({ username, projectName, week }),
});
if (!response.ok) {
return { success: false, message: "Failed to get weekly report" };
} else {
const data = (await response.json()) as NewWeeklyReport;
return { success: true, data };
}
} catch (e) {
return { success: false, message: "Failed to get weekly report" };
}
},
async login(NewUser: NewUser): Promise<APIResponse<string>> {
try {
const response = await fetch("/api/login", {

View file

@ -1,94 +0,0 @@
import { useState } from "react";
import { APIResponse, api } from "../API/API";
import { NewProject, Project } from "../Types/goTypes";
import InputField from "./InputField";
import Logo from "../assets/Logo.svg";
import Button from "./Button";
/**
* Tries to add a project to the system
* @param props - Project name and description
* @returns {boolean} True if created, false if not
*/
function CreateProject(props: { name: string; description: string }): boolean {
const project: NewProject = {
name: props.name,
description: props.description,
};
let created = false;
api
.createProject(project, localStorage.getItem("accessToken") ?? "")
.then((response: APIResponse<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

@ -5,7 +5,7 @@ function Header({ username }: { username: string }): JSX.Element {
const [isOpen, setIsOpen] = useState(false);
const handleLogout = (): void => {
localStorage.clear();
// Add any logout logic here
};
return (

View file

@ -10,21 +10,17 @@ function LoginCheck(props: {
username: string;
password: string;
setAuthority: Dispatch<SetStateAction<number>>;
}): void {
}): number {
const user: NewUser = {
username: props.username,
password: props.password,
};
localStorage.clear();
api
.login(user)
.then((response: APIResponse<string>) => {
if (response.success) {
if (response.data !== undefined) {
const token = response.data;
localStorage.setItem("accessToken", token);
//TODO: change so that it checks for user type (admin, user, pm) instead
if (token !== "" && props.username === "admin") {
props.setAuthority((prevAuth) => {
@ -46,12 +42,14 @@ function LoginCheck(props: {
console.error("Token was undefined");
}
} else {
console.error("Token could not be fetched/No such user");
console.error("Token could not be fetched");
}
})
.catch((error) => {
console.error("An error occurred during login:", error);
});
return 0;
}
export default LoginCheck;

View file

@ -3,27 +3,40 @@ import { NewUser } from "../Types/goTypes";
import { api } from "../API/API";
import Logo from "../assets/Logo.svg";
import Button from "./Button";
import InputField from "./InputField";
import { useNavigate } from "react-router-dom";
function InputField(props: {
label: string;
type: string;
value: string;
onChange: (e: React.ChangeEvent<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 {
const [username, setUsername] = useState<string>();
const [password, setPassword] = useState<string>();
const [errMessage, setErrMessage] = useState<string>();
const nav = useNavigate();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const handleRegister = async (): Promise<void> => {
const newUser: NewUser = {
username: username ?? "",
password: password ?? "",
};
const response = await api.registerUser(newUser);
if (response.success) {
nav("/"); // Instantly navigate to the login page
} else {
setErrMessage(response.message ?? "Unknown error");
}
const newUser: NewUser = { username: username, password };
await api.registerUser(newUser); // TODO: Handle errors
};
return (

View file

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

View file

@ -1,13 +1,25 @@
import AddProject from "../../Components/AddProject";
import BackButton from "../../Components/BackButton";
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminAddProject(): JSX.Element {
const content = <AddProject />;
const content = <></>;
const buttons = (
<>
<BackButton />
<Button
text="Finish"
onClick={(): void => {
return;
}}
type="button"
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
type="button"
/>
</>
);

View file

@ -1,5 +1,3 @@
import { Link } from "react-router-dom";
import BackButton from "../../Components/BackButton";
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
@ -8,16 +6,20 @@ function AdminManageProjects(): JSX.Element {
const buttons = (
<>
<Link to="/addProject">
<Button
text="Add Project"
onClick={(): void => {
return;
}}
type="button"
/>
</Link>
<BackButton />
<Button
text="Add Project"
onClick={(): void => {
return;
}}
type="button"
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
type="button"
/>
</>
);

View file

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

View file

@ -6,12 +6,12 @@ function AdminMenuPage(): JSX.Element {
<>
<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]">
<Link to="/adminManageUser">
<Link to="/admin-manage-users">
<h1 className="font-bold underline text-[30px] cursor-pointer">
Manage Users
</h1>
</Link>
<Link to="/adminManageProject">
<Link to="/admin-manage-projects">
<h1 className="font-bold underline text-[30px] cursor-pointer">
Manage Projects
</h1>

View file

@ -5,11 +5,6 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom";
import App from "./Pages/App";
import AdminMenuPage from "./Pages/AdminPages/AdminMenuPage";
import YourProjectsPage from "./Pages/YourProjectsPage";
import AdminAddProject from "./Pages/AdminPages/AdminAddProject";
import AdminManageProjects from "./Pages/AdminPages/AdminManageProjects";
import AdminManageUsers from "./Pages/AdminPages/AdminManageUsers";
import AdminAddUser from "./Pages/AdminPages/AdminAddUser";
import AdminViewUserInfo from "./Pages/AdminPages/AdminViewUserInfo";
// This is where the routes are mounted
const router = createBrowserRouter([
@ -25,30 +20,6 @@ const router = createBrowserRouter([
path: "/pm",
element: <YourProjectsPage />,
},
{
path: "/user",
element: <YourProjectsPage />,
},
{
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

View file

@ -21,7 +21,6 @@ registerPath = base_url + "/api/register"
loginPath = base_url + "/api/login"
addProjectPath = base_url + "/api/project"
submitReportPath = base_url + "/api/submitReport"
getWeeklyReportPath = base_url + "/api/getWeeklyReport"
# Posts the username and password to the register endpoint
@ -75,7 +74,7 @@ def test_submit_report():
response = requests.post(
submitReportPath,
json={
"projectName": projectName,
"projectName": "report1",
"week": 1,
"developmentTime": 10,
"meetingTime": 5,
@ -90,18 +89,9 @@ def test_submit_report():
assert response.status_code == 200, "Submit report failed"
print("Submit report successful")
def test_get_weekly_report():
token = login(username, "always_same").json()["token"]
response = requests.get(
getWeeklyReportPath,
headers={"Authorization": "Bearer " + token},
params={"username": username, "projectName": projectName , "week": 1}
)
print(response.text)
if __name__ == "__main__":
test_create_user()
test_login()
test_add_project()
test_submit_report()
test_get_weekly_report()