Merge branch 'frontend' into gruppPP
This commit is contained in:
commit
b6d9b51865
25 changed files with 1732 additions and 990 deletions
1792
frontend/package-lock.json
generated
1792
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -11,6 +11,7 @@ import {
|
|||
NewProject,
|
||||
WeeklyReport,
|
||||
StrNameChange,
|
||||
Statistics,
|
||||
} from "../Types/goTypes";
|
||||
|
||||
/**
|
||||
|
@ -221,6 +222,15 @@ interface API {
|
|||
*/
|
||||
signReport(reportId: number, token: string): Promise<APIResponse<string>>;
|
||||
|
||||
/**
|
||||
* Unsigns a report. Keep in mind that the user which the token belongs to must be
|
||||
* the project manager of the project the report belongs to.
|
||||
*
|
||||
* @param {number} reportId The id of the report to sign
|
||||
* @param {string} token The authentication token
|
||||
*/
|
||||
unsignReport(reportId: number, token: string): Promise<APIResponse<string>>;
|
||||
|
||||
/**
|
||||
* Promotes a user to project manager within a project.
|
||||
*
|
||||
|
@ -233,6 +243,33 @@ 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>>;
|
||||
|
||||
/**
|
||||
* Deletes a WeeklyReport from the database
|
||||
* @param {number} reportId The id of the report to delete
|
||||
* @param {string} token The authentication token
|
||||
*/
|
||||
deleteWeeklyReport(
|
||||
reportId: number,
|
||||
token: string,
|
||||
): Promise<APIResponse<string>>;
|
||||
|
||||
/**
|
||||
* Retrieves the total time spent on a project for a particular user (the user is determined by the token)
|
||||
*
|
||||
* @param {string} projectName The name of the project
|
||||
* @param {string} token The authentication token
|
||||
*/
|
||||
getStatistics(
|
||||
projectName: string,
|
||||
token: string,
|
||||
): Promise<APIResponse<Statistics>>;
|
||||
}
|
||||
|
||||
/** An instance of the API */
|
||||
|
@ -844,6 +881,29 @@ export const api: API = {
|
|||
}
|
||||
},
|
||||
|
||||
async unsignReport(
|
||||
reportId: number,
|
||||
token: string,
|
||||
): Promise<APIResponse<string>> {
|
||||
try {
|
||||
const response = await fetch(`/api/unsignReport/${reportId}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer " + token,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return { success: false, message: "Failed to unsign report" };
|
||||
} else {
|
||||
return { success: true, message: "Report unsigned" };
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, message: "Failed to unsign report" };
|
||||
}
|
||||
},
|
||||
|
||||
async promoteToPm(
|
||||
userName: string,
|
||||
projectName: string,
|
||||
|
@ -874,4 +934,74 @@ 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" };
|
||||
}
|
||||
},
|
||||
|
||||
async deleteWeeklyReport(
|
||||
reportId: number,
|
||||
token: string,
|
||||
): Promise<APIResponse<string>> {
|
||||
try {
|
||||
const response = await fetch(`/api/deleteReport/${reportId}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer " + token,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return { success: false, message: "Failed to delete report" };
|
||||
} else {
|
||||
return { success: true, message: "Report deleted" };
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, message: "Failed to delete report" };
|
||||
}
|
||||
},
|
||||
async getStatistics(
|
||||
token: string,
|
||||
projectName: string,
|
||||
): Promise<APIResponse<Statistics>> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/getStatistics/?projectName=${projectName}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer " + token,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
return { success: false, message: "Failed to get statistics" };
|
||||
} else {
|
||||
const data = (await response.json()) as Statistics;
|
||||
return { success: true, data };
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, message: "Failed to get statistics" };
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -60,7 +60,7 @@ function AllTimeReportsInProject(): JSX.Element {
|
|||
</h1>
|
||||
<h1>
|
||||
<span className="font-bold">{"Signed: "}</span>
|
||||
NO
|
||||
{newWeeklyReport.signedBy ? "YES" : "NO"}
|
||||
</h1>
|
||||
</div>
|
||||
</Link>
|
||||
|
|
|
@ -3,17 +3,14 @@ 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 navigate = useNavigate();
|
||||
const [usernames, setUsernames] = useState<string[]>([]);
|
||||
const token = localStorage.getItem("accessToken") ?? "";
|
||||
|
||||
useEffect(() => {
|
||||
const getUnsignedReports = async (): Promise<void> => {
|
||||
const token = localStorage.getItem("accessToken") ?? "";
|
||||
const response = await api.getUnsignedReportsInProject(
|
||||
projectName ?? "",
|
||||
token,
|
||||
|
@ -21,13 +18,21 @@ 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]); // Include 'projectName' in the dependency array
|
||||
}, [projectName, token]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -39,8 +44,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">UserID:</span>
|
||||
<h1>{unsignedReport.userId}</h1>
|
||||
<span className="ml-6 mr-2 font-bold">Username:</span>
|
||||
<h1>{usernames[index]}</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>
|
||||
|
@ -58,7 +63,7 @@ function DisplayUserProject(): JSX.Element {
|
|||
<div className="flex">
|
||||
<div className="ml-auto flex space-x-4">
|
||||
<Link
|
||||
to={`/PMViewUnsignedReport/${projectName}/${unsignedReport.userId}/${unsignedReport.week}`}
|
||||
to={`/PMViewUnsignedReport/${projectName}/${usernames[index]}/${unsignedReport.week}`}
|
||||
>
|
||||
<h1 className="underline cursor-pointer font-bold">
|
||||
View Report
|
||||
|
|
|
@ -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 } from "react-router-dom";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import backgroundImage from "../assets/1.jpg";
|
||||
|
||||
/**
|
||||
|
@ -9,23 +9,33 @@ 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})` }}
|
||||
>
|
||||
<Link to="/your-projects">
|
||||
<div onClick={handleNavigation}>
|
||||
<img
|
||||
src="/src/assets/Logo.svg"
|
||||
alt="TTIME Logo"
|
||||
className="w-11 h-14 cursor-pointer"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="relative"
|
||||
|
|
|
@ -62,7 +62,7 @@ export default function NewWeeklyReport(): JSX.Element {
|
|||
const success = await handleNewWeeklyReport();
|
||||
if (!success) {
|
||||
alert(
|
||||
"A Time Report for this week already exists, please go to the edit page to edit it or change week number.",
|
||||
"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.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -31,8 +31,9 @@ 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,
|
||||
|
@ -62,25 +63,33 @@ export default function GetOtherUsersReport(): JSX.Element {
|
|||
void fetchUsersWeeklyReport();
|
||||
});
|
||||
|
||||
const handleSignWeeklyReport = async (): Promise<void> => {
|
||||
await api.signReport(reportId, token);
|
||||
const handleSignWeeklyReport = async (): Promise<boolean> => {
|
||||
const response = await api.signReport(reportId, token);
|
||||
if (response.success) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-[30px] font-bold">
|
||||
{" "}
|
||||
UserId: {username}'s Report
|
||||
</h1>
|
||||
<h1 className="text-[30px] font-bold"> {username}'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 handleSignWeeklyReport();
|
||||
alert("Report successfully signed!");
|
||||
navigate(-1);
|
||||
void (async (): Promise<void> => {
|
||||
const success = await handleSignWeeklyReport();
|
||||
if (!success) {
|
||||
alert("Failed to sign report!");
|
||||
return;
|
||||
}
|
||||
alert("Report successfully signed!");
|
||||
navigate(-1);
|
||||
})();
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col items-center">
|
||||
|
|
|
@ -124,6 +124,14 @@ export interface WeeklyReport {
|
|||
*/
|
||||
signedBy?: number /* int */;
|
||||
}
|
||||
export interface Statistics {
|
||||
totalDevelopmentTime: number /* int */;
|
||||
totalMeetingTime: number /* int */;
|
||||
totalAdminTime: number /* int */;
|
||||
totalOwnWorkTime: number /* int */;
|
||||
totalStudyTime: number /* int */;
|
||||
totalTestingTime: number /* int */;
|
||||
}
|
||||
export interface UpdateWeeklyReport {
|
||||
/**
|
||||
* The name of the project, as it appears in the database
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue