diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index e8566b6..6078513 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -46,6 +46,7 @@ interface API { username: string, token: string, ): Promise>; + /** Gets a project from id*/ getProject(id: number): Promise>; } @@ -149,7 +150,10 @@ export const api: API = { } }, - async getUserProjects(token: string): Promise> { + async getUserProjects( + username: string, + token: string, + ): Promise> { try { const response = await fetch("/api/getUserProjects", { method: "GET", @@ -157,6 +161,7 @@ export const api: API = { "Content-Type": "application/json", Authorization: "Bearer " + token, }, + body: JSON.stringify({ username }), }); if (!response.ok) { diff --git a/frontend/src/Components/AddProject.tsx b/frontend/src/Components/AddProject.tsx new file mode 100644 index 0000000..45814e3 --- /dev/null +++ b/frontend/src/Components/AddProject.tsx @@ -0,0 +1,94 @@ +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) => { + 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 ( +
+
+
{ + e.preventDefault(); + CreateProject({ name: name, description: description }); + }} + > + TTIME Logo +

+ Create a new project +

+ { + setName(e.target.value); + }} + /> + { + setDescription(e.target.value); + }} + /> +
+
+ +

+
+
+ ); +} + +export default AddProject; diff --git a/frontend/src/Components/BasicWindow.tsx b/frontend/src/Components/BasicWindow.tsx index 1835d6a..d5fd3b6 100644 --- a/frontend/src/Components/BasicWindow.tsx +++ b/frontend/src/Components/BasicWindow.tsx @@ -2,17 +2,15 @@ import Header from "./Header"; import Footer from "./Footer"; function BasicWindow({ - username, content, buttons, }: { - username: string; content: React.ReactNode; buttons: React.ReactNode; }): JSX.Element { return (
-
+
{content}
{buttons}
diff --git a/frontend/src/Components/EditWeeklyReport.tsx b/frontend/src/Components/EditWeeklyReport.tsx new file mode 100644 index 0000000..b0e8771 --- /dev/null +++ b/frontend/src/Components/EditWeeklyReport.tsx @@ -0,0 +1,247 @@ +import { useState, useEffect } from "react"; +import { NewWeeklyReport } from "../Types/goTypes"; +import { api } from "../API/API"; +import { useNavigate } from "react-router-dom"; +import Button from "./Button"; + +export default function GetWeeklyReport(): JSX.Element { + const [projectName, setProjectName] = useState(""); + const [week, setWeek] = useState(0); + const [developmentTime, setDevelopmentTime] = useState(0); + const [meetingTime, setMeetingTime] = useState(0); + const [adminTime, setAdminTime] = useState(0); + const [ownWorkTime, setOwnWorkTime] = useState(0); + const [studyTime, setStudyTime] = useState(0); + const [testingTime, setTestingTime] = useState(0); + + const token = localStorage.getItem("accessToken") ?? ""; + const username = localStorage.getItem("username") ?? ""; + + useEffect(() => { + const fetchWeeklyReport = async (): Promise => { + const response = await api.getWeeklyReport( + username, + projectName, + week.toString(), + token, + ); + + if (response.success) { + const report: NewWeeklyReport = response.data ?? { + projectName: "", + week: 0, + developmentTime: 0, + meetingTime: 0, + adminTime: 0, + ownWorkTime: 0, + studyTime: 0, + testingTime: 0, + }; + setProjectName(report.projectName); + setWeek(report.week); + setDevelopmentTime(report.developmentTime); + setMeetingTime(report.meetingTime); + setAdminTime(report.adminTime); + setOwnWorkTime(report.ownWorkTime); + setStudyTime(report.studyTime); + setTestingTime(report.testingTime); + } else { + console.error("Failed to fetch weekly report:", response.message); + } + }; + + void fetchWeeklyReport(); + }, [projectName, token, username, week]); + + const handleNewWeeklyReport = async (): Promise => { + const newWeeklyReport: NewWeeklyReport = { + projectName, + week, + developmentTime, + meetingTime, + adminTime, + ownWorkTime, + studyTime, + testingTime, + }; + + await api.submitWeeklyReport(newWeeklyReport, token); + }; + + const navigate = useNavigate(); + + return ( + <> +
+
{ + if (week === 0) { + alert("Please enter a week number"); + e.preventDefault(); + return; + } + e.preventDefault(); + void handleNewWeeklyReport(); + navigate("/project"); + }} + > +
+ { + const weekNumber = parseInt(e.target.value.split("-W")[1]); + setWeek(weekNumber); + }} + onKeyDown={(event) => { + event.preventDefault(); + }} + onPaste={(event) => { + event.preventDefault(); + }} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Activity + + Total Time (min) +
Development + { + setDevelopmentTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Meeting + { + setMeetingTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Administration + { + setAdminTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Own Work + { + setOwnWorkTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Studies + { + setStudyTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Testing + { + setTestingTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
+
+
+
+ + ); +} diff --git a/frontend/src/Components/Header.tsx b/frontend/src/Components/Header.tsx index ba0a939..819c5de 100644 --- a/frontend/src/Components/Header.tsx +++ b/frontend/src/Components/Header.tsx @@ -1,11 +1,11 @@ import { useState } from "react"; import { Link } from "react-router-dom"; -function Header({ username }: { username: string }): JSX.Element { +function Header(): JSX.Element { const [isOpen, setIsOpen] = useState(false); const handleLogout = (): void => { - // Add any logout logic here + localStorage.clear(); }; return ( @@ -31,7 +31,7 @@ function Header({ username }: { username: string }): JSX.Element { }} > {isOpen && ( diff --git a/frontend/src/Components/LoginCheck.tsx b/frontend/src/Components/LoginCheck.tsx index 3658cbf..ce7d52c 100644 --- a/frontend/src/Components/LoginCheck.tsx +++ b/frontend/src/Components/LoginCheck.tsx @@ -10,17 +10,22 @@ function LoginCheck(props: { username: string; password: string; setAuthority: Dispatch>; -}): number { +}): void { const user: NewUser = { username: props.username, password: props.password, }; + + localStorage.clear(); + api .login(user) .then((response: APIResponse) => { if (response.success) { if (response.data !== undefined) { const token = response.data; + localStorage.setItem("accessToken", token); + localStorage.setItem("username", props.username); //TODO: change so that it checks for user type (admin, user, pm) instead if (token !== "" && props.username === "admin") { props.setAuthority((prevAuth) => { @@ -42,14 +47,12 @@ function LoginCheck(props: { console.error("Token was undefined"); } } else { - console.error("Token could not be fetched"); + console.error("Token could not be fetched/No such user"); } }) .catch((error) => { console.error("An error occurred during login:", error); }); - - return 0; } export default LoginCheck; diff --git a/frontend/src/Components/TimeReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx similarity index 77% rename from frontend/src/Components/TimeReport.tsx rename to frontend/src/Components/NewWeeklyReport.tsx index e7eb5b7..0a84b48 100644 --- a/frontend/src/Components/TimeReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -1,50 +1,51 @@ -import { useState } from "react"; +import { useState, useContext } from "react"; +import type { NewWeeklyReport } from "../Types/goTypes"; import { api } from "../API/API"; import { useNavigate } from "react-router-dom"; import Button from "./Button"; -import { NewWeeklyReport } from "../Types/goTypes"; +import { ProjectNameContext } from "../Pages/YourProjectsPage"; -export default function NewTimeReport(): JSX.Element { - const [projectName, setProjectName] = useState("projectName"); // TODO: Get from backend - const [week, setWeek] = useState(NaN); - const [development, setDevelopment] = useState(NaN); - const [meeting, setMeeting] = useState(NaN); - const [administration, setAdministration] = useState(NaN); - const [ownwork, setOwnWork] = useState(NaN); - const [studies, setStudies] = useState(NaN); - const [testing, setTesting] = useState(NaN); +export default function NewWeeklyReport(): JSX.Element { + const [week, setWeek] = useState(0); + const [developmentTime, setDevelopmentTime] = useState(0); + const [meetingTime, setMeetingTime] = useState(0); + const [adminTime, setAdminTime] = useState(0); + const [ownWorkTime, setOwnWorkTime] = useState(0); + const [studyTime, setStudyTime] = useState(0); + const [testingTime, setTestingTime] = useState(0); - const handleNewTimeReport = async (): Promise => { - const newTimeReport: NewWeeklyReport = { + const projectName = useContext(ProjectNameContext); + const token = localStorage.getItem("accessToken") ?? ""; + + const handleNewWeeklyReport = async (): Promise => { + const newWeeklyReport: NewWeeklyReport = { projectName, week, - developmentTime: development, - meetingTime: meeting, - adminTime: administration, - ownWorkTime: ownwork, - studyTime: studies, - testingTime: testing, + developmentTime, + meetingTime, + adminTime, + ownWorkTime, + studyTime, + testingTime, }; - await Promise.resolve(); - await api.submitWeeklyReport(newTimeReport, "token"); + + await api.submitWeeklyReport(newWeeklyReport, token); }; const navigate = useNavigate(); - setProjectName("Something Reasonable"); // This should obviously not be used here - return ( <>
{ - if (!week) { + if (week === 0) { alert("Please enter a week number"); e.preventDefault(); return; } e.preventDefault(); - void handleNewTimeReport(); + void handleNewWeeklyReport(); navigate("/project"); }} > @@ -83,9 +84,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={development} + value={developmentTime} onChange={(e) => { - setDevelopment(parseInt(e.target.value)); + setDevelopmentTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -102,9 +103,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={meeting} + value={meetingTime} onChange={(e) => { - setMeeting(parseInt(e.target.value)); + setMeetingTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -121,9 +122,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={administration} + value={adminTime} onChange={(e) => { - setAdministration(parseInt(e.target.value)); + setAdminTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -140,9 +141,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={ownwork} + value={ownWorkTime} onChange={(e) => { - setOwnWork(parseInt(e.target.value)); + setOwnWorkTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -159,9 +160,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={studies} + value={studyTime} onChange={(e) => { - setStudies(parseInt(e.target.value)); + setStudyTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -178,9 +179,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={testing} + value={testingTime} onChange={(e) => { - setTesting(parseInt(e.target.value)); + setTestingTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx index af77d36..facca39 100644 --- a/frontend/src/Components/Register.tsx +++ b/frontend/src/Components/Register.tsx @@ -3,34 +3,9 @@ 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) => void; -}): JSX.Element { - return ( -
- - -
- ); -} - export default function Register(): JSX.Element { const [username, setUsername] = useState(); const [password, setPassword] = useState(); @@ -48,6 +23,7 @@ export default function Register(): JSX.Element { nav("/"); // Instantly navigate to the login page } else { setErrMessage(response.message ?? "Unknown error"); + console.error(errMessage); } }; @@ -85,43 +61,6 @@ export default function Register(): JSX.Element { setPassword(e.target.value); }} /> -
- - { - setUsername(e.target.value); - }} - /> -
-
- - { - setPassword(e.target.value); - }} - /> -
- {errMessage &&

{errMessage}

}