Merge branch 'dev' into gruppDM

This commit is contained in:
Davenludd 2024-03-21 08:56:07 +01:00
commit faf998e652
29 changed files with 664 additions and 106 deletions

View file

@ -4,6 +4,7 @@ import {
User,
Project,
NewProject,
UserProjectMember,
WeeklyReport,
} from "../Types/goTypes";
@ -127,6 +128,11 @@ interface API {
* @returns {Promise<APIResponse<string[]>>} A promise resolving to an API response containing the list of users.
*/
getAllUsers(token: string): Promise<APIResponse<string[]>>;
/** Gets all users in a project from name*/
getAllUsersProject(
projectName: string,
token: string,
): Promise<APIResponse<UserProjectMember[]>>;
}
/** An instance of the API */
@ -448,4 +454,34 @@ export const api: API = {
});
}
},
//Gets all users in a project
async getAllUsersProject(
projectName: string,
token: string,
): Promise<APIResponse<UserProjectMember[]>> {
try {
const response = await fetch(`/api/getUsersProject/${projectName}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
});
if (!response.ok) {
return Promise.resolve({
success: false,
message: "Failed to get users",
});
} else {
const data = (await response.json()) as UserProjectMember[];
return Promise.resolve({ success: true, data });
}
} catch (e) {
return Promise.resolve({
success: false,
message: "API is not ok",
});
}
},
};

View file

@ -13,24 +13,24 @@ function AllTimeReportsInProject(): JSX.Element {
const { projectName } = useParams();
const [weeklyReports, setWeeklyReports] = useState<WeeklyReport[]>([]);
const getWeeklyReports = async (): Promise<void> => {
const token = localStorage.getItem("accessToken") ?? "";
const response = await api.getWeeklyReportsForUser(
projectName ?? "",
token,
);
console.log(response);
if (response.success) {
setWeeklyReports(response.data ?? []);
} else {
console.error(response.message);
}
};
// Call getProjects when the component mounts
useEffect(() => {
const getWeeklyReports = async (): Promise<void> => {
const token = localStorage.getItem("accessToken") ?? "";
const response = await api.getWeeklyReportsForUser(
projectName ?? "",
token,
);
console.log(response);
if (response.success) {
setWeeklyReports(response.data ?? []);
} else {
console.error(response.message);
}
};
void getWeeklyReports();
}, []);
}, [projectName]);
return (
<>

View file

@ -0,0 +1,37 @@
import { Dispatch, useEffect } from "react";
import { Project } from "../Types/goTypes";
import { api } from "../API/API";
/**
* Gets all projects that user is a member of
* @param props - A setStateAction for the array you want to put projects in
* @returns {void} Nothing
* @example
* const [projects, setProjects] = useState<Project[]>([]);
* GetAllUsers({ setProjectsProp: setProjects });
*/
function GetProjects(props: {
setProjectsProp: Dispatch<React.SetStateAction<Project[]>>;
}): void {
const setProjects: Dispatch<React.SetStateAction<Project[]>> =
props.setProjectsProp;
useEffect(() => {
const fetchUsers = async (): Promise<void> => {
try {
const token = localStorage.getItem("accessToken") ?? "";
const response = await api.getUserProjects(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 fetchUsers();
}, [setProjects]);
}
export default GetProjects;

View file

@ -0,0 +1,37 @@
import { Dispatch, useEffect } from "react";
import { UserProjectMember } from "../Types/goTypes";
import { api } from "../API/API";
/**
* Gets all projects that user is a member of
* @param props - A setStateAction for the array you want to put projects in
* @returns {void} Nothing
* @example
* const [projects, setProjects] = useState<Project[]>([]);
* GetAllUsers({ setProjectsProp: setProjects });
*/
function GetUsersInProject(props: {
projectName: string;
setUsersProp: Dispatch<React.SetStateAction<UserProjectMember[]>>;
}): void {
const setUsers: Dispatch<React.SetStateAction<UserProjectMember[]>> =
props.setUsersProp;
useEffect(() => {
const fetchUsers = async (): Promise<void> => {
try {
const token = localStorage.getItem("accessToken") ?? "";
const response = await api.getAllUsersProject(props.projectName, token);
if (response.success) {
setUsers(response.data ?? []);
} else {
console.error("Failed to fetch projects:", response.message);
}
} catch (error) {
console.error("Error fetching projects:", error);
}
};
void fetchUsers();
}, [props.projectName, setUsers]);
}
export default GetUsersInProject;

View file

@ -0,0 +1,66 @@
import { useState } from "react";
import Button from "./Button";
import { UserProjectMember } from "../Types/goTypes";
import GetUsersInProject from "./GetUsersInProject";
function ProjectInfoModal(props: {
isVisible: boolean;
projectname: string;
onClose: () => void;
onClick: (username: string) => void;
}): JSX.Element {
const [users, setUsers] = useState<UserProjectMember[]>([]);
GetUsersInProject({ projectName: props.projectname, setUsersProp: setUsers });
if (!props.isVisible) return <></>;
return (
<div
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 p-2 rounded-2xl text-center h-[41vh] w-[40vw] flex flex-col">
<div className="pl-20 pr-20">
<h1 className="font-bold text-[32px] mb-[20px]">Project members:</h1>
<div className="border-2 border-black p-2 rounded-lg text-center overflow-scroll h-[26vh]">
<ul className="text-left font-medium space-y-2">
<div></div>
{users.map((user) => (
<li
className="items-start p-1 border-2 border-black rounded-lg bg-orange-200 hover:bg-orange-600 hover:text-slate-100 hover:cursor-pointer"
key={user.Username}
onClick={() => {
props.onClick(user.Username);
}}
>
<span>
Name: {user.Username}
<div></div>
Role: {user.UserRole}
</span>
</li>
))}
</ul>
</div>
</div>
<div className="space-x-16">
<Button
text={"Delete"}
onClick={function (): void {
//DELETE PROJECT
}}
type="button"
/>
<Button
text={"Close"}
onClick={function (): void {
props.onClose();
}}
type="button"
/>
</div>
</div>
</div>
);
}
export default ProjectInfoModal;

View file

@ -0,0 +1,79 @@
import { useState } from "react";
import { NewProject } from "../Types/goTypes";
import ProjectInfoModal from "./ProjectInfoModal";
import UserInfoModal from "./UserInfoModal";
import DeleteUser from "./DeleteUser";
/**
* A list of projects for admin manage projects page, that sets an onClick
* function for eact project <li> element, which displays a modul with
* user info.
* @param props - An array of projects to display
* @returns {JSX.Element} The project list
* @example
* const projects: NewProject[] = [{ name: "Project", description: "New" }];
* return <ProjectListAdmin projects={projects} />
*/
export function ProjectListAdmin(props: {
projects: NewProject[];
}): JSX.Element {
const [projectModalVisible, setProjectModalVisible] = useState(false);
const [projectname, setProjectname] = useState("");
const [userModalVisible, setUserModalVisible] = useState(false);
const [username, setUsername] = useState("");
const handleClickUser = (username: string): void => {
setUsername(username);
setUserModalVisible(true);
};
const handleClickProject = (username: string): void => {
setProjectname(username);
setProjectModalVisible(true);
};
const handleCloseProject = (): void => {
setProjectname("");
setProjectModalVisible(false);
};
const handleCloseUser = (): void => {
setProjectname("");
setUserModalVisible(false);
};
return (
<>
<ProjectInfoModal
onClose={handleCloseProject}
onClick={handleClickUser}
isVisible={projectModalVisible}
projectname={projectname}
/>
<UserInfoModal
manageMember={true}
onClose={handleCloseUser}
//TODO: CHANGE TO REMOVE USER FROM PROJECT
onDelete={() => DeleteUser}
isVisible={userModalVisible}
username={username}
/>
<div>
<ul className="font-bold underline text-[30px] cursor-pointer padding">
{props.projects.map((project) => (
<li
className="pt-5"
key={project.name}
onClick={() => {
handleClickProject(project.name);
}}
>
{project.name}
</li>
))}
</ul>
</div>
</>
);
}

View file

@ -5,23 +5,38 @@ import UserProjectListAdmin from "./UserProjectListAdmin";
function UserInfoModal(props: {
isVisible: boolean;
manageMember: boolean;
username: string;
onClose: () => void;
onDelete: (username: string) => void;
}): JSX.Element {
if (!props.isVisible) return <></>;
const ManageUserOrMember = (check: boolean): JSX.Element => {
if (check) {
return (
<Link to="/AdminChangeRole">
<p className="mb-[20px] hover:font-bold hover:cursor-pointer underline">
(Change Role)
</p>
</Link>
);
}
return (
<Link to="/AdminChangeUserName">
<p className="mb-[20px] hover:font-bold hover:cursor-pointer underline">
(Change Username)
</p>
</Link>
);
};
return (
<div
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 p-2 rounded-lg text-center">
<div className="border-4 border-black bg-white p-2 rounded-lg text-center flex flex-col">
<p className="font-bold text-[30px]">{props.username}</p>
<Link to="/AdminChangeUserName">
<p className="mb-[20px] hover:font-bold hover:cursor-pointer underline">
(Change Username)
</p>
</Link>
{ManageUserOrMember(props.manageMember)}
<div>
<h2 className="font-bold text-[22px] mb-[20px]">
Member of these projects:

View file

@ -1,5 +1,6 @@
import { useState } from "react";
import UserInfoModal from "./UserInfoModal";
import DeleteUser from "./DeleteUser";
/**
* A list of users for admin manage users page, that sets an onClick
@ -29,7 +30,9 @@ export function UserListAdmin(props: { users: string[] }): JSX.Element {
return (
<>
<UserInfoModal
manageMember={false}
onClose={handleClose}
onDelete={() => DeleteUser}
isVisible={modalVisible}
username={username}
/>

View file

@ -2,9 +2,22 @@ import { Link } from "react-router-dom";
import BackButton from "../../Components/BackButton";
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
import { ProjectListAdmin } from "../../Components/ProjectListAdmin";
import { Project } from "../../Types/goTypes";
import GetProjects from "../../Components/GetProjects";
import { useState } from "react";
function AdminManageProjects(): JSX.Element {
const content = <></>;
const [projects, setProjects] = useState<Project[]>([]);
GetProjects({ setProjectsProp: setProjects });
const content = (
<>
<h1 className="font-bold text-[30px] mb-[20px]">Manage 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]">
<ProjectListAdmin projects={projects} />
</div>
</>
);
const buttons = (
<>

View file

@ -1,3 +1,4 @@
import BackButton from "../../Components/BackButton";
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
@ -13,13 +14,7 @@ function AdminProjectAddMember(): JSX.Element {
}}
type="button"
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
type="button"
/>
<BackButton />
</>
);

View file

@ -1,3 +1,4 @@
import BackButton from "../../Components/BackButton";
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
@ -13,13 +14,7 @@ function AdminProjectChangeUserRole(): JSX.Element {
}}
type="button"
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
type="button"
/>
<BackButton />
</>
);

View file

@ -1,3 +1,4 @@
import BackButton from "../../Components/BackButton";
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
@ -13,13 +14,7 @@ function AdminProjectManageMembers(): JSX.Element {
}}
type="button"
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
type="button"
/>
<BackButton />
</>
);

View file

@ -1,3 +1,4 @@
import BackButton from "../../Components/BackButton";
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
@ -13,13 +14,7 @@ function AdminProjectPage(): JSX.Element {
}}
type="button"
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
type="button"
/>
<BackButton />
</>
);

View file

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

View file

@ -1,3 +1,4 @@
import BackButton from "../../Components/BackButton";
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
@ -13,13 +14,7 @@ function AdminProjectViewMemberInfo(): JSX.Element {
}}
type="button"
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
type="button"
/>
<BackButton />
</>
);

View file

@ -184,6 +184,11 @@ export interface PublicUser {
userId: string;
username: string;
}
export interface UserProjectMember {
Username: string;
UserRole: string;
}
/**
* wrapper type for token
*/