Compare commits

..

No commits in common. "7635791f098e1dc56a9fc6f609d825574c74fbfc" and "12810075f9d9ce1eeaf490f51c12e15024bb496b" have entirely different histories.

23 changed files with 174 additions and 468 deletions

View file

@ -108,56 +108,6 @@ const docTemplate = `{
}
}
},
"/promote/{projectName}": {
"put": {
"security": [
{
"JWT": []
}
],
"description": "Promote a user to project manager",
"consumes": [
"text/plain"
],
"produces": [
"text/plain"
],
"tags": [
"Auth"
],
"summary": "Promote to project manager",
"parameters": [
{
"type": "string",
"description": "Project name",
"name": "projectName",
"in": "path",
"required": true
},
{
"type": "string",
"description": "User name",
"name": "userName",
"in": "query",
"required": true
}
],
"responses": {
"403": {
"description": "Forbidden",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "string"
}
}
}
}
},
"/promoteToAdmin": {
"post": {
"security": [

View file

@ -17,7 +17,6 @@ type Database interface {
AddUser(username string, password string) error
CheckUser(username string, password string) bool
RemoveUser(username string) error
RemoveUserFromProject(username string, projectname string) error
PromoteToAdmin(username string) error
GetUserId(username string) (int, error)
AddProject(name string, description string, username string) error
@ -44,7 +43,6 @@ type Database interface {
GetProjectTimes(projectName string) (map[string]int, error)
UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error
RemoveProject(projectname string) error
GetUserName(id int) (string, error)
}
// This struct is a wrapper type that holds the database connection
@ -88,10 +86,6 @@ const isProjectManagerQuery = `SELECT COUNT(*) > 0 FROM user_roles
JOIN projects ON user_roles.project_id = projects.id
WHERE users.username = ? AND projects.name = ? AND user_roles.p_role = 'project_manager'`
const removeUserFromProjectQuery = `DELETE FROM user_roles
WHERE user_id = (SELECT id FROM users WHERE username = ?)
AND project_id = (SELECT id FROM projects WHERE name = ?)`
// DbConnect connects to the database
func DbConnect(dbpath string) Database {
// Open the database
@ -153,11 +147,6 @@ func (d *Db) AddUserToProject(username string, projectname string, role string)
return err
}
func (d *Db) RemoveUserFromProject(username string, projectname string) error {
_, err := d.Exec(removeUserFromProjectQuery, username, projectname)
return err
}
// ChangeUserRole changes the role of a user within a project.
func (d *Db) ChangeUserRole(username string, projectname string, role string) error {
// Execute the SQL query to change the user's role
@ -350,14 +339,9 @@ func (d *Db) SignWeeklyReport(reportId int, projectManagerId int) error {
return err
}
managerQuery := `SELECT project_id FROM user_roles
WHERE user_id = ?
AND project_id = (SELECT project_id FROM weekly_reports WHERE report_id = ?)
AND p_role = 'project_manager'`
// Retrieve the project ID associated with the project manager
var managerProjectID int
err = d.Get(&managerProjectID, managerQuery, projectManagerId, reportId)
err = d.Get(&managerProjectID, "SELECT project_id FROM user_roles WHERE user_id = ? AND p_role = 'project_manager'", projectManagerId)
if err != nil {
return err
}
@ -617,9 +601,3 @@ func (d *Db) RemoveProject(projectname string) error {
_, err := d.Exec("DELETE FROM projects WHERE name = ?", projectname)
return err
}
func (d *Db) GetUserName(id int) (string, error) {
var username string
err := d.Get(&username, "SELECT username FROM users WHERE id = ?", id)
return username, err
}

View file

@ -1,40 +0,0 @@
package projects
import (
db "ttime/internal/database"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
"github.com/golang-jwt/jwt/v5"
)
func RemoveUserFromProject(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
pm_name := claims["name"].(string)
project := c.Params("projectName")
username := c.Query("userName")
// Check if the user is a project manager
isPM, err := db.GetDb(c).IsProjectManager(pm_name, project)
if err != nil {
log.Info("Error checking if user is project manager:", err)
return c.Status(500).SendString(err.Error())
}
if !isPM {
log.Info("User: ", pm_name, " is not a project manager in project: ", project)
return c.Status(403).SendString("User is not a project manager")
}
// Remove the user from the project
if err = db.GetDb(c).RemoveUserFromProject(username, project); err != nil {
log.Info("Error removing user from project:", err)
return c.Status(500).SendString(err.Error())
}
// Return success message
log.Info("User : ", username, " removed from project: ", project)
return c.SendStatus(fiber.StatusOK)
}

View file

@ -1,32 +0,0 @@
package users
import (
"strconv"
db "ttime/internal/database"
"github.com/gofiber/fiber/v2"
)
// Return the username of a user given their user id
func GetUserName(c *fiber.Ctx) error {
// Check the query params for userId
user_id_string := c.Query("userId")
if user_id_string == "" {
return c.Status(400).SendString("Missing user id")
}
// Convert to int
user_id, err := strconv.Atoi(user_id_string)
if err != nil {
return c.Status(400).SendString("Invalid user id")
}
// Get the username from the database
username, err := db.GetDb(c).GetUserName(user_id)
if err != nil {
return c.Status(500).SendString(err.Error())
}
// Send the nuclear launch codes to north korea
return c.JSON(fiber.Map{"username": username})
}

View file

@ -103,7 +103,6 @@ func main() {
// userGroup := api.Group("/user") // Not currently in use
api.Get("/users/all", users.ListAllUsers)
api.Get("/project/getAllUsers", users.GetAllUsersProject)
api.Get("/username", users.GetUserName)
api.Post("/login", users.Login)
api.Post("/register", users.Register)
api.Post("/loginrenew", users.LoginRenew)
@ -122,7 +121,6 @@ func main() {
api.Post("/ProjectRoleChange", projects.ProjectRoleChange)
api.Put("/promoteToPm/:projectName", projects.PromoteToPm)
api.Put("/addUserToProject/:projectName", projects.AddUserToProjectHandler)
api.Delete("/removeUserFromProject/:projectName", projects.RemoveUserFromProject)
api.Delete("/removeProject/:projectName", projects.RemoveProject)
api.Delete("/project/:projectID", projects.DeleteProject)

View file

@ -1,4 +1,4 @@
import { AddMemberInfo } from "../Components/AddMember";
import { NewProjMember } from "../Components/AddMember";
import { ProjectRoleChange } from "../Components/ChangeRole";
import { projectTimes } from "../Components/GetProjectTimes";
import { ProjectMember } from "../Components/GetUsersInProject";
@ -197,13 +197,7 @@ interface API {
): Promise<APIResponse<void>>;
addUserToProject(
addMemberInfo: AddMemberInfo,
token: string,
): Promise<APIResponse<void>>;
removeUserFromProject(
user: string,
project: string,
user: NewProjMember,
token: string,
): Promise<APIResponse<void>>;
@ -233,12 +227,6 @@ interface API {
projectName: string,
token: string,
): Promise<APIResponse<string>>;
/**
* Get the username from the id
* @param {number} id The id of the user
* @param {string} token Your token
*/
getUsername(id: number, token: string): Promise<APIResponse<string>>;
}
/** An instance of the API */
@ -348,20 +336,18 @@ export const api: API = {
},
async addUserToProject(
addMemberInfo: AddMemberInfo,
user: NewProjMember,
token: string,
): Promise<APIResponse<void>> {
try {
const response = await fetch(
`/api/addUserToProject/${addMemberInfo.projectName}/?userName=${addMemberInfo.userName}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
const response = await fetch("/api/addUserToProject", {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
);
body: JSON.stringify(user),
});
if (!response.ok) {
return { success: false, message: "Failed to add member" };
@ -373,31 +359,6 @@ export const api: API = {
}
},
async removeUserFromProject(
user: string,
project: string,
token: string,
): Promise<APIResponse<void>> {
try {
const response = await fetch(
`/api/removeUserFromProject/${project}?userName=${user}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
},
);
if (!response.ok) {
return { success: false, message: "Failed to remove member" };
}
} catch (e) {
return { success: false, message: "Failed to remove member" };
}
return { success: true, message: "Removed member" };
},
async renewToken(token: string): Promise<APIResponse<string>> {
try {
const response = await fetch("/api/loginrenew", {
@ -577,7 +538,7 @@ export const api: API = {
): Promise<APIResponse<WeeklyReport>> {
try {
const response = await fetch(
`/api/getWeeklyReport?projectName=${projectName}&week=${week}&targetUser=${targetUser ?? ""}`,
`/api/getWeeklyReport?projectName=${projectName}&week=${week}&targetUser=${targetUser}`,
{
method: "GET",
headers: {
@ -605,7 +566,7 @@ export const api: API = {
): Promise<APIResponse<WeeklyReport[]>> {
try {
const response = await fetch(
`/api/getAllWeeklyReports/${projectName}?targetUser=${targetUser ?? ""}`,
`/api/getAllWeeklyReports/${projectName}?targetUser=${targetUser}`,
{
method: "GET",
headers: {
@ -876,25 +837,4 @@ export const api: API = {
}
return { success: true, message: "User promoted to project manager" };
},
async getUsername(id: number, token: string): Promise<APIResponse<string>> {
try {
const response = await fetch(`/api/username?userId=${id}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
});
if (!response.ok) {
return { success: false, message: "Failed to get username" };
} else {
const data = (await response.json()) as string;
return { success: true, data };
}
} catch (e) {
return { success: false, message: "Failed to get username" };
}
},
};

