Merge branch 'frontend' into gruppPP
This commit is contained in:
commit
2f8e7547da
17 changed files with 397 additions and 49 deletions
|
@ -1004,8 +1004,8 @@ export const api: API = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getStatistics(
|
async getStatistics(
|
||||||
token: string,
|
|
||||||
projectName: string,
|
projectName: string,
|
||||||
|
token: string,
|
||||||
userName?: string,
|
userName?: string,
|
||||||
): Promise<APIResponse<Statistics>> {
|
): Promise<APIResponse<Statistics>> {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -37,9 +37,9 @@ function AllTimeReportsInProject(): JSX.Element {
|
||||||
<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] text-[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] text-[30px]">
|
||||||
{weeklyReports.map((newWeeklyReport, index) => (
|
{weeklyReports.map((newWeeklyReport, index) => (
|
||||||
<Link
|
<Link
|
||||||
to={`/editTimeReport/${projectName}/${newWeeklyReport.week}`}
|
to={`/editTimeReport/${projectName}/${newWeeklyReport.week}/${newWeeklyReport.signedBy ? "signed" : "unsigned"}`}
|
||||||
key={index}
|
key={index}
|
||||||
className="border-b-2 border-black w-full"
|
className="border-b-2 border-black w-full cursor-pointer hover:font-extrabold"
|
||||||
>
|
>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<h1>
|
<h1>
|
||||||
|
|
|
@ -39,9 +39,9 @@ function AllTimeReportsInProject(): JSX.Element {
|
||||||
<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] text-[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] text-[30px]">
|
||||||
{weeklyReports.map((newWeeklyReport, index) => (
|
{weeklyReports.map((newWeeklyReport, index) => (
|
||||||
<Link
|
<Link
|
||||||
to={`/editOthersTR/${projectName}/${username}/${newWeeklyReport.week}`}
|
to={`/editOthersTR/${projectName}/${username}/${newWeeklyReport.week}/${newWeeklyReport.signedBy ? "signed" : "unsigned"}`}
|
||||||
key={index}
|
key={index}
|
||||||
className="border-b-2 border-black w-full"
|
className="border-b-2 border-black w-full hover:font-extrabold"
|
||||||
>
|
>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<h1>
|
<h1>
|
||||||
|
|
|
@ -65,7 +65,7 @@ function DisplayUserProject(): JSX.Element {
|
||||||
<Link
|
<Link
|
||||||
to={`/PMViewUnsignedReport/${projectName}/${usernames[index]}/${unsignedReport.week}`}
|
to={`/PMViewUnsignedReport/${projectName}/${usernames[index]}/${unsignedReport.week}`}
|
||||||
>
|
>
|
||||||
<h1 className="underline cursor-pointer font-bold">
|
<h1 className="cursor-pointer font-bold hover:font-extrabold hover:underline">
|
||||||
View Report
|
View Report
|
||||||
</h1>
|
</h1>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -45,7 +45,7 @@ function DisplayUserProject(): JSX.Element {
|
||||||
onClick={() => void handleProjectClick(project.name)}
|
onClick={() => void handleProjectClick(project.name)}
|
||||||
key={project.id}
|
key={project.id}
|
||||||
>
|
>
|
||||||
<h1 className="font-bold underline text-[30px] cursor-pointer">
|
<h1 className="font-bold hover:underline text-[30px] cursor-pointer hover:font-extrabold">
|
||||||
{project.name}
|
{project.name}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,12 +18,13 @@ export default function GetWeeklyReport(): JSX.Element {
|
||||||
const [testingTime, setTestingTime] = useState(0);
|
const [testingTime, setTestingTime] = useState(0);
|
||||||
|
|
||||||
const token = localStorage.getItem("accessToken") ?? "";
|
const token = localStorage.getItem("accessToken") ?? "";
|
||||||
const { projectName, fetchedWeek } = useParams<{
|
const { projectName, fetchedWeek, signedOrUnsigned } = useParams<{
|
||||||
projectName: string;
|
projectName: string;
|
||||||
fetchedWeek: string;
|
fetchedWeek: string;
|
||||||
|
signedOrUnsigned: string;
|
||||||
}>();
|
}>();
|
||||||
const username = localStorage.getItem("userName") ?? "";
|
const username = localStorage.getItem("userName") ?? "";
|
||||||
console.log(projectName, fetchedWeek);
|
console.log(projectName, fetchedWeek, signedOrUnsigned);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchWeeklyReport = async (): Promise<void> => {
|
const fetchWeeklyReport = async (): Promise<void> => {
|
||||||
|
@ -59,7 +60,7 @@ export default function GetWeeklyReport(): JSX.Element {
|
||||||
};
|
};
|
||||||
|
|
||||||
void fetchWeeklyReport();
|
void fetchWeeklyReport();
|
||||||
}, [projectName, fetchedWeek, token]);
|
}, [projectName, fetchedWeek, signedOrUnsigned, token]);
|
||||||
|
|
||||||
const handleUpdateWeeklyReport = async (): Promise<void> => {
|
const handleUpdateWeeklyReport = async (): Promise<void> => {
|
||||||
const updateWeeklyReport: UpdateWeeklyReport = {
|
const updateWeeklyReport: UpdateWeeklyReport = {
|
||||||
|
@ -139,6 +140,12 @@ export default function GetWeeklyReport(): JSX.Element {
|
||||||
)
|
)
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}}
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (signedOrUnsigned === "signed") {
|
||||||
|
alert("You cannot edit a signed report.");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
readOnly={signedOrUnsigned === "signed"}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -168,6 +175,12 @@ export default function GetWeeklyReport(): JSX.Element {
|
||||||
)
|
)
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}}
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (signedOrUnsigned === "signed") {
|
||||||
|
alert("You cannot edit a signed report.");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
readOnly={signedOrUnsigned === "signed"}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -197,6 +210,12 @@ export default function GetWeeklyReport(): JSX.Element {
|
||||||
)
|
)
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}}
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (signedOrUnsigned === "signed") {
|
||||||
|
alert("You cannot edit a signed report.");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
readOnly={signedOrUnsigned === "signed"}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -226,6 +245,12 @@ export default function GetWeeklyReport(): JSX.Element {
|
||||||
)
|
)
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}}
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (signedOrUnsigned === "signed") {
|
||||||
|
alert("You cannot edit a signed report.");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
readOnly={signedOrUnsigned === "signed"}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -255,6 +280,12 @@ export default function GetWeeklyReport(): JSX.Element {
|
||||||
)
|
)
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}}
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (signedOrUnsigned === "signed") {
|
||||||
|
alert("You cannot edit a signed report.");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
readOnly={signedOrUnsigned === "signed"}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -284,11 +315,18 @@ export default function GetWeeklyReport(): JSX.Element {
|
||||||
)
|
)
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}}
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (signedOrUnsigned === "signed") {
|
||||||
|
alert("You cannot edit a signed report.");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
readOnly={signedOrUnsigned === "signed"}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{signedOrUnsigned !== "signed" && (
|
||||||
<Button
|
<Button
|
||||||
text="Submit changes"
|
text="Submit changes"
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
|
@ -296,6 +334,7 @@ export default function GetWeeklyReport(): JSX.Element {
|
||||||
}}
|
}}
|
||||||
type="submit"
|
type="submit"
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { WeeklyReport } from "../Types/goTypes";
|
import { WeeklyReport } from "../Types/goTypes";
|
||||||
import { api } from "../API/API";
|
import { api } from "../API/API";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams, useNavigate } from "react-router-dom";
|
||||||
|
import Button from "./Button";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the component for editing a weekly report.
|
* Renders the component for editing a weekly report.
|
||||||
|
@ -17,11 +18,14 @@ export default function OtherUsersTR(): JSX.Element {
|
||||||
const [ownWorkTime, setOwnWorkTime] = useState(0);
|
const [ownWorkTime, setOwnWorkTime] = useState(0);
|
||||||
const [studyTime, setStudyTime] = useState(0);
|
const [studyTime, setStudyTime] = useState(0);
|
||||||
const [testingTime, setTestingTime] = useState(0);
|
const [testingTime, setTestingTime] = useState(0);
|
||||||
|
const [reportId, setReportId] = useState(0);
|
||||||
|
|
||||||
const token = localStorage.getItem("accessToken") ?? "";
|
const token = localStorage.getItem("accessToken") ?? "";
|
||||||
const { projectName } = useParams();
|
const { projectName } = useParams();
|
||||||
const { username } = useParams();
|
const { username } = useParams();
|
||||||
const { fetchedWeek } = useParams();
|
const { fetchedWeek } = useParams();
|
||||||
|
const { signedOrUnsigned } = useParams();
|
||||||
|
console.log(projectName, username, fetchedWeek, signedOrUnsigned);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchUsersWeeklyReport = async (): Promise<void> => {
|
const fetchUsersWeeklyReport = async (): Promise<void> => {
|
||||||
|
@ -45,6 +49,7 @@ export default function OtherUsersTR(): JSX.Element {
|
||||||
studyTime: 0,
|
studyTime: 0,
|
||||||
testingTime: 0,
|
testingTime: 0,
|
||||||
};
|
};
|
||||||
|
setReportId(report.reportId);
|
||||||
setWeek(report.week);
|
setWeek(report.week);
|
||||||
setDevelopmentTime(report.developmentTime);
|
setDevelopmentTime(report.developmentTime);
|
||||||
setMeetingTime(report.meetingTime);
|
setMeetingTime(report.meetingTime);
|
||||||
|
@ -60,6 +65,27 @@ export default function OtherUsersTR(): JSX.Element {
|
||||||
void fetchUsersWeeklyReport();
|
void fetchUsersWeeklyReport();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleUnsignWeeklyReport = async (): Promise<boolean> => {
|
||||||
|
const response = await api.unsignReport(reportId, token);
|
||||||
|
console.log(response);
|
||||||
|
console.log(reportId);
|
||||||
|
if (response.success) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteWeeklyReport = async (): Promise<boolean> => {
|
||||||
|
const response = await api.deleteWeeklyReport(reportId, token);
|
||||||
|
console.log(response);
|
||||||
|
if (response.success) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1 className="text-[30px] font-bold">{username}'s Report</h1>
|
<h1 className="text-[30px] font-bold">{username}'s Report</h1>
|
||||||
|
@ -153,6 +179,48 @@ export default function OtherUsersTR(): JSX.Element {
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<div className="flex space-x-4">
|
||||||
|
{signedOrUnsigned === "signed" && (
|
||||||
|
<Button
|
||||||
|
text="Unsign Report"
|
||||||
|
onClick={(): void => {
|
||||||
|
void (async (): Promise<void> => {
|
||||||
|
const success = await handleUnsignWeeklyReport();
|
||||||
|
if (success) {
|
||||||
|
alert("Report successfully unsigned!");
|
||||||
|
navigate(-1);
|
||||||
|
} else {
|
||||||
|
alert("Failed to unsign report");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}}
|
||||||
|
type={"button"}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
text="Delete Time Report"
|
||||||
|
onClick={(): void => {
|
||||||
|
void (async (): Promise<void> => {
|
||||||
|
const confirmDelete = window.confirm(
|
||||||
|
"Are you sure you want to delete this report? This action cannot be undone.",
|
||||||
|
);
|
||||||
|
if (!confirmDelete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const success = await handleDeleteWeeklyReport();
|
||||||
|
if (success) {
|
||||||
|
alert("Report successfully deleted!");
|
||||||
|
navigate(-1);
|
||||||
|
} else {
|
||||||
|
alert("Failed to delete report");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}}
|
||||||
|
type={"button"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -8,22 +8,22 @@ function PMProjectMenu(): JSX.Element {
|
||||||
<h1 className="font-bold text-[30px] mb-[20px]">{projectName}</h1>
|
<h1 className="font-bold text-[30px] mb-[20px]">{projectName}</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-[5vh] 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-[5vh] p-[30px]">
|
||||||
<Link to={`/timeReports/${projectName}/`}>
|
<Link to={`/timeReports/${projectName}/`}>
|
||||||
<h1 className="font-bold underline text-[30px] cursor-pointer">
|
<h1 className="font-bold hover:underline text-[30px] cursor-pointer hover:font-extrabold">
|
||||||
Your Time Reports
|
Your Time Reports
|
||||||
</h1>
|
</h1>
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={`/newTimeReport/${projectName}`}>
|
<Link to={`/newTimeReport/${projectName}`}>
|
||||||
<h1 className="font-bold underline text-[30px] cursor-pointer">
|
<h1 className="font-bold hover:underline text-[30px] cursor-pointer hover:font-extrabold">
|
||||||
New Time Report
|
New Time Report
|
||||||
</h1>
|
</h1>
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={`/projectMembers/${projectName}`}>
|
<Link to={`/projectMembers/${projectName}`}>
|
||||||
<h1 className="font-bold underline text-[30px] cursor-pointer">
|
<h1 className="font-bold hover:underline text-[30px] cursor-pointer hover:font-extrabold">
|
||||||
Statistics
|
Statistics
|
||||||
</h1>
|
</h1>
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={`/unsignedReports/${projectName}`}>
|
<Link to={`/unsignedReports/${projectName}`}>
|
||||||
<h1 className="font-bold underline text-[30px] cursor-pointer">
|
<h1 className="font-bold hover:underline text-[30px] cursor-pointer hover:font-extrabold">
|
||||||
Unsigned Time Reports
|
Unsigned Time Reports
|
||||||
</h1>
|
</h1>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Link, useParams } from "react-router-dom";
|
import { Link, useParams } from "react-router-dom";
|
||||||
import GetUsersInProject, { ProjectMember } from "./GetUsersInProject";
|
import GetUsersInProject, { ProjectMember } from "./GetUsersInProject";
|
||||||
|
import { api } from "../API/API";
|
||||||
|
|
||||||
function ProjectMembers(): JSX.Element {
|
function ProjectMembers(): JSX.Element {
|
||||||
const { projectName } = useParams();
|
const { projectName } = useParams();
|
||||||
|
@ -11,13 +12,32 @@ function ProjectMembers(): JSX.Element {
|
||||||
setUsersProp: setProjectMembers,
|
setUsersProp: setProjectMembers,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleUserDeleteClick = async (username: string): Promise<void> => {
|
||||||
|
const token = localStorage.getItem("accessToken") ?? "";
|
||||||
|
const response = await api.removeUserFromProject(
|
||||||
|
username,
|
||||||
|
projectName ?? "",
|
||||||
|
token,
|
||||||
|
);
|
||||||
|
console.log(response.data);
|
||||||
|
|
||||||
|
// Remove the deleted user from the state
|
||||||
|
setProjectMembers((prevMembers) =>
|
||||||
|
prevMembers.filter((member) => member.Username !== username),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1 className="font-bold text-[30px] mb-[20px]">
|
<h1 className="font-bold text-[30px] mb-[20px]">
|
||||||
All Members In: {projectName}{" "}
|
All Members In: {projectName}{" "}
|
||||||
</h1>
|
</h1>
|
||||||
<div className="border-4 border-black bg-white flex flex-col items-center justify-center min-h-[65vh] h-fit w-[70vw] rounded-3xl content-center overflow-scroll space-y-[10vh] p-[30px] text-[20px]">
|
<div className="border-4 border-black bg-white flex flex-col items-center justify-center min-h-[65vh] h-fit w-[70vw] rounded-3xl content-center overflow-scroll space-y-[10vh] p-[30px] text-[20px]">
|
||||||
{projectMembers.map((projectMember: ProjectMember, index: number) => (
|
{projectMembers.map((projectMember: ProjectMember, index: number) => {
|
||||||
|
if (projectMember.Username === "admin") {
|
||||||
|
return null; // Skip rendering for admin user
|
||||||
|
}
|
||||||
|
return (
|
||||||
<h1 key={index} className="border-b-2 border-black w-full">
|
<h1 key={index} className="border-b-2 border-black w-full">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
|
@ -27,10 +47,24 @@ function ProjectMembers(): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="ml-auto flex space-x-4">
|
<div className="ml-auto flex space-x-4">
|
||||||
|
{projectMember.Username !==
|
||||||
|
localStorage.getItem("username") && (
|
||||||
|
<h1
|
||||||
|
className="cursor-pointer font-bold hover:font-extrabold hover:underline"
|
||||||
|
onClick={() => {
|
||||||
|
confirm(
|
||||||
|
"Are you sure you want to delete this user? This action cannot be undone.",
|
||||||
|
) &&
|
||||||
|
void handleUserDeleteClick(projectMember.Username);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete User
|
||||||
|
</h1>
|
||||||
|
)}
|
||||||
<Link
|
<Link
|
||||||
to={`/otherUsersTimeReports/${projectName}/${projectMember.Username}`}
|
to={`/otherUsersTimeReports/${projectName}/${projectMember.Username}`}
|
||||||
>
|
>
|
||||||
<h1 className="underline cursor-pointer font-bold">
|
<h1 className="cursor-pointer font-bold hover:font-extrabold hover:underline">
|
||||||
View Reports
|
View Reports
|
||||||
</h1>
|
</h1>
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -38,7 +72,8 @@ function ProjectMembers(): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</h1>
|
</h1>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,6 +14,7 @@ export default function TimePerRole(): JSX.Element {
|
||||||
const [own_work, setOwnWork] = useState<number>(0);
|
const [own_work, setOwnWork] = useState<number>(0);
|
||||||
const [study, setStudy] = useState<number>(0);
|
const [study, setStudy] = useState<number>(0);
|
||||||
const [testing, setTesting] = useState<number>(0);
|
const [testing, setTesting] = useState<number>(0);
|
||||||
|
const total = development + meeting + admin + own_work + study + testing;
|
||||||
|
|
||||||
const token = localStorage.getItem("accessToken") ?? "";
|
const token = localStorage.getItem("accessToken") ?? "";
|
||||||
const { projectName } = useParams();
|
const { projectName } = useParams();
|
||||||
|
@ -49,7 +50,7 @@ export default function TimePerRole(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1 className="font-bold text-[30px] mb-[20px]">
|
<h1 className="font-bold text-[30px] mb-[20px]">
|
||||||
Total Time Per Activity In: {projectName}{" "}
|
Total Time Per Activity For All Members In: {projectName}{" "}
|
||||||
</h1>
|
</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">
|
<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="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
|
@ -129,6 +130,12 @@ export default function TimePerRole(): JSX.Element {
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr className="h-[10vh] font-bold font-">
|
||||||
|
<td>In Total:</td>
|
||||||
|
<td>
|
||||||
|
<h1>{total}</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,12 +16,12 @@ function UserProjectMenu(): JSX.Element {
|
||||||
<h1 className="font-bold text-[30px] mb-[20px]">{projectName}</h1>
|
<h1 className="font-bold text-[30px] mb-[20px]">{projectName}</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={`/timeReports/${projectName}/`}>
|
<Link to={`/timeReports/${projectName}/`}>
|
||||||
<h1 className="font-bold underline text-[30px] cursor-pointer">
|
<h1 className="font-bold hover:underline text-[30px] cursor-pointer hover:font-extrabold">
|
||||||
Your Time Reports
|
Your Time Reports
|
||||||
</h1>
|
</h1>
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={`/newTimeReport/${projectName}`}>
|
<Link to={`/newTimeReport/${projectName}`}>
|
||||||
<h1 className="font-bold underline text-[30px] cursor-pointer">
|
<h1 className="font-bold hover:underline text-[30px] cursor-pointer hover:font-extrabold">
|
||||||
New Time Report
|
New Time Report
|
||||||
</h1>
|
</h1>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
150
frontend/src/Components/UserStatistics.tsx
Normal file
150
frontend/src/Components/UserStatistics.tsx
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { api } from "../API/API";
|
||||||
|
import { Statistics } from "../Types/goTypes";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the component for showing total time per role in a project.
|
||||||
|
* @returns JSX.Element
|
||||||
|
*/
|
||||||
|
export default function UserStatistics(): 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 total = development + meeting + admin + own_work + study + testing;
|
||||||
|
|
||||||
|
const token = localStorage.getItem("accessToken") ?? "";
|
||||||
|
const { projectName } = useParams();
|
||||||
|
const { username } = useParams();
|
||||||
|
|
||||||
|
const fetchTimePerActivity = async (): Promise<void> => {
|
||||||
|
const response = await api.getStatistics(
|
||||||
|
projectName ?? "",
|
||||||
|
token,
|
||||||
|
username ?? "",
|
||||||
|
);
|
||||||
|
{
|
||||||
|
if (response.success) {
|
||||||
|
const statistics: Statistics = response.data ?? {
|
||||||
|
totalDevelopmentTime: 0,
|
||||||
|
totalMeetingTime: 0,
|
||||||
|
totalAdminTime: 0,
|
||||||
|
totalOwnWorkTime: 0,
|
||||||
|
totalStudyTime: 0,
|
||||||
|
totalTestingTime: 0,
|
||||||
|
};
|
||||||
|
setDevelopment(statistics.totalDevelopmentTime);
|
||||||
|
setMeeting(statistics.totalMeetingTime);
|
||||||
|
setAdmin(statistics.totalAdminTime);
|
||||||
|
setOwnWork(statistics.totalOwnWorkTime);
|
||||||
|
setStudy(statistics.totalStudyTime);
|
||||||
|
setTesting(statistics.totalTestingTime);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to fetch weekly report:", response.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
void fetchTimePerActivity();
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1 className="font-bold text-[30px] mb-[20px]">
|
||||||
|
Total Time In: {projectName}{" "}
|
||||||
|
</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">
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<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="string"
|
||||||
|
className="border-2 border-black rounded-md text-center w-1/2"
|
||||||
|
value={development}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="h-[10vh]">
|
||||||
|
<td>Meeting</td>
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
type="string"
|
||||||
|
className="border-2 border-black rounded-md text-center w-1/2"
|
||||||
|
value={meeting}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="h-[10vh]">
|
||||||
|
<td>Administration</td>
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
type="string"
|
||||||
|
className="border-2 border-black rounded-md text-center w-1/2"
|
||||||
|
value={admin}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="h-[10vh]">
|
||||||
|
<td>Own Work</td>
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
type="string"
|
||||||
|
className="border-2 border-black rounded-md text-center w-1/2"
|
||||||
|
value={own_work}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="h-[10vh]">
|
||||||
|
<td>Studies</td>
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
type="string"
|
||||||
|
className="border-2 border-black rounded-md text-center w-1/2"
|
||||||
|
value={study}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="h-[10vh]">
|
||||||
|
<td>Testing</td>
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
type="string"
|
||||||
|
className="border-2 border-black rounded-md text-center w-1/2"
|
||||||
|
value={testing}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="h-[10vh] font-bold font-">
|
||||||
|
<td>In Total:</td>
|
||||||
|
<td>
|
||||||
|
<h1>{total}</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,8 +1,12 @@
|
||||||
import BasicWindow from "../../Components/BasicWindow";
|
import BasicWindow from "../../Components/BasicWindow";
|
||||||
import BackButton from "../../Components/BackButton";
|
import BackButton from "../../Components/BackButton";
|
||||||
import AllTimeReportsInProjectOtherUser from "../../Components/AllTimeReportsInProjectOtherUser";
|
import AllTimeReportsInProjectOtherUser from "../../Components/AllTimeReportsInProjectOtherUser";
|
||||||
|
import Button from "../../Components/Button";
|
||||||
|
import { useParams, Link } from "react-router-dom";
|
||||||
|
|
||||||
function PMOtherUsersTR(): JSX.Element {
|
function PMOtherUsersTR(): JSX.Element {
|
||||||
|
const { projectName } = useParams();
|
||||||
|
const { username } = useParams();
|
||||||
const content = (
|
const content = (
|
||||||
<>
|
<>
|
||||||
<AllTimeReportsInProjectOtherUser />
|
<AllTimeReportsInProjectOtherUser />
|
||||||
|
@ -11,6 +15,15 @@ function PMOtherUsersTR(): JSX.Element {
|
||||||
|
|
||||||
const buttons = (
|
const buttons = (
|
||||||
<>
|
<>
|
||||||
|
<Link to={`/viewStatistics/${projectName}/${username}`}>
|
||||||
|
<Button
|
||||||
|
text={`Statistics: ${username}`}
|
||||||
|
onClick={(): void => {
|
||||||
|
return;
|
||||||
|
}}
|
||||||
|
type={"button"}
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -16,7 +16,7 @@ function PMProjectMembers(): JSX.Element {
|
||||||
<>
|
<>
|
||||||
<Link to={`/PMtimeactivity/${projectName}`}>
|
<Link to={`/PMtimeactivity/${projectName}`}>
|
||||||
<Button
|
<Button
|
||||||
text="Time / Activity"
|
text="Statistics"
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
return;
|
return;
|
||||||
}}
|
}}
|
||||||
|
|
20
frontend/src/Pages/UserPages/UserViewStatistics.tsx
Normal file
20
frontend/src/Pages/UserPages/UserViewStatistics.tsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import BackButton from "../../Components/BackButton";
|
||||||
|
import BasicWindow from "../../Components/BasicWindow";
|
||||||
|
import UserStatistics from "../../Components/UserStatistics";
|
||||||
|
|
||||||
|
function UserNewTimeReportPage(): JSX.Element {
|
||||||
|
const content = (
|
||||||
|
<>
|
||||||
|
<UserStatistics />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const buttons = (
|
||||||
|
<>
|
||||||
|
<BackButton />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return <BasicWindow content={content} buttons={buttons} />;
|
||||||
|
}
|
||||||
|
export default UserNewTimeReportPage;
|
|
@ -1,10 +1,12 @@
|
||||||
import BasicWindow from "../../Components/BasicWindow";
|
import BasicWindow from "../../Components/BasicWindow";
|
||||||
import BackButton from "../../Components/BackButton";
|
import BackButton from "../../Components/BackButton";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams, Link } from "react-router-dom";
|
||||||
import AllTimeReportsInProject from "../../Components/AllTimeReportsInProject";
|
import AllTimeReportsInProject from "../../Components/AllTimeReportsInProject";
|
||||||
|
import Button from "../../Components/Button";
|
||||||
|
|
||||||
function UserViewTimeReportsPage(): JSX.Element {
|
function UserViewTimeReportsPage(): JSX.Element {
|
||||||
const { projectName } = useParams();
|
const { projectName } = useParams();
|
||||||
|
const username = localStorage.getItem("username");
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<>
|
<>
|
||||||
|
@ -17,6 +19,15 @@ function UserViewTimeReportsPage(): JSX.Element {
|
||||||
|
|
||||||
const buttons = (
|
const buttons = (
|
||||||
<>
|
<>
|
||||||
|
<Link to={`/viewStatistics/${projectName}/${username}`}>
|
||||||
|
<Button
|
||||||
|
text="Statistics"
|
||||||
|
onClick={(): void => {
|
||||||
|
return;
|
||||||
|
}}
|
||||||
|
type={"button"}
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import AdminMenuPage from "./Pages/AdminPages/AdminMenuPage.tsx";
|
||||||
import UserEditTimeReportPage from "./Pages/UserPages/UserEditTimeReportPage.tsx";
|
import UserEditTimeReportPage from "./Pages/UserPages/UserEditTimeReportPage.tsx";
|
||||||
import UserNewTimeReportPage from "./Pages/UserPages/UserNewTimeReportPage.tsx";
|
import UserNewTimeReportPage from "./Pages/UserPages/UserNewTimeReportPage.tsx";
|
||||||
import UserViewTimeReportsPage from "./Pages/UserPages/UserViewTimeReportsPage.tsx";
|
import UserViewTimeReportsPage from "./Pages/UserPages/UserViewTimeReportsPage.tsx";
|
||||||
|
import UserViewStatistics from "./Pages/UserPages/UserViewStatistics.tsx";
|
||||||
import PMChangeRole from "./Pages/ProjectManagerPages/PMChangeRole.tsx";
|
import PMChangeRole from "./Pages/ProjectManagerPages/PMChangeRole.tsx";
|
||||||
import PMOtherUsersTR from "./Pages/ProjectManagerPages/PMOtherUsersTR.tsx";
|
import PMOtherUsersTR from "./Pages/ProjectManagerPages/PMOtherUsersTR.tsx";
|
||||||
import PMProjectMembers from "./Pages/ProjectManagerPages/PMProjectMembers.tsx";
|
import PMProjectMembers from "./Pages/ProjectManagerPages/PMProjectMembers.tsx";
|
||||||
|
@ -54,9 +55,13 @@ const router = createBrowserRouter([
|
||||||
element: <UserViewTimeReportsPage />,
|
element: <UserViewTimeReportsPage />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/editTimeReport/:projectName/:fetchedWeek",
|
path: "/editTimeReport/:projectName/:fetchedWeek/:signedOrUnsigned",
|
||||||
element: <UserEditTimeReportPage />,
|
element: <UserEditTimeReportPage />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/viewStatistics/:projectName/:username",
|
||||||
|
element: <UserViewStatistics />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/changeRole/:projectName/:username",
|
path: "/changeRole/:projectName/:username",
|
||||||
element: <PMChangeRole />,
|
element: <PMChangeRole />,
|
||||||
|
@ -66,7 +71,7 @@ const router = createBrowserRouter([
|
||||||
element: <PMOtherUsersTR />,
|
element: <PMOtherUsersTR />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/editOthersTR/:projectName/:username/:fetchedWeek",
|
path: "/editOthersTR/:projectName/:username/:fetchedWeek/:signedOrUnsigned",
|
||||||
element: <PMViewOtherUsersTR />,
|
element: <PMViewOtherUsersTR />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue