Compare commits

...

51 commits

Author SHA1 Message Date
Imbus
5f6977354f Justfile save-release target now saves image with name containing commit hash and date 2024-03-19 04:02:04 +01:00
Imbus
ab313551c9 Merge branch 'frontend' into dev 2024-03-19 03:40:24 +01:00
Imbus
2ce1837223 Merge branch 'frontend' into dev 2024-03-19 03:36:42 +01:00
al8763be
502cd67b4c Merge branch 'dev' into BumBranch 2024-03-19 02:15:33 +01:00
Imbus
4dbbee3249 Checking errors from transactions in go 2024-03-19 02:14:09 +01:00
al8763be
e3fd9f52ca Merge branch 'gruppPP' into BumBranch 2024-03-19 02:12:37 +01:00
al8763be
8081f289b5 fixed NewWeeklyReport 2024-03-19 02:11:47 +01:00
Peter KW
cdbd6ca0ce Small fixes to design 2024-03-19 01:48:01 +01:00
al8763be
77a24421e9 Merge branch 'gruppdm' into BumBranch 2024-03-19 01:24:55 +01:00
al8763be
de234c12f2 Merge branch 'imbs' into BumBranch 2024-03-19 01:24:38 +01:00
pavel Hamawand
6a84b1c14d fix backbutton 2024-03-19 01:03:38 +01:00
pavel Hamawand
c072aff9da added change username button 2024-03-19 01:02:39 +01:00
al8763be
9434c31013 Merge remote-tracking branch 'origin/dev' into frontend 2024-03-19 00:48:13 +01:00
Davenludd
ba2bb1fc5e Merge branch 'frontend' into gruppDM 2024-03-19 00:43:25 +01:00
al8763be
847427a543 Merge remote-tracking branch 'origin/dev' into frontend 2024-03-19 00:42:32 +01:00
Davenludd
b8c69fabf5 Remove unused variable and update API call in YourProjectsPage.tsx 2024-03-19 00:35:27 +01:00
pavel Hamawand
d2a8399bde minor fix 2024-03-19 00:35:15 +01:00
Peter KW
b3dfbc47a4 Merge branch 'frontend' into gruppPP 2024-03-19 00:33:30 +01:00
Davenludd
17c30f5dd9 Merge branch 'frontend' into gruppDM 2024-03-19 00:28:12 +01:00
al8763be
d7e14f1886 quick fix for getUserProjects API 2024-03-19 00:27:35 +01:00
al8763be
59c4dab2e2 quick fix for getUserProjects API 2024-03-19 00:26:17 +01:00
Peter KW
d2ff2428cd Some corrections 2024-03-19 00:26:05 +01:00
Peter KW
36524e5cbb Changed so that it makes a modal for each user instead of a link 2024-03-19 00:25:37 +01:00
Davenludd
a2bc13ec22 Merge branch 'frontend' into gruppDM 2024-03-19 00:20:43 +01:00
al8763be
83f8097c2b API getUserProjects Fucked 2024-03-19 00:20:08 +01:00
Peter KW
a0759b099a Modul for viewing user info in admin manage users page 2024-03-19 00:17:29 +01:00
Peter KW
db4f869712 Delete user component 2024-03-19 00:15:42 +01:00
Davenludd
652f74884f Merge branch 'frontend' into gruppDM 2024-03-19 00:15:41 +01:00
al8763be
3e1f899414 Merge branch 'dev' into frontend 2024-03-19 00:14:55 +01:00
Peter KW
e55b380bb4 Should be able to delete users except for self now 2024-03-19 00:14:55 +01:00
Davenludd
2eab030212 Merge branch 'frontend' into gruppDM 2024-03-18 23:37:48 +01:00
Davenludd
ff9eba039f Minor fixes YourProjectsPage 2024-03-18 23:36:59 +01:00
al8763be
2cd2ef9ef5 Merge branch 'frontend' of https://github.com/imbus64/TTime into frontend 2024-03-18 23:36:50 +01:00
Davenludd
cc09eb0ead Remove duplicate import statements 2024-03-18 23:01:30 +01:00
Davenludd
8df3311f5a Remove duplicate code in UserProjectPage 2024-03-18 22:59:16 +01:00
Davenludd
f5a4c3d0e5 Merge branch 'frontend' into gruppDM 2024-03-18 22:05:50 +01:00
Davenludd
55fd42090d Remove username prop from BasicWindow component on all pages 2024-03-18 22:00:58 +01:00
Davenludd
5a4049eaf3 Remove username prop from BasicWindow component 2024-03-18 21:56:37 +01:00
Davenludd
59add3b6b3 Remove username prop from BasicWindow component 2024-03-18 21:55:47 +01:00
Davenludd
6982d21016 Add project name to URL in UserProjectPage 2024-03-18 21:55:15 +01:00
Davenludd
f16dc1722c Update project name in YourProjectsPage.tsx URL 2024-03-18 21:53:16 +01:00
Davenludd
31c5a78dae Refactor routing paths in main.tsx 2024-03-18 21:52:34 +01:00
Davenludd
93addc9870 Fixes in NewWeeklyReport component 2024-03-18 21:40:07 +01:00
Davenludd
847180cf75 Update user navigation route 2024-03-18 21:37:52 +01:00
Davenludd
b9d7e57f2c Update background image in Header component 2024-03-18 21:37:31 +01:00
Davenludd
25713443e2 Remove username prop from BasicWindow component 2024-03-18 21:33:20 +01:00
Davenludd
d64ec708a1 Minor fixes 2024-03-18 19:37:37 +01:00
Davenludd
3e9dc87100 Add NotFoundPage to handle 404 errors 2024-03-18 19:36:28 +01:00
Davenludd
a2ad2913e4 Add NotFoundPage component 2024-03-18 19:34:15 +01:00
Peter KW
83e781c877 Fixed getting the username and removed comment 2024-03-18 18:31:58 +01:00
Davenludd
531e9a0535 Fix links in UserProjectPage component 2024-03-18 17:38:45 +01:00
16 changed files with 212 additions and 86 deletions