View file

@ -1,35 +1,44 @@
import { api } from "../API/API";
import { APIResponse, api } from "../API/API";
export interface AddMemberInfo {
userName: string;
projectName: string;
export interface NewProjMember {
username: string;
role: string;
projectname: string;
}
/**
* Tries to add a member to a project
* @param {AddMemberInfo} props.membertoAdd - Contains user's name and project's name
* @returns {Promise<void>}
* @param {Object} props - A NewProjMember
* @returns {boolean} True if added, false if not
*/
async function AddMember(props: { memberToAdd: AddMemberInfo }): Promise<void> {
if (props.memberToAdd.userName === "") {
alert("You must choose at least one user to add");
return;
function AddMember(props: { memberToAdd: NewProjMember }): boolean {
let added = false;
if (
props.memberToAdd.username === "" ||
props.memberToAdd.role === "" ||
props.memberToAdd.projectname === ""
) {
alert("All fields must be filled before adding");
return added;
}
try {
const response = await api.addUserToProject(
api
.addUserToProject(
props.memberToAdd,
localStorage.getItem("accessToken") ?? "",
);
if (response.success) {
alert(`[${props.memberToAdd.userName}] added`);
} else {
alert(`[${props.memberToAdd.userName}] not added`);
console.error(response.message);
}
} catch (error) {
alert(`[${props.memberToAdd.userName}] not added`);
console.error("An error occurred during member add:", error);
}
)
.then((response: APIResponse<void>) => {
if (response.success) {
alert("Member added");
added = true;
} else {
alert("Member not added");
console.error(response.message);
}
})
.catch((error) => {
console.error("An error occurred during member add:", error);
});
return added;
}
export default AddMember;

View file

@ -1,10 +1,37 @@
import { useState } from "react";
import { api } from "../API/API";
import { APIResponse, api } from "../API/API";
import { NewProject } 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 {Object} props - Project name and description
* @returns {boolean} True if created, false if not
*/
function CreateProject(props: { name: string; description: string }): void {
const project: NewProject = {
name: props.name,
description: props.description,
};
api
.createProject(project, localStorage.getItem("accessToken") ?? "")
.then((response: APIResponse<void>) => {
if (response.success) {
alert("Project added!");
} else {
alert("Project NOT added!");
console.error(response.message);
}
})
.catch((error) => {
alert("Project NOT added!");
console.error("An error occurred during creation:", error);
});
}
/**
* Provides UI for adding a project to the system.
* @returns {JSX.Element} - Returns the component UI for adding a project
@ -13,33 +40,6 @@ function AddProject(): JSX.Element {
const [name, setName] = useState("");
const [description, setDescription] = useState("");
/**
* Tries to add a project to the system
*/
const handleCreateProject = async (): Promise<void> => {
const project: NewProject = {
name: name.replace(/ /g, ""),
description: description.trim(),
};
try {
const response = await api.createProject(
project,
localStorage.getItem("accessToken") ?? "",
);
if (response.success) {
alert(`${project.name} added!`);
setDescription("");
setName("");
} else {
alert("Project not added, name could be taken");
console.error(response.message);
}
} catch (error) {
alert("Project not added");
console.error(error);
}
};
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">
@ -47,7 +47,10 @@ function AddProject(): JSX.Element {
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();
void handleCreateProject();
CreateProject({
name: name,
description: description,
});
}}
>
<img

View file

@ -1,86 +1,71 @@
import { useEffect, useState } from "react";
import { useState } from "react";
import Button from "./Button";
import AddMember, { AddMemberInfo } from "./AddMember";
import BackButton from "./BackButton";
import GetUsersInProject, { ProjectMember } from "./GetUsersInProject";
import GetAllUsers from "./GetAllUsers";
import AddMember, { NewProjMember } from "./AddMember";
import BackButton from "./BackButton";
/**
* Provides UI for adding a member to a project.
* @returns {JSX.Element} - Returns the component UI for adding a member
*/
function AddUserToProject(props: { projectName: string }): JSX.Element {
const [names, setNames] = useState<string[]>([]);
const [name, setName] = useState("");
const [users, setUsers] = useState<string[]>([]);
const [usersProj, setUsersProj] = useState<ProjectMember[]>([]);
// Gets all users and project members for filtering
const [role, setRole] = useState("");
GetAllUsers({ setUsersProp: setUsers });
GetUsersInProject({
setUsersProp: setUsersProj,
projectName: props.projectName,
});
/*
* Filters the members from users so that users who are already
* members are not shown
*/
useEffect(() => {
setUsers((prevUsers) => {
const filteredUsers = prevUsers.filter(
(user) =>
!usersProj.some((projectUser) => projectUser.Username === user),
);
return filteredUsers;
});
}, [usersProj]);
// Attempts to add all of the selected users to the project
const handleAddClick = async (): Promise<void> => {
if (names.length === 0)
alert("You have to choose at least one user to add");
for (const name of names) {
const newMember: AddMemberInfo = {
userName: name,
projectName: props.projectName,
};
await AddMember({ memberToAdd: newMember });
}
setNames([]);
location.reload();
};
// Updates the names that have been selected
const handleUserClick = (user: string): void => {
setNames((prevNames): string[] => {
if (!prevNames.includes(user)) {
return [...prevNames, user];
}
return prevNames.filter((name) => name !== user);
});
const handleClick = (): boolean => {
const newMember: NewProjMember = {
username: name,
projectname: props.projectName,
role: role,
};
return AddMember({ memberToAdd: newMember });
};
return (
<div className="border-4 border-black bg-white flex flex-col items-center pt-10 rounded-3xl content-center pl-20 pr-20 h-[63vh] w-[50] overflow-auto">
<h1 className="text-center font-bold text-[36px] pb-10">
{props.projectName}
</h1>
<p className="p-1 text-center font-bold text-[26px]">
Choose users to add:
<div className="border-4 border-black bg-white flex flex-col items-center justify-center rounded-3xl content-center pl-20 pr-20 h-[75vh] w-[50vh]">
<p className="pb-4 mb-2 text-center font-bold text-[18px]">
User chosen: [{name}]
</p>
<div className="border-2 border-black pl-2 pr-2 pb-2 rounded-xl text-center overflow-auto h-[26vh] w-[26vh]">
<p className="pb-4 mb-2 text-center font-bold text-[18px]">
Role chosen: [{role}]
</p>
<p className="pb-4 mb-2 text-center font-bold text-[18px]">
Project chosen: [{props.projectName}]
</p>
<p className="p-1">Choose role:</p>
<div className="border-2 border-black p-2 rounded-xl text-center h-[10h] w-[16] overflow-auto">
<ul className="text-center items-center font-medium space-y-2">
<li
className="h-[10] w-[14] items-start px-2 py-1 border-2 border-black rounded-full bg-orange-200 hover:bg-orange-600 hover:text-slate-100 hover:cursor-pointer"
onClick={() => {
setRole("member");
}}
>
{"Member"}
</li>
<li
className="h-[10] w-[14] items-start px-2 py-1 border-2 border-black rounded-full bg-orange-200 hover:bg-orange-600 hover:text-slate-100 hover:cursor-pointer"
onClick={() => {
setRole("project_manager");
}}
>
{"Project manager"}
</li>
</ul>
</div>
<p className="p-1">Choose user:</p>
<div className="border-2 border-black p-2 rounded-xl text-center overflow-scroll h-[26vh] w-[26vh]">
<ul className="text-center font-medium space-y-2">
<div></div>
{users.map((user) => (
<li
className={
names.includes(user)
? "items-start p-1 border-2 border-transparent rounded-full bg-orange-500 hover:bg-orange-600 text-white hover:cursor-pointer ring-2 ring-black"
: "items-start p-1 border-2 border-black rounded-full bg-orange-200 hover:bg-orange-400 hover:text-slate-100 hover:cursor-pointer"
}
className="items-start p-1 border-2 border-black rounded-full bg-orange-200 hover:bg-orange-600 hover:text-slate-100 hover:cursor-pointer"
key={user}
value={user}
onClick={() => {
handleUserClick(user);
setName(user);
}}
>
<span>{user}</span>
@ -88,16 +73,13 @@ function AddUserToProject(props: { projectName: string }): JSX.Element {
))}
</ul>
</div>
<p className="pt-10 pb-5 underline text-center font-bold text-[18px]">
Number of users to be added: {names.length}
</p>
<div className="space-x-10 items-center">
<div className="flex space-x-5 items-center justify-between">
<Button
text="Add"
onClick={(): void => {
void handleAddClick();
handleClick();
}}
type="button"
type="submit"
/>
<BackButton />
</div>

View file

@ -17,7 +17,7 @@ function AllTimeReportsInProject(): JSX.Element {
useEffect(() => {
const getWeeklyReports = async (): Promise<void> => {
const token = localStorage.getItem("accessToken") ?? "";
const response = await api.getAllWeeklyReportsForUser(
const response = await api.getWeeklyReportsForUser(
projectName ?? "",
token,
);

View file

@ -17,10 +17,10 @@ function AllTimeReportsInProject(): JSX.Element {
useEffect(() => {
const getWeeklyReports = async (): Promise<void> => {
const token = localStorage.getItem("accessToken") ?? "";
const response = await api.getAllWeeklyReportsForUser(
const response = await api.getWeeklyReportsForDifferentUser(
projectName ?? "",
token,
username ?? "",
token,
);
console.log(response);
if (response.success) {
@ -31,7 +31,7 @@ function AllTimeReportsInProject(): JSX.Element {
};
void getWeeklyReports();
}, [projectName, username]);
}, []);
return (
<>

View file

@ -2,7 +2,7 @@ import { useState } from "react";
import Button from "./Button";
import ChangeRole, { ProjectRoleChange } from "./ChangeRole";
export default function ChangeRoleView(props: {
export default function ChangeRoles(props: {
projectName: string;
username: string;
}): JSX.Element {

View file

@ -2,11 +2,8 @@ import { APIResponse, api } from "../API/API";
import { StrNameChange } from "../Types/goTypes";
function ChangeUsername(props: { nameChange: StrNameChange }): void {
if (
props.nameChange.newName === "" ||
props.nameChange.newName === props.nameChange.prevName
) {
alert("You have to give a new name\n\nName not changed");
if (props.nameChange.newName === "") {
alert("You have to select a new name");
return;
}
api
@ -16,7 +13,7 @@ function ChangeUsername(props: { nameChange: StrNameChange }): void {
alert("Name changed successfully");
location.reload();
} else {
alert("Name not changed, name could be taken");
alert("Name not changed");
console.error(response.message);
}
})

View file

@ -3,14 +3,17 @@ import { Link, useParams } from "react-router-dom";
import { api } from "../API/API";
import { WeeklyReport } from "../Types/goTypes";
/**
* Renders a component that displays the projects a user is a part of and links to the projects start-page.
* @returns The JSX element representing the component.
*/
function DisplayUserProject(): JSX.Element {
const { projectName } = useParams();
const [unsignedReports, setUnsignedReports] = useState<WeeklyReport[]>([]);
const [usernames, setUsernames] = useState<string[]>([]);
const token = localStorage.getItem("accessToken") ?? "";
//const navigate = useNavigate();
useEffect(() => {
const getUnsignedReports = async (): Promise<void> => {
const token = localStorage.getItem("accessToken") ?? "";
const response = await api.getUnsignedReportsInProject(
projectName ?? "",
token,
@ -18,21 +21,13 @@ function DisplayUserProject(): JSX.Element {
console.log(response);
if (response.success) {
setUnsignedReports(response.data ?? []);
const usernamesPromises = (response.data ?? []).map((report) =>
api.getUsername(report.userId, token),
);
const usernamesResponses = await Promise.all(usernamesPromises);
const usernames = usernamesResponses.map(
(res) => (res.data as { username?: string }).username ?? "",
);
setUsernames(usernames);
} else {
console.error(response.message);
}
};
void getUnsignedReports();
}, [projectName, token]);
}, [projectName]); // Include 'projectName' in the dependency array
return (
<>
@ -44,8 +39,8 @@ function DisplayUserProject(): JSX.Element {
<h1 key={index} className="border-b-2 border-black w-full">
<div className="flex justify-between">
<div className="flex">
<span className="ml-6 mr-2 font-bold">Username:</span>
<h1>{usernames[index]}</h1>{" "}
<span className="ml-6 mr-2 font-bold">UserID:</span>
<h1>{unsignedReport.userId}</h1>
<span className="ml-6 mr-2 font-bold">Week:</span>
<h1>{unsignedReport.week}</h1>
<span className="ml-6 mr-2 font-bold">Total Time:</span>
@ -63,7 +58,7 @@ function DisplayUserProject(): JSX.Element {
<div className="flex">
<div className="ml-auto flex space-x-4">
<Link
to={`/PMViewUnsignedReport/${projectName}/${usernames[index]}/${unsignedReport.week}`}
to={`/PMViewUnsignedReport/${projectName}/${unsignedReport.userId}/${unsignedReport.week}`}
>
<h1 className="underline cursor-pointer font-bold">
View Report

View file

@ -1,6 +1,6 @@
//info: Header component to display the header of the page including the logo and user information where thr user can logout
import { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { Link } from "react-router-dom";
import backgroundImage from "../assets/1.jpg";
/**
@ -9,33 +9,23 @@ import backgroundImage from "../assets/1.jpg";
*/
function Header(): JSX.Element {
const [isOpen, setIsOpen] = useState(false);
const username = localStorage.getItem("username");
const navigate = useNavigate();
const handleLogout = (): void => {
localStorage.clear();
};
const handleNavigation = (): void => {
if (username === "admin") {
navigate("/admin");
} else {
navigate("/yourProjects");
}
};
return (
<header
className="fixed top-0 left-0 right-0 border-[1.75px] border-black text-black p-3 pl-5 flex items-center justify-between bg-cover"
style={{ backgroundImage: `url(${backgroundImage})` }}
>
<div onClick={handleNavigation}>
<Link to="/your-projects">
<img
src="/src/assets/Logo.svg"
alt="TTIME Logo"
className="w-11 h-14 cursor-pointer"
/>
</div>
</Link>
<div
className="relative"

View file

@ -1,8 +1,8 @@
import Button from "./Button";
import DeleteUser from "./DeleteUser";
import UserProjectListAdmin from "./UserProjectListAdmin";
import { useState } from "react";
import ChangeRoleView from "./ChangeRoleView";
import RemoveUserFromProj from "./RemoveUserFromProj";
function MemberInfoModal(props: {
projectName: string;
@ -20,7 +20,7 @@ function MemberInfoModal(props: {
};
return (
<div
className="fixed inset-10 bg-opacity-30 backdrop-blur-sm
className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm
flex justify-center items-center"
>
<div className="border-4 border-black bg-white rounded-lg text-center flex flex-col">
@ -42,16 +42,13 @@ function MemberInfoModal(props: {
<UserProjectListAdmin username={props.username} />
<div className="items-center space-x-6">
<Button
text={"Remove"}
text={"Delete"}
onClick={function (): void {
if (
window.confirm(
"Are you sure you want to remove this user from the project?",
)
window.confirm("Are you sure you want to delete this user?")
) {
RemoveUserFromProj({
userToRemove: props.username,
projectName: props.projectName,
DeleteUser({
usernameToDelete: props.username,
});
}
}}

View file

@ -62,7 +62,7 @@ export default function NewWeeklyReport(): JSX.Element {
const success = await handleNewWeeklyReport();
if (!success) {
alert(
"Error occurred! Your connection to the server might be lost or a time report for this week already exists, please check your connection or go to the edit page to edit your report or change week number.",
"A Time Report for this week already exists, please go to the edit page to edit it or change week number.",
);
return;
}

View file

@ -29,7 +29,6 @@ export default function OtherUsersTR(): JSX.Element {
projectName ?? "",
fetchedWeek?.toString() ?? "0",
token,
username ?? "",
);
if (response.success) {
@ -87,7 +86,6 @@ export default function OtherUsersTR(): JSX.Element {
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={developmentTime === 0 ? "" : developmentTime}
readOnly
/>
</td>
</tr>
@ -99,7 +97,6 @@ export default function OtherUsersTR(): JSX.Element {
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={meetingTime === 0 ? "" : meetingTime}
readOnly
/>
</td>
</tr>
@ -111,7 +108,6 @@ export default function OtherUsersTR(): JSX.Element {
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={adminTime === 0 ? "" : adminTime}
readOnly
/>
</td>
</tr>
@ -123,7 +119,6 @@ export default function OtherUsersTR(): JSX.Element {
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={ownWorkTime === 0 ? "" : ownWorkTime}
readOnly
/>
</td>
</tr>
@ -135,7 +130,6 @@ export default function OtherUsersTR(): JSX.Element {
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={studyTime === 0 ? "" : studyTime}
readOnly
/>
</td>
</tr>
@ -147,7 +141,6 @@ export default function OtherUsersTR(): JSX.Element {
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={testingTime === 0 ? "" : testingTime}
readOnly
/>
</td>
</tr>

View file

@ -15,21 +15,17 @@ export default function Register(): JSX.Element {
const [errMessage, setErrMessage] = useState<string>();
const handleRegister = async (): Promise<void> => {
if (username === "" || password === "") {
alert("Must provide username and password");
return;
}
const newUser: NewUser = {
username: username?.replace(/ /g, "") ?? "",
username: username ?? "",
password: password ?? "",
};
const response = await api.registerUser(newUser);
if (response.success) {
alert(`${newUser.username} added!`);
alert("User added!");
setPassword("");
setUsername("");
} else {
alert("User not added, name could be taken");
alert("User not added");
setErrMessage(response.message ?? "Unknown error");
console.error(errMessage);
}

View file

@ -1,41 +0,0 @@
import { api, APIResponse } from "../API/API";
/**
* Removes a user from a project
* @param {string} props.usernameToDelete - The username of user to remove
* @param {string} props.projectName - Project to remove user from
* @returns {void}
* @example
* const exampleUsername = "user";
* const exampleProjectName "project";
* RemoveUserFromProj({ userToRemove: exampleUsername, projectName: exampleProjectName });
*/
export default function RemoveUserFromProj(props: {
userToRemove: string;
projectName: string;
}): void {
if (props.userToRemove === localStorage.getItem("username")) {
alert("Cannot remove yourself");
return;
}
api
.removeUserFromProject(
props.userToRemove,
props.projectName,
localStorage.getItem("accessToken") ?? "",
)
.then((response: APIResponse<void>) => {
if (response.success) {
alert(`${props.userToRemove} has been removed!`);
location.reload();
} else {
alert(`${props.userToRemove} has not been removed due to an error`);
console.error(response.message);
}
})
.catch((error) => {
alert(`${props.userToRemove} has not been removed due to an error`);
console.error("An error occurred during deletion:", error);
});
}

View file

@ -8,12 +8,12 @@ import { projectTimes } from "./GetProjectTimes";
* @returns JSX.Element
*/
export default function TimePerRole(): JSX.Element {
const [development, setDevelopment] = useState<number>(0);
const [meeting, setMeeting] = useState<number>(0);
const [admin, setAdmin] = useState<number>(0);
const [own_work, setOwnWork] = useState<number>(0);
const [study, setStudy] = useState<number>(0);
const [testing, setTesting] = useState<number>(0);
const [development, setDevelopment] = useState<number>();
const [meeting, setMeeting] = useState<number>();
const [admin, setAdmin] = useState<number>();
const [own_work, setOwnWork] = useState<number>();
const [study, setStudy] = useState<number>();
const [testing, setTesting] = useState<number>();
const token = localStorage.getItem("accessToken") ?? "";
const { projectName } = useParams();

View file

@ -28,7 +28,7 @@ function UserInfoModal(props: {
const handleClickChangeName = (): void => {
const nameChange: StrNameChange = {
prevName: props.username,
newName: newUsername.replace(/ /g, ""),
newName: newUsername,
};
ChangeUsername({ nameChange: nameChange });
};

View file

@ -31,9 +31,8 @@ export default function GetOtherUsersReport(): JSX.Element {
projectName ?? "",
fetchedWeek?.toString() ?? "0",
token,
username ?? "",
);
console.log(response);
if (response.success) {
const report: WeeklyReport = response.data ?? {
reportId: 0,
@ -63,33 +62,25 @@ export default function GetOtherUsersReport(): JSX.Element {
void fetchUsersWeeklyReport();
});
const handleSignWeeklyReport = async (): Promise<boolean> => {
const response = await api.signReport(reportId, token);
if (response.success) {
return true;
} else {
return false;
}
const handleSignWeeklyReport = async (): Promise<void> => {
await api.signReport(reportId, token);
};
const navigate = useNavigate();
return (
<>
<h1 className="text-[30px] font-bold"> {username}&apos;s Report</h1>
<h1 className="text-[30px] font-bold">
{" "}
UserId: {username}&apos;s Report
</h1>
<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) => {
e.preventDefault();
void (async (): Promise<void> => {
const success = await handleSignWeeklyReport();
if (!success) {
alert("Failed to sign report!");
return;
}
alert("Report successfully signed!");
navigate(-1);
})();
void handleSignWeeklyReport();
alert("Report successfully signed!");
navigate(-1);
}}
>
<div className="flex flex-col items-center">