Compare commits

..

56 commits

Author SHA1 Message Date
Imbus
aa7c5de1fc GetUserProjects handler 2024-03-14 21:25:14 +01:00
Imbus
e1153934c6 Database interface extension, GetAllProjects, GetUserRole, GetProjectsForUser 2024-03-14 19:48:49 +01:00
Imbus
df7ca1ab90 Merge branch 'frontend' into dev 2024-03-14 18:40:07 +01:00
Imbus
baade40d77 Merge branch 'gruppDM' into frontend 2024-03-14 18:39:51 +01:00
Imbus
7f6a9f6fd1 Merge frontend with dev 2024-03-14 18:38:25 +01:00
Imbus
3bf0c34a5f Merge branch 'gruppDM' into frontend 2024-03-14 18:36:27 +01:00
Imbus
1c0884bb5d Formatting and edits to make linter happy 2024-03-14 18:32:56 +01:00
Imbus
6cd940866e Merge frontend into dev 2024-03-14 18:31:00 +01:00
Imbus
5e8af6098b Merge branch 'imbs' into dev 2024-03-14 18:27:12 +01:00
dDogge
676d6637a2 Added GetAllUsersApplication and corresponding test 2024-03-14 16:25:54 +01:00
dDogge
0e1ea15cc9 Added GetAllUsersProject and corresponding test 2024-03-14 16:01:56 +01:00
Davenludd
8690e381c8 Add links to navigate between pages projectPage & NewTimeReport 2024-03-14 15:49:13 +01:00
Davenludd
41e1c32ee0 Added time report component to various pages 2024-03-14 15:17:38 +01:00
Mattias
46c4a5dc92 Added more paths in main 2024-03-14 14:59:57 +01:00
Davenludd
03e2be0a46 Add AdminProjectChangeUserRole page & buttons 2024-03-14 14:53:51 +01:00
Davenludd
39edc419df Add AdminProjectViewMemberInfo page & buttons 2024-03-14 14:53:51 +01:00
Davenludd
027bce6dfc Add AdminProjectAddMember page & buttons 2024-03-14 14:53:51 +01:00
Davenludd
da730a2d18 Add AdminProjectManageMembers page & buttons 2024-03-14 14:53:51 +01:00
Davenludd
dd370d86e3 Add AdminProjectStatistics page & buttons 2024-03-14 14:53:51 +01:00
Davenludd
ca7e4c6189 Add AdminProjectPage page & buttons 2024-03-14 14:53:51 +01:00
Mattias
19e3567c78 More paths in main 2024-03-14 14:42:26 +01:00
Davenludd
6a68ad1c3f Add AdminAddProject page & buttons 2024-03-14 14:39:34 +01:00
Davenludd
3047db28f6 Add AdminManageProjects page & buttons 2024-03-14 14:39:34 +01:00
Davenludd
2d5de569ae Add AdminChangeUsername page & buttons 2024-03-14 14:39:34 +01:00
Davenludd
5a6fe1c472 Add AdminViewUserInfo page & buttons 2024-03-14 14:39:34 +01:00
Mattias
1b3660eb83 Added more paths in main 2024-03-14 14:33:43 +01:00
Davenludd
69df212fde Add AdminAddUser page & buttons 2024-03-14 14:30:56 +01:00
Davenludd
434879c26c Add AdminManageUsers page & buttons 2024-03-14 14:30:56 +01:00
Mattias
c5d5c389dd Added new paths in main 2024-03-14 14:29:10 +01:00
Davenludd
03f350f303 Made a new component for time reporting 2024-03-14 13:21:19 +01:00
Davenludd
7e319e34c9 Change logo 2024-03-14 11:08:07 +01:00
Mattias
60774f6324 Added more paths in main 2024-03-14 11:00:55 +01:00
Mattias
db647c6e7c Added buttons 2024-03-14 11:00:55 +01:00
Mattias
45749afe69 Added buttons 2024-03-14 11:00:55 +01:00
Davenludd
2cce3f3ab4 Add time/activity and time/role buttons to PMProjectMembers page 2024-03-14 10:59:51 +01:00
Davenludd
a67e43e537 Add back button to PMUnsignedReports component 2024-03-14 10:57:42 +01:00
Davenludd
ce5d6d2837 Add "Back" button to PMTotalTimeRole component 2024-03-14 10:56:01 +01:00
Davenludd
f9260976df Add "Back" button to PMTotalTimeActivity component 2024-03-14 10:55:30 +01:00
Davenludd
5c0cf5fc33 Add buttons to PMViewUnsignedReport component 2024-03-14 10:54:37 +01:00
Davenludd
8a2724de5e Add save and back buttons to PMChangeRole component 2024-03-14 10:52:48 +01:00
Davenludd
7aa83b1d99 Add back button to PMOtherUsersTR component 2024-03-14 10:50:52 +01:00
Davenludd
2c9d3baafa Added button to PMProjectMembers 2024-03-14 10:49:15 +01:00
Davenludd
a5f15e5c06 Create all files for ProjectManagerPages 2024-03-14 10:41:10 +01:00
Mattias
c1aa0769bb Added path in main 2024-03-14 10:33:00 +01:00
Mattias
6be1060cff Added buttons to UserEditTimeReportPage 2024-03-14 10:32:39 +01:00
Imbus
974d86c2d9 Merge branch 'dev' into frontend 2024-03-13 17:07:12 +01:00
Mattias
42498ca1c4 Changed name on exampletext 2024-03-13 16:18:06 +01:00
Davenludd
029fdd85b9 Minor change LoginPage 2024-03-13 16:11:36 +01:00
Davenludd
8bb4a1c893 Change logo to svg 2024-03-13 16:07:52 +01:00
Mattias
7c51f586ce Created three new empty pages for basic user 2024-03-13 14:18:06 +01:00
Imbus
2630a0c9ef Merge branch 'frontend' of github.com:imbus64/TTime into frontend 2024-03-13 14:17:22 +01:00
Mattias
41674c3969 Removed example pages Home and Settings aswell as the react-logo 2024-03-13 14:17:11 +01:00
Peter KW
1672b100d9 added new page for admin and path in main 2024-03-13 14:17:11 +01:00
Imbus
4d23f8acea Merge branch 'frontend' of github.com:imbus64/TTime into frontend 2024-03-13 13:51:35 +01:00
Mattias
ac6638b344 Removed example pages Home and Settings aswell as the react-logo 2024-03-08 11:36:36 +01:00
Peter KW
d7cf291836 added new page for admin and path in main 2024-03-07 17:48:43 +01:00
40 changed files with 1123 additions and 91 deletions