View file

@ -15,7 +15,7 @@ remove-podman-containers:
# Saves the release container to a tarball, pigz is just gzip but multithreaded # Saves the release container to a tarball, pigz is just gzip but multithreaded
save-release: build-container-release save-release: build-container-release
podman save --format=oci-archive ttime-server | pigz -9 > ttime-server.tar.gz podman save --format=oci-archive ttime-server | pigz -9 > ttime-server_`date -I`_`git rev-parse --short HEAD`.tar.gz
# Loads the release container from a tarball # Loads the release container from a tarball
load-release file: load-release file:

View file

@ -204,15 +204,22 @@ func (d *Db) AddProject(name string, description string, username string) error
tx := d.MustBegin() tx := d.MustBegin()
_, err := tx.Exec(projectInsert, name, description, username) _, err := tx.Exec(projectInsert, name, description, username)
if err != nil { if err != nil {
tx.Rollback() if err := tx.Rollback(); err != nil {
return err
}
return err return err
} }
_, err = tx.Exec(changeUserRole, "project_manager", username, name) _, err = tx.Exec(changeUserRole, "project_manager", username, name)
if err != nil { if err != nil {
tx.Rollback() if err := tx.Rollback(); err != nil {
return err
}
return err return err
} }
tx.Commit() if err := tx.Commit(); err != nil {
return err
}
return err return err
} }

View file