View file

@ -5,6 +5,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
"ttime/internal/types"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
@ -23,6 +24,11 @@ type Database interface {
AddTimeReport(projectName string, userName string, start time.Time, end time.Time) error AddTimeReport(projectName string, userName string, start time.Time, end time.Time) error
AddUserToProject(username string, projectname string, role string) error AddUserToProject(username string, projectname string, role string) error
ChangeUserRole(username string, projectname string, role string) error ChangeUserRole(username string, projectname string, role string) error
GetAllUsersProject(projectname string) ([]UserProjectMember, error)
GetAllUsersApplication() ([]string, error)
GetProjectsForUser(username string) ([]types.Project, error)
GetAllProjects() ([]types.Project, error)
GetUserRole(username string, projectname string) (string, error)
} }
// This struct is a wrapper type that holds the database connection // This struct is a wrapper type that holds the database connection
@ -31,6 +37,11 @@ type Db struct {
*sqlx.DB *sqlx.DB
} }
type UserProjectMember struct {
Username string `db:"username"`
UserRole string `db:"p_role"`
}
//go:embed migrations //go:embed migrations
var scripts embed.FS var scripts embed.FS
@ -45,6 +56,21 @@ const addTimeReport = `WITH UserLookup AS (SELECT id FROM users WHERE username =
const addUserToProject = "INSERT INTO user_roles (user_id, project_id, p_role) VALUES (?, ?, ?)" // WIP const addUserToProject = "INSERT INTO user_roles (user_id, project_id, p_role) VALUES (?, ?, ?)" // WIP
const changeUserRole = "UPDATE user_roles SET p_role = ? WHERE user_id = ? AND project_id = ?" const changeUserRole = "UPDATE user_roles SET p_role = ? WHERE user_id = ? AND project_id = ?"
const getProjectsForUser = `
SELECT
projects.id,
projects.name,
projects.description,
projects.owner_user_id
FROM
projects
JOIN
user_roles ON projects.id = user_roles.project_id
JOIN
users ON user_roles.user_id = users.id
WHERE
users.username = ?;`
// DbConnect connects to the database // DbConnect connects to the database
func DbConnect(dbpath string) Database { func DbConnect(dbpath string) Database {
// Open the database // Open the database
@ -62,6 +88,18 @@ func DbConnect(dbpath string) Database {
return &Db{db} return &Db{db}
} }
func (d *Db) GetProjectsForUser(username string) ([]types.Project, error) {
var projects []types.Project
err := d.Select(&projects, getProjectsForUser, username)
return projects, err
}
func (d *Db) GetAllProjects() ([]types.Project, error) {
var projects []types.Project
err := d.Select(&projects, "SELECT * FROM projects")
return projects, err
}
func (d *Db) AddTimeReport(projectName string, userName string, start time.Time, end time.Time) error { // WIP func (d *Db) AddTimeReport(projectName string, userName string, start time.Time, end time.Time) error { // WIP
_, err := d.Exec(addTimeReport, userName, projectName, start, end) _, err := d.Exec(addTimeReport, userName, projectName, start, end)
return err return err
@ -101,6 +139,12 @@ func (d *Db) ChangeUserRole(username string, projectname string, role string) er
return err3 return err3
} }
func (d *Db) GetUserRole(username string, projectname string) (string, error) {
var role string
err := d.Get(&role, "SELECT p_role FROM user_roles WHERE user_id = (SELECT id FROM users WHERE username = ?) AND project_id = (SELECT id FROM projects WHERE name = ?)", username, projectname)
return role, err
}
// AddUser adds a user to the database // AddUser adds a user to the database
func (d *Db) AddUser(username string, password string) error { func (d *Db) AddUser(username string, password string) error {
_, err := d.Exec(userInsert, username, password) _, err := d.Exec(userInsert, username, password)
@ -136,6 +180,69 @@ func (d *Db) AddProject(name string, description string, username string) error
return err return err
} }
func (d *Db) GetAllUsersProject(projectname string) ([]UserProjectMember, error) {
// Define the SQL query to fetch users and their roles for a given project
query := `
SELECT u.username, ur.p_role
FROM users u
INNER JOIN user_roles ur ON u.id = ur.user_id
INNER JOIN projects p ON ur.project_id = p.id
WHERE p.name = ?
`
// Execute the query
rows, err := d.Queryx(query, projectname)
if err != nil {
return nil, err
}
defer rows.Close()
// Iterate over the rows and populate the result slice
var users []UserProjectMember
for rows.Next() {
var user UserProjectMember
if err := rows.StructScan(&user); err != nil {
return nil, err
}
users = append(users, user)
}
if err := rows.Err(); err != nil {
return nil, err
}
return users, nil
}
// GetAllUsersApplication retrieves all usernames from the database
func (d *Db) GetAllUsersApplication() ([]string, error) {
// Define the SQL query to fetch all usernames
query := `
SELECT username FROM users
`
// Execute the query
rows, err := d.Queryx(query)
if err != nil {
return nil, err
}
defer rows.Close()
// Iterate over the rows and populate the result slice
var usernames []string
for rows.Next() {
var username string
if err := rows.Scan(&username); err != nil {
return nil, err
}
usernames = append(usernames, username)
}
if err := rows.Err(); err != nil {
return nil, err
}
return usernames, nil
}
// Reads a directory of migration files and applies them to the database. // Reads a directory of migration files and applies them to the database.
// This will eventually be used on an embedded directory // This will eventually be used on an embedded directory
func (d *Db) Migrate(dirname string) error { func (d *Db) Migrate(dirname string) error {

View file

@ -169,8 +169,177 @@ func TestChangeUserRole(t *testing.T) {
t.Error("AddUserToProject failed:", err) t.Error("AddUserToProject failed:", err)
} }
role, err := db.GetUserRole("testuser", "testproject")
if err != nil {
t.Error("GetUserRole failed:", err)
}
if role != "user" {
t.Error("GetUserRole failed: expected user, got", role)
}
err = db.ChangeUserRole("testuser", "testproject", "admin") err = db.ChangeUserRole("testuser", "testproject", "admin")
if err != nil { if err != nil {
t.Error("ChangeUserRole failed:", err) t.Error("ChangeUserRole failed:", err)
} }
role, err = db.GetUserRole("testuser", "testproject")
if err != nil {
t.Error("GetUserRole failed:", err)
}
if role != "admin" {
t.Error("GetUserRole failed: expected admin, got", role)
}
}
func TestGetAllUsersProject(t *testing.T) {
db, err := setupState()
if err != nil {
t.Error("setupState failed:", err)
}
err = db.AddUser("testuser1", "password")
if err != nil {
t.Error("AddUser failed:", err)
}
err = db.AddUser("testuser2", "password")
if err != nil {
t.Error("AddUser failed:", err)
}
err = db.AddProject("testproject", "description", "testuser1")
if err != nil {
t.Error("AddProject failed:", err)
}
err = db.AddUserToProject("testuser1", "testproject", "project_manager")
if err != nil {
t.Error("AddUserToProject failed:", err)
}
err = db.AddUserToProject("testuser2", "testproject", "user")
if err != nil {
t.Error("AddUserToProject failed:", err)
}
users, err := db.GetAllUsersProject("testproject")
if err != nil {
t.Error("GetAllUsersProject failed:", err)
}
// Check if both users are returned with their roles
if len(users) != 2 {
t.Errorf("Expected 2 users, got %d", len(users))
}
// Check if testuser1 has project manager role
foundProjectManager := false
for _, user := range users {
if user.Username == "testuser1" && user.UserRole == "project_manager" {
foundProjectManager = true
break
}
}
if !foundProjectManager {
t.Error("Project Manager user not found")
}
// Check if testuser2 has user role
foundUser := false
for _, user := range users {
if user.Username == "testuser2" && user.UserRole == "user" {
foundUser = true
break
}
}
if !foundUser {
t.Error("User user not found")
}
}
func TestGetAllUsersApplication(t *testing.T) {
db, err := setupState()
if err != nil {
t.Error("setupState failed:", err)
}
err = db.AddUser("testuser1", "password")
if err != nil {
t.Error("AddUser failed:", err)
}
err = db.AddUser("testuser2", "password")
if err != nil {
t.Error("AddUser failed:", err)
}
users, err := db.GetAllUsersApplication()
if err != nil {
t.Error("GetAllUsersApplication failed:", err)
}
// Check if both users are returned
if len(users) != 2 {
t.Errorf("Expected 2 users, got %d", len(users))
}
// Check if the test users are included in the list
foundTestUser1 := false
foundTestUser2 := false
for _, user := range users {
if user == "testuser1" {
foundTestUser1 = true
}
if user == "testuser2" {
foundTestUser2 = true
}
}
if !foundTestUser1 {
t.Error("testuser1 not found")
}
if !foundTestUser2 {
t.Error("testuser2 not found")
}
}
func TestGetProjectsForUser(t *testing.T) {
db, err := setupState()
if err != nil {
t.Error("setupState failed:", err)
}
err = db.AddUser("testuser", "password")
if err != nil {
t.Error("AddUser failed:", err)
}
err = db.AddProject("testproject", "description", "testuser")
if err != nil {
t.Error("AddProject failed:", err)
}
err = db.AddUserToProject("testuser", "testproject", "user")
if err != nil {
t.Error("AddUserToProject failed:", err)
}
projects1, err := db.GetAllProjects()
if err != nil {
t.Error("GetAllProjects failed:", err)
}
if len(projects1) != 1 {
t.Error("GetAllProjects failed: expected 1, got", len(projects1))
}
projects, err := db.GetProjectsForUser("testuser")
if err != nil {
t.Error("GetProjectsForUser failed:", err)
}
if len(projects) != 1 {
t.Error("GetProjectsForUser failed: expected 1, got", len(projects))
}
} }

View file

@ -11,12 +11,12 @@ import (
// The actual interface that we will use // The actual interface that we will use
type GlobalState interface { type GlobalState interface {
Register(c *fiber.Ctx) error // To register a new user Register(c *fiber.Ctx) error // To register a new user
UserDelete(c *fiber.Ctx) error // To delete a user UserDelete(c *fiber.Ctx) error // To delete a user
Login(c *fiber.Ctx) error // To get the token Login(c *fiber.Ctx) error // To get the token
LoginRenew(c *fiber.Ctx) error // To renew the token LoginRenew(c *fiber.Ctx) error // To renew the token
CreateProject(c *fiber.Ctx) error // To create a new project CreateProject(c *fiber.Ctx) error // To create a new project
// GetProjects(c *fiber.Ctx) error // To get all projects GetUserProjects(c *fiber.Ctx) error // To get all projects
// GetProject(c *fiber.Ctx) error // To get a specific project // GetProject(c *fiber.Ctx) error // To get a specific project
// UpdateProject(c *fiber.Ctx) error // To update a project // UpdateProject(c *fiber.Ctx) error // To update a project
// DeleteProject(c *fiber.Ctx) error // To delete a project // DeleteProject(c *fiber.Ctx) error // To delete a project
@ -163,3 +163,20 @@ func (gs *GState) CreateProject(c *fiber.Ctx) error {
return c.Status(200).SendString("Project added") return c.Status(200).SendString("Project added")
} }
// GetUserProjects returns all projects that the user is a member of
func (gs *GState) GetUserProjects(c *fiber.Ctx) error {
// First we get the username from the token
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
username := claims["name"].(string)
// Then dip into the database to get the projects
projects, err := gs.Db.GetProjectsForUser(username)
if err != nil {
return c.Status(500).SendString(err.Error())
}
// Return a json serialized list of projects
return c.JSON(projects)
}

View file

@ -1,16 +1,11 @@
package types package types
import (
"time"
)
// Project is a struct that holds the information about a project // Project is a struct that holds the information about a project
type Project struct { type Project struct {
ID int `json:"id" db:"id"` ID int `json:"id" db:"id"`
Name string `json:"name" db:"name"` Name string `json:"name" db:"name"`
Description string `json:"description" db:"description"` Description string `json:"description" db:"description"`
Owner string `json:"owner" db:"owner"` Owner string `json:"owner" db:"owner_user_id"`
Created time.Time `json:"created" db:"created"`
} }
// As it arrives from the client // As it arrives from the client

View file

@ -69,6 +69,7 @@ func main() {
SigningKey: jwtware.SigningKey{Key: []byte("secret")}, SigningKey: jwtware.SigningKey{Key: []byte("secret")},
})) }))
server.Get("/api/getUserProjects", gs.GetUserProjects)
server.Post("/api/loginrenew", gs.LoginRenew) server.Post("/api/loginrenew", gs.LoginRenew)
server.Delete("/api/userdelete", gs.UserDelete) // Perhaps just use POST to avoid headaches server.Delete("/api/userdelete", gs.UserDelete) // Perhaps just use POST to avoid headaches
server.Post("/api/project", gs.CreateProject) server.Post("/api/project", gs.CreateProject)

View file

@ -15,7 +15,7 @@ function Header({ username }: { username: string }): JSX.Element {
> >
<Link to="/your-projects"> <Link to="/your-projects">
<img <img
src="/src/assets/TTIMElogo.png" src="/src/assets/Logo.svg"
alt="TTIME Logo" alt="TTIME Logo"
className="w-11 h-14 cursor-pointer" className="w-11 h-14 cursor-pointer"
/> />

View file

@ -1,5 +1,5 @@
import { useState } from "react"; import { useState } from "react";
import { NewUser, User } from "../Types/Users"; import { NewUser } from "../Types/Users";
import { api } from "../API/API"; import { api } from "../API/API";
export default function Register(): JSX.Element { export default function Register(): JSX.Element {
@ -8,7 +8,7 @@ export default function Register(): JSX.Element {
const handleRegister = async (): Promise<void> => { const handleRegister = async (): Promise<void> => {
const newUser: NewUser = { userName: username, password }; const newUser: NewUser = { userName: username, password };
const user = await api.registerUser(newUser); await api.registerUser(newUser); // TODO: Handle errors
}; };
return ( return (

View file

@ -0,0 +1,59 @@
function NewTimeReport(): JSX.Element {
const activities = [
"Development",
"Meeting",
"Administration",
"Own Work",
"Studies",
"Testing",
];
return (
<>
<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">
<input
className="w-fill h-[5vh] font-sans text-[3vh] pl-[1vw] rounded-full text-center pt-[1vh] pb-[1vh] border-2 border-black"
type="week"
placeholder="Week"
onKeyDown={(event) => {
event.preventDefault();
}}
onPaste={(event) => {
event.preventDefault();
}}
/>
<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">
{activities.map((activity, index) => (
<tr key={index} className="h-[10vh]">
<td>{activity}</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
))}
</tbody>
</table>
</div>
</>
);
}
export default NewTimeReport;

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminAddProject(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Finish"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminAddProject;

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminAddUser(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Finish"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminAddUser;

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminChangeUsername(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Finish"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminChangeUsername;

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminManageProjects(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Add Project"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminManageProjects;

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminManageUsers(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Add User"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminManageUsers;

View file

@ -0,0 +1,27 @@
import { Link } from "react-router-dom";
import BasicWindow from "../../Components/BasicWindow";
function AdminMenuPage(): JSX.Element {
const content = (
<>
<h1 className="font-bold text-[30px] mb-[20px]">Administrator Menu</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]">
<Link to="/admin-users-page">
<h1 className="font-bold underline text-[30px] cursor-pointer">
Manage Users
</h1>
</Link>
<Link to="/admin-projects-page">
<h1 className="font-bold underline text-[30px] cursor-pointer">
Manage Projects
</h1>
</Link>
</div>
</>
);
const buttons = <></>;
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminMenuPage;

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminProjectAddMember(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Add"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminProjectAddMember;

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminProjectChangeUserRole(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Change"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminProjectChangeUserRole;

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminProjectManageMembers(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Add Member"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminProjectManageMembers;

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminProjectPage(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Delete"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminProjectPage;

View file

@ -0,0 +1,20 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminProjectStatistics(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminProjectStatistics;

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminProjectViewMemberInfo(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Remove"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminProjectViewMemberInfo;

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function AdminViewUserInfo(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Delete"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default AdminViewUserInfo;

View file

@ -1,36 +0,0 @@
import reactLogo from "../assets/react.svg";
import viteLogo from "/vite.svg";
import "../index.css";
import { CountButton } from "../Components/CountButton";
import { Link } from "react-router-dom";
/**
* The home page of the application
* @returns {JSX.Element} The home page
*/
export default function HomePage(): JSX.Element {
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} className="logo h-32 p-5" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank" rel="noreferrer">
<img
src={reactLogo}
className="logo react h-32 p-5"
alt="React logo"
/>
</a>
</div>
<h1>Vite + React</h1>
<div className="card flex flex-col items-center space-y-4">
<CountButton />
<Link to="/settings">To Settings</Link>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
);
}

View file

@ -1,9 +1,8 @@
import Button from "../Components/Button"; import Button from "../Components/Button";
import Logo from "/src/assets/TTIMElogo.png"; import Logo from "/src/assets/Logo.svg";
import "./LoginPage.css"; import "./LoginPage.css";
import { useEffect } from "react"; import { useEffect } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import Register from "../Components/Register";
const PreloadBackgroundAnimation = (): JSX.Element => { const PreloadBackgroundAnimation = (): JSX.Element => {
useEffect(() => { useEffect(() => {
@ -70,14 +69,14 @@ function LoginPage(): JSX.Element {
}} }}
/> />
</Link> </Link>
<Link to="/register"> <Link to="/register">
<Button <Button
text="Register new user" text="Register new user"
onClick={(): void => { onClick={(): void => {
return; return;
}} }}
/> />
</Link> </Link>
</div> </div>
</div> </div>
</> </>

View file

@ -0,0 +1,26 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function ChangeRole(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Save"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default ChangeRole;

View file

@ -0,0 +1,20 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function PMOtherUsersTR(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default PMOtherUsersTR;

View file

@ -0,0 +1,32 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function PMProjectMembers(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Time / Activity"
onClick={(): void => {
return;
}}
/>
<Button
text="Time / Role"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default PMProjectMembers;

View file

@ -0,0 +1,28 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
import TimeReport from "../../Components/TimeReport";
function PMTotalTimeActivity(): JSX.Element {
const content = (
<>
<h1 className="font-bold text-[30px] mb-[20px]">
Total Time Per Activity
</h1>
<TimeReport />
</>
);
const buttons = (
<>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default PMTotalTimeActivity;

View file

@ -0,0 +1,20 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function PMTotalTimeRole(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default PMTotalTimeRole;

View file

@ -0,0 +1,20 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function PMUnsignedReports(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default PMUnsignedReports;

View file

@ -0,0 +1,40 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
import TimeReport from "../../Components/TimeReport";
function PMViewUnsignedReport(): JSX.Element {
const content = (
<>
<h1 className="font-bold text-[30px] mb-[20px]">
Username&apos;s Time Report
</h1>
<TimeReport />
</>
);
const buttons = (
<>
<Button
text="Sign"
onClick={(): void => {
return;
}}
/>
<Button
text="Save"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default PMViewUnsignedReport;

View file

@ -1,17 +0,0 @@
import "../index.css";
import { Link } from "react-router-dom";
/**
* The settings page of the application
* @returns {JSX.Element} The settings page
*/
export default function SettingsPage(): JSX.Element {
return (
<>
<h1>Very Fancy Settings Page</h1>
<div className="card">
<Link to="/">To Home</Link>
</div>
</>
);
}

View file

@ -0,0 +1,32 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
import NewTimeReport from "../../Components/TimeReport";
function UserEditTimeReportPage(): JSX.Element {
const content = (
<>
<h1 className="font-bold text-[30px] mb-[20px]">Edit Time Report</h1>
<NewTimeReport />
</>
);
const buttons = (
<>
<Button
text="Save"
onClick={(): void => {
return;
}}
/>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default UserEditTimeReportPage;

View file

@ -0,0 +1,35 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
import NewTimeReport from "../../Components/TimeReport";
import { Link } from "react-router-dom";
function UserNewTimeReportPage(): JSX.Element {
const content = (
<>
<h1 className="font-bold text-[30px] mb-[20px]">New Time Report</h1>
<NewTimeReport />
</>
);
const buttons = (
<>
<Button
text="Submit"
onClick={(): void => {
return;
}}
/>
<Link to="/project">
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</Link>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default UserNewTimeReportPage;

View file

@ -5,16 +5,16 @@ import Button from "../../Components/Button";
function UserProjectPage(): JSX.Element { function UserProjectPage(): JSX.Element {
const content = ( const content = (
<> <>
<Link to="/settingsPage"> <h1 className="font-bold text-[30px] mb-[20px]">ProjectNameExample</h1>
<h1 className="font-bold text-[30px] mb-[20px]">ProjectNameExample</h1>
</Link>
<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]">
<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>
<h1 className="font-bold underline text-[30px] cursor-pointer"> <Link to="/new-time-report">
New Time Report <h1 className="font-bold underline text-[30px] cursor-pointer">
</h1> New Time Report
</h1>
</Link>
</div> </div>
</> </>
); );

View file

@ -0,0 +1,20 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
function UserViewTimeReportsPage(): JSX.Element {
const content = <></>;
const buttons = (
<>
<Button
text="Back"
onClick={(): void => {
return;
}}
/>
</>
);
return <BasicWindow username="Admin" content={content} buttons={buttons} />;
}
export default UserViewTimeReportsPage;

View file

@ -12,13 +12,13 @@ function YourProjectsPage(): JSX.Element {
</h1> </h1>
</Link> </Link>
<h1 className="underline text-[24px] cursor-pointer font-bold"> <h1 className="underline text-[24px] cursor-pointer font-bold">
ProjectNameExample ProjectNameExample2
</h1> </h1>
<h1 className="underline text-[24px] cursor-pointer font-bold"> <h1 className="underline text-[24px] cursor-pointer font-bold">
ProjectNameExample ProjectNameExample3
</h1> </h1>
<h1 className="underline text-[24px] cursor-pointer font-bold"> <h1 className="underline text-[24px] cursor-pointer font-bold">
ProjectNameExample ProjectNameExample4
</h1> </h1>
</div> </div>
</> </>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

Before

Width:  |  Height:  |  Size: 4 KiB

View file

@ -6,6 +6,30 @@ import LoginPage from "./Pages/LoginPage.tsx";
import YourProjectsPage from "./Pages/YourProjectsPage.tsx"; import YourProjectsPage from "./Pages/YourProjectsPage.tsx";
import UserProjectPage from "./Pages/UserPages/UserProjectPage.tsx"; import UserProjectPage from "./Pages/UserPages/UserProjectPage.tsx";
import Register from "./Components/Register.tsx"; import Register from "./Components/Register.tsx";
import AdminMenuPage from "./Pages/AdminPages/AdminMenuPage.tsx";
import UserEditTimeReportPage from "./Pages/UserPages/UserEditTimeReportPage.tsx";
import UserNewTimeReportPage from "./Pages/UserPages/UserNewTimeReportPage.tsx";
import UserViewTimeReportsPage from "./Pages/UserPages/UserViewTimeReportsPage.tsx";
import PMChangeRole from "./Pages/ProjectManagerPages/PMChangeRole.tsx";
import PMOtherUsersTR from "./Pages/ProjectManagerPages/PMOtherUsersTR.tsx";
import PMProjectMembers from "./Pages/ProjectManagerPages/PMProjectMembers.tsx";
import PMProjectPage from "./Pages/ProjectManagerPages/PMProjectPage.tsx";
import PMTotalTimeActivity from "./Pages/ProjectManagerPages/PMTotalTimeActivity.tsx";
import PMTotalTimeRole from "./Pages/ProjectManagerPages/PMTotalTimeRole.tsx";
import PMUnsignedReports from "./Pages/ProjectManagerPages/PMUnsignedReports.tsx";
import PMViewUnsignedReport from "./Pages/ProjectManagerPages/PMViewUnsignedReport.tsx";
import AdminManageUsers from "./Pages/AdminPages/AdminManageUsers.tsx";
import AdminViewUserInfo from "./Pages/AdminPages/AdminViewUserInfo.tsx";
import AdminManageProjects from "./Pages/AdminPages/AdminManageProjects.tsx";
import AdminAddProject from "./Pages/AdminPages/AdminAddProject.tsx";
import AdminAddUser from "./Pages/AdminPages/AdminAddUser.tsx";
import AdminChangeUsername from "./Pages/AdminPages/AdminChangeUsername.tsx";
import AdminProjectAddMember from "./Pages/AdminPages/AdminProjectAddMember.tsx";
import AdminProjectChangeUserRole from "./Pages/AdminPages/AdminProjectChangeUserRole.tsx";
import AdminProjectManageMembers from "./Pages/AdminPages/AdminProjectManageMembers.tsx";
import AdminProjectStatistics from "./Pages/AdminPages/AdminProjectStatistics.tsx";
import AdminProjectViewMemberInfo from "./Pages/AdminPages/AdminProjectViewMemberInfo.tsx";
import AdminProjectPage from "./Pages/AdminPages/AdminProjectPage.tsx";
// This is where the routes are mounted // This is where the routes are mounted
const router = createBrowserRouter([ const router = createBrowserRouter([
@ -17,6 +41,14 @@ const router = createBrowserRouter([
path: "/your-projects", path: "/your-projects",
element: <YourProjectsPage />, element: <YourProjectsPage />,
}, },
{
path: "/edit-time-report",
element: <UserEditTimeReportPage />,
},
{
path: "/new-time-report",
element: <UserNewTimeReportPage />,
},
{ {
path: "/project", path: "/project",
element: <UserProjectPage />, element: <UserProjectPage />,
@ -25,6 +57,98 @@ const router = createBrowserRouter([
path: "/register", path: "/register",
element: <Register />, element: <Register />,
}, },
{
path: "/admin-menu",
element: <AdminMenuPage />,
},
{
path: "/project-page",
element: <UserViewTimeReportsPage />,
},
{
path: "/change-role",
element: <PMChangeRole />,
},
{
path: "/other-users-time-reports",
element: <PMOtherUsersTR />,
},
{
path: "/project-members",
element: <PMProjectMembers />,
},
{
path: "/PM-project-page",
element: <PMProjectPage />,
},
{
path: "/PM-time-activity",
element: <PMTotalTimeActivity />,
},
{
path: "/PM-time-role",
element: <PMTotalTimeRole />,
},
{
path: "/PM-unsigned-reports",
element: <PMUnsignedReports />,
},
{
path: "/PM-view-unsigned-report",
element: <PMViewUnsignedReport />,
},
{
path: "/admin-add-project",
element: <AdminAddProject />,
},
{
path: "/admin-add-user",
element: <AdminAddUser />,
},
{
path: "/admin-change-username",
element: <AdminChangeUsername />,
},
{
path: "/admin-manage-projects",
element: <AdminManageProjects />,
},
{
path: "/admin-manage-users",
element: <AdminManageUsers />,
},
{
path: "/admin-menu",
element: <AdminMenuPage />,
},
{
path: "/admin-project-add-member",
element: <AdminProjectAddMember />,
},
{
path: "/admin-project-change-user-role",
element: <AdminProjectChangeUserRole />,
},
{
path: "/admin-project-manage-members",
element: <AdminProjectManageMembers />,
},
{
path: "/admin-project-page",
element: <AdminProjectPage />,
},
{
path: "/admin-project-statistics",
element: <AdminProjectStatistics />,
},
{
path: "/admin-project-view-members",
element: <AdminProjectViewMemberInfo />,
},
{
path: "/admin-view-user",
element: <AdminViewUserInfo />,
},
]); ]);
// Semi-hacky way to get the root element // Semi-hacky way to get the root element