@ -42,10 +42,7 @@ interface API {
token: string, token: string,
): Promise<APIResponse<NewWeeklyReport>>; ): Promise<APIResponse<NewWeeklyReport>>;
/** Gets all the projects of a user*/ /** Gets all the projects of a user*/
getUserProjects( getUserProjects(token: string): Promise<APIResponse<Project[]>>;
username: string,
token: string,
): Promise<APIResponse<Project[]>>;
/** Gets a project from id*/ /** Gets a project from id*/
getProject(id: number): Promise<APIResponse<Project>>; getProject(id: number): Promise<APIResponse<Project>>;
} }
@ -150,10 +147,7 @@ export const api: API = {
} }
}, },
async getUserProjects( async getUserProjects(token: string): Promise<APIResponse<Project[]>> {
username: string,
token: string,
): Promise<APIResponse<Project[]>> {
try { try {
const response = await fetch("/api/getUserProjects", { const response = await fetch("/api/getUserProjects", {
method: "GET", method: "GET",
@ -161,7 +155,6 @@ export const api: API = {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: "Bearer " + token, Authorization: "Bearer " + token,
}, },
body: JSON.stringify({ username }),
}); });
if (!response.ok) { if (!response.ok) {
@ -176,7 +169,7 @@ export const api: API = {
} catch (e) { } catch (e) {
return Promise.resolve({ return Promise.resolve({
success: false, success: false,
message: "Failed to get user projects", message: "API fucked",
}); });
} }
}, },

View file

@ -0,0 +1,34 @@
import { User } from "../Types/goTypes";
import { api, APIResponse } from "../API/API";
/**
* Use to remove a user from the system
* @param props - The username of user to remove
* @returns {boolean} True if removed, false if not
* @example
* const exampleUsername = "user";
* DeleteUser({ usernameToDelete: exampleUsername });
*/
function DeleteUser(props: { usernameToDelete: string }): boolean {
//console.log(props.usernameToDelete); FOR DEBUG
let removed = false;
api
.removeUser(
props.usernameToDelete,
localStorage.getItem("accessToken") ?? "",
)
.then((response: APIResponse<User>) => {
if (response.success) {
removed = true;
} else {
console.error(response.message);
}
})
.catch((error) => {
console.error("An error occurred during creation:", error);
});
return removed;
}
export default DeleteUser;

View file

@ -1,5 +1,6 @@
import { useState } from "react"; import { useState } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import backgroundImage from "../assets/1.jpg";
function Header(): JSX.Element { function Header(): JSX.Element {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@ -11,7 +12,7 @@ function Header(): JSX.Element {
return ( return (
<header <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" 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('src/assets/1.jpg')` }} style={{ backgroundImage: `url(${backgroundImage})` }}
> >
<Link to="/your-projects"> <Link to="/your-projects">
<img <img

View file

@ -1,32 +1,31 @@
import { useState, useContext } from "react"; import { useState } from "react";
import type { NewWeeklyReport } from "../Types/goTypes"; import type { NewWeeklyReport } from "../Types/goTypes";
import { api } from "../API/API"; import { api } from "../API/API";
import { useNavigate } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import Button from "./Button"; import Button from "./Button";
import { ProjectNameContext } from "../Pages/YourProjectsPage";
export default function NewWeeklyReport(): JSX.Element { export default function NewWeeklyReport(): JSX.Element {
const [week, setWeek] = useState(0); const [week, setWeek] = useState<number>();
const [developmentTime, setDevelopmentTime] = useState(0); const [developmentTime, setDevelopmentTime] = useState<number>();
const [meetingTime, setMeetingTime] = useState(0); const [meetingTime, setMeetingTime] = useState<number>();
const [adminTime, setAdminTime] = useState(0); const [adminTime, setAdminTime] = useState<number>();
const [ownWorkTime, setOwnWorkTime] = useState(0); const [ownWorkTime, setOwnWorkTime] = useState<number>();
const [studyTime, setStudyTime] = useState(0); const [studyTime, setStudyTime] = useState<number>();
const [testingTime, setTestingTime] = useState(0); const [testingTime, setTestingTime] = useState<number>();
const projectName = useContext(ProjectNameContext); const { projectName } = useParams();
const token = localStorage.getItem("accessToken") ?? ""; const token = localStorage.getItem("accessToken") ?? "";
const handleNewWeeklyReport = async (): Promise<void> => { const handleNewWeeklyReport = async (): Promise<void> => {
const newWeeklyReport: NewWeeklyReport = { const newWeeklyReport: NewWeeklyReport = {
projectName, projectName: projectName ?? "",
week, week: week ?? 0,
developmentTime, developmentTime: developmentTime ?? 0,
meetingTime, meetingTime: meetingTime ?? 0,
adminTime, adminTime: adminTime ?? 0,
ownWorkTime, ownWorkTime: ownWorkTime ?? 0,
studyTime, studyTime: studyTime ?? 0,
testingTime, testingTime: testingTime ?? 0,
}; };
await api.submitWeeklyReport(newWeeklyReport, token); await api.submitWeeklyReport(newWeeklyReport, token);
@ -59,7 +58,9 @@ export default function NewWeeklyReport(): JSX.Element {
setWeek(weekNumber); setWeek(weekNumber);
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
event.preventDefault(); const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}} }}
onPaste={(event) => { onPaste={(event) => {
event.preventDefault(); event.preventDefault();

View file

@ -0,0 +1,54 @@
import { Link } from "react-router-dom";
import Button from "./Button";
import DeleteUser from "./DeleteUser";
import UserProjectListAdmin from "./UserProjectListAdmin";
function UserInfoModal(props: {
isVisible: boolean;
username: string;
onClose: () => void;
}): JSX.Element {
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-lg text-center">
<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>
<div>
<h2 className="font-bold text-[22px] mb-[20px]">
Member of these projects:
</h2>
<div className="pr-6 pl-6">
<UserProjectListAdmin />
</div>
</div>
<div className="items-center space-x-6 pr-6 pl-6">
<Button
text={"Delete"}
onClick={function (): void {
DeleteUser({ usernameToDelete: props.username });
}}
type="button"
/>
<Button
text={"Close"}
onClick={function (): void {
props.onClose();
}}
type="button"
/>
</div>
</div>
</div>
);
}
export default UserInfoModal;

View file

@ -1,5 +1,6 @@
import { Link } from "react-router-dom"; import { useState } from "react";
import { PublicUser } from "../Types/goTypes"; import { PublicUser } from "../Types/goTypes";
import UserInfoModal from "./UserInfoModal";
/** /**
* The props for the UserProps component * The props for the UserProps component
@ -9,27 +10,52 @@ interface UserProps {
} }
/** /**
* A list of users for admin manage users page, that links admin to the right user page * A list of users for admin manage users page, that sets an onClick
* thanks to the state property * function for eact user <li> element, which displays a modul with
* @param props - The users to display * user info.
* @param props - An array of users users to display
* @returns {JSX.Element} The user list * @returns {JSX.Element} The user list
* @example * @example
* const users = [{ id: 1, userName: "Random name" }]; * const users = [{ id: 1, userName: "ExampleName" }];
* return <UserList users={users} />; * return <UserList users={users} />;
*/ */
export function UserListAdmin(props: UserProps): JSX.Element { export function UserListAdmin(props: UserProps): JSX.Element {
const [modalVisible, setModalVisible] = useState(false);
const [username, setUsername] = useState("");
const handleClick = (username: string): void => {
setUsername(username);
setModalVisible(true);
};
const handleClose = (): void => {
setUsername("");
setModalVisible(false);
};
return ( return (
<div> <>
<ul className="font-bold underline text-[30px] cursor-pointer padding"> <UserInfoModal
{props.users.map((user) => ( onClose={handleClose}
<Link to="/adminUserInfo" key={user.userId} state={user.username}> isVisible={modalVisible}
<li className="pt-5" key={user.userId}> username={username}
/>
<div>
<ul className="font-bold underline text-[30px] cursor-pointer padding">
{props.users.map((user) => (
<li
className="pt-5"
key={user.userId}
onClick={() => {
handleClick(user.username);
}}
>
{user.username} {user.username}
</li> </li>
</Link> ))}
))} </ul>
</ul> </div>
</div> </>
); );
} }

View file

@ -1,17 +1,17 @@
import React, { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { api } from "../API/API"; import { api } from "../API/API";
import { Project } from "../Types/goTypes"; import { Project } from "../Types/goTypes";
const UserProjectListAdmin: React.FC = () => { function UserProjectListAdmin(): JSX.Element {
const [projects, setProjects] = useState<Project[]>([]); const [projects, setProjects] = useState<Project[]>([]);
useEffect(() => { useEffect(() => {
const fetchProjects = async (): Promise<void> => { const fetchProjects = async (): Promise<void> => {
try { try {
const token = localStorage.getItem("accessToken") ?? ""; const token = localStorage.getItem("accessToken") ?? "";
const username = "NoUser"; // getUsernameFromContext(); // Assuming you have a function to get the username from your context // const username = props.username;
const response = await api.getUserProjects(username, token); const response = await api.getUserProjects(token);
if (response.success) { if (response.success) {
setProjects(response.data ?? []); setProjects(response.data ?? []);
} else { } else {
@ -26,18 +26,16 @@ const UserProjectListAdmin: React.FC = () => {
}, []); }, []);
return ( return (
<div> <div className="border-2 border-black bg-white p-2 rounded-lg text-center">
<h2>User Projects</h2>
<ul> <ul>
{projects.map((project) => ( {projects.map((project) => (
<li key={project.id}> <li key={project.id}>
<span>{project.name}</span> <span>{project.name}</span>
{/* Add any additional project details you want to display */}
</li> </li>
))} ))}
</ul> </ul>
</div> </div>
); );
}; }
export default UserProjectListAdmin; export default UserProjectListAdmin;

View file

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

View file

@ -13,7 +13,7 @@ function App(): JSX.Element {
} else if (authority === 2) { } else if (authority === 2) {
navigate("/pm"); navigate("/pm");
} else if (authority === 3) { } else if (authority === 3) {
navigate("/user"); navigate("/yourProjects");
} }
}, [authority, navigate]); }, [authority, navigate]);

View file

@ -0,0 +1,18 @@
import Button from "../Components/Button";
export default function NotFoundPage(): JSX.Element {
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-white">
<h1 className="text-[30px]">404 Page Not Found</h1>
<a href="/">
<Button
text="Go to Home Page"
onClick={(): void => {
localStorage.clear();
}}
type="button"
/>
</a>
</div>
);
}

View file

@ -1,18 +1,20 @@
import { Link, useLocation } from "react-router-dom"; import { Link, useLocation, useParams } from "react-router-dom";
import BasicWindow from "../../Components/BasicWindow"; import BasicWindow from "../../Components/BasicWindow";
import BackButton from "../../Components/BackButton"; import BackButton from "../../Components/BackButton";
function UserProjectPage(): JSX.Element { function UserProjectPage(): JSX.Element {
const { projectName } = useParams();
const content = ( const content = (
<> <>
<h1 className="font-bold text-[30px] mb-[20px]">{useLocation().state}</h1> <h1 className="font-bold text-[30px] mb-[20px]">{useLocation().state}</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="/project-page"> <Link to={`/projectPage/${projectName}`}>
<h1 className="font-bold underline text-[30px] cursor-pointer"> <h1 className="font-bold underline text-[30px] cursor-pointer">
Your Time Reports Your Time Reports
</h1> </h1>
</Link> </Link>
<Link to="/new-time-report"> <Link to={`/newTimeReport/${projectName}`}>
<h1 className="font-bold underline text-[30px] cursor-pointer"> <h1 className="font-bold underline text-[30px] cursor-pointer">
New Time Report New Time Report
</h1> </h1>

View file

@ -11,9 +11,8 @@ function UserProjectPage(): JSX.Element {
const [selectedProject, setSelectedProject] = useState(""); const [selectedProject, setSelectedProject] = useState("");
const getProjects = async (): Promise<void> => { const getProjects = async (): Promise<void> => {
const username = localStorage.getItem("username") ?? ""; // replace with actual username const token = localStorage.getItem("accessToken") ?? "";
const token = localStorage.getItem("accessToken") ?? ""; // replace with actual token const response = await api.getUserProjects(token);
const response = await api.getUserProjects(username, token);
console.log(response); console.log(response);
if (response.success) { if (response.success) {
setProjects(response.data ?? []); setProjects(response.data ?? []);
@ -36,7 +35,7 @@ function UserProjectPage(): 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]"> <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]">
{projects.map((project, index) => ( {projects.map((project, index) => (
<Link <Link
to={`/project/${project.id}`} to={`/project/${project.name}`}
onClick={() => { onClick={() => {
handleProjectClick(project.name); handleProjectClick(project.name);
}} }}

View file

@ -29,12 +29,14 @@ import AdminProjectManageMembers from "./Pages/AdminPages/AdminProjectManageMemb
import AdminProjectStatistics from "./Pages/AdminPages/AdminProjectStatistics.tsx"; import AdminProjectStatistics from "./Pages/AdminPages/AdminProjectStatistics.tsx";
import AdminProjectViewMemberInfo from "./Pages/AdminPages/AdminProjectViewMemberInfo.tsx"; import AdminProjectViewMemberInfo from "./Pages/AdminPages/AdminProjectViewMemberInfo.tsx";
import AdminProjectPage from "./Pages/AdminPages/AdminProjectPage.tsx"; import AdminProjectPage from "./Pages/AdminPages/AdminProjectPage.tsx";
import NotFoundPage from "./Pages/NotFoundPage.tsx";
// This is where the routes are mounted // This is where the routes are mounted
const router = createBrowserRouter([ const router = createBrowserRouter([
{ {
path: "/", path: "/",
element: <App />, element: <App />,
errorElement: <NotFoundPage />,
}, },
{ {
path: "/admin", path: "/admin",
@ -44,30 +46,26 @@ const router = createBrowserRouter([
path: "/pm", path: "/pm",
element: <YourProjectsPage />, element: <YourProjectsPage />,
}, },
{
path: "/user",
element: <YourProjectsPage />,
},
{ {
path: "/yourProjects", path: "/yourProjects",
element: <YourProjectsPage />, element: <YourProjectsPage />,
}, },
{ {
path: "/editTimeReport", path: "/project/:projectName",
element: <UserEditTimeReportPage />,
},
{
path: "/newTimeReport",
element: <UserNewTimeReportPage />,
},
{
path: "/project",
element: <UserProjectPage />, element: <UserProjectPage />,
}, },
{ {
path: "/projectPage", path: "/newTimeReport/:projectName",
element: <UserNewTimeReportPage />,
},
{
path: "/projectPage/:projectName",
element: <UserViewTimeReportsPage />, element: <UserViewTimeReportsPage />,
}, },
{
path: "/editTimeReport",
element: <UserEditTimeReportPage />,
},
{ {
path: "/changeRole", path: "/changeRole",
element: <PMChangeRole />, element: <PMChangeRole />,