Compare commits

...

73 commits

Author SHA1 Message Date
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
Imbus
7cb2e4a363 Better wsl instructions for recent versions of go & node 2024-03-14 18:24:51 +01:00
Imbus
a291972f82 Makefile targets for generating UML 2024-03-14 18:14:28 +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
Imbus
555a3fa7ec Merge imbs again 2024-03-14 14:38:53 +01:00
Imbus
be04ba148d Merge imbs 2024-03-14 14:37:50 +01:00
Mattias
1b3660eb83 Added more paths in main 2024-03-14 14:33:43 +01:00
dDogge
0bd1fc5397 Merge remote-tracking branch 'refs/remotes/gh/dev' into dev 2024-03-14 14:31:01 +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
dDogge
2b491ed798 Clean up 2024-03-14 14:29:37 +01:00
Mattias
c5d5c389dd Added new paths in main 2024-03-14 14:29:10 +01:00
Imbus
0824a344e3 Target for dumping database content to file 2024-03-14 14:10:25 +01:00
Imbus
dd4809d631 Merge branch 'dev' of github.com:imbus64/TTime into dev 2024-03-14 14:08:27 +01:00
Imbus
3790e8a3c6 Test cleaning 2024-03-14 13:48:56 +01:00
Imbus
d12e3a26ef Role change database interface with corresponding tests 2024-03-14 13:47:04 +01:00
Imbus
6c8abf1f53 Database refactor and test coverage 2024-03-14 13:39:56 +01:00
dDogge
f0745c5a75 Added ChangeUserRole and corresponding test, also GetProjectId should be fixed. 2024-03-14 13:27:57 +01:00
Davenludd
03f350f303 Made a new component for time reporting 2024-03-14 13:21:19 +01:00
Hollgy
327f90e448 Co-authored-by: Imbus <imbus64@users.noreply.github.com> 2024-03-14 11:24:31 +01:00
Hollgy
1f2bff62f9 Refactor, lint removal 2024-03-14 11:23:57 +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
6afe6345cf renewToken API interface 2024-03-13 20:56:47 +01:00
Hollgy
e1bf25148e Co-authored-by: Imbus <imbus64@users.noreply.github.com> 2024-03-13 17:56:31 +01:00
Hollgy
a4b19e32eb Boilerplate added for register, non functional 2024-03-13 17:56:04 +01:00
Imbus
b5c2987281 Add project API interface in frontend 2024-03-13 17:52:56 +01:00
Imbus
1c87380db7 Mounting handler for adding project 2024-03-13 17:52:37 +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
49 changed files with 1277 additions and 96 deletions

7
.gitignore vendored
View file

@ -6,8 +6,13 @@
*.dylib
bin
database.txt
plantuml.jar
db.sqlite3
*.png
diagram.puml
backend/*.png
backend/*.jpg
backend/*.svg
# Test binary, built with `go test -c`
*.test

View file

@ -62,6 +62,21 @@ You should consult the [WSL documentation](https://docs.microsoft.com/en-us/wind
wsl --install -d Ubuntu-22.04 # To get a somewhat recent version of Go
```
After this, you can open a (wsl) terminal and run the commands:
```bash
sudo apt update && sudo apt upgrade
sudo apt install -y make podman
sudo add-apt-repository ppa:longsleep/golang-backports
sudo apt update
sudo apt install golang-go
# For a recent version of node:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install node
```
If you get any errors related to virtualization, you will need to enable virtualization in the BIOS. This is a common issue, and you can find a guide for your specific motherboard online. This is a one-time operation and will not affect your windows installation. This setting is usually called "VT-x" or "AMD-V" and is usually found in the CPU settings. If you can't find it, shoot me a message and I'll find it for you.
If you're **still dead set** on using a vanilla Windows environment, you will need the following:

View file

@ -27,6 +27,10 @@ clean:
$(GOCLEAN)
rm -rf bin
rm -f db.sqlite3
rm -f diagram*
rm -f plantuml.jar
rm -f erd.png
rm -f config.toml
# Test target
test: db.sqlite3
@ -54,6 +58,9 @@ migrate:
db.sqlite3:
make migrate
dbdump:
sqlite3 $(DB_FILE) .dump > database.txt
backup:
mkdir -p backups
sqlite3 $(DB_FILE) .dump | gzip -9 > ./backups/BACKUP_$(DB_FILE)_$(shell date +"%Y-%m-%d_%H:%M:%S").sql.gz
@ -95,6 +102,18 @@ install-lint:
@echo "Installing golangci-lint"
@curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.42.1
# Fetches the latest plantuml.jar and checks its SHA256 hash
plantuml.jar:
curl -sSfL https://github.com/plantuml/plantuml/releases/download/v1.2024.3/plantuml.jar -o plantuml.jar \
&& echo "519a4a7284c6a0357c369e4bb0caf72c4bfbbde851b8c6d6bbdb7af3c01fc82f plantuml.jar" | sha256sum -c
# Generate UML diagrams diagral.png & diagram.svg
.PHONY: uml
uml: plantuml.jar
goplantuml -recursive . > diagram.puml
java -jar plantuml.jar -tpng diagram.puml
java -jar plantuml.jar -tsvg diagram.puml
# Convenience target to install just (requires sudo privileges)
install-just:
@echo "Installing just"

View file

@ -14,18 +14,17 @@ import (
type Database interface {
// Insert a new user into the database, password should be hashed before calling
AddUser(username string, password string) error
RemoveUser(username string) error
PromoteToAdmin(username string) error
GetUserId(username string) (int, error)
AddProject(name string, description string, username string) error
Migrate(dirname string) error
// AddTimeReport(projectname string, start time.Time, end time.Time) error
// AddUserToProject(username string, projectname string) error
// ChangeUserRole(username string, projectname string, role string) error
// AddTimeReport(projectname string, start time.Time, end time.Time) error
// AddUserToProject(username string, projectname string) error
// ChangeUserRole(username string, projectname string, role string) error
GetProjectId(projectname string) (int, error)
AddTimeReport(projectName string, userName string, start time.Time, end time.Time) error
AddUserToProject(username string, projectname string, role string) error
ChangeUserRole(username string, projectname string, role string) error
GetAllUsersProject(projectname string) ([]UserProjectMember, error)
GetAllUsersApplication() ([]string, error)
}
// This struct is a wrapper type that holds the database connection
@ -34,15 +33,24 @@ type Db struct {
*sqlx.DB
}
type UserProjectMember struct {
Username string `db:"username"`
UserRole string `db:"p_role"`
}
//go:embed migrations
var scripts embed.FS
// TODO: Possibly break these out into separate files bundled with the embed package?
const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)"
const projectInsert = "INSERT INTO projects (name, description, owner_user_id) SELECT ?, ?, id FROM users WHERE username = ?"
const promoteToAdmin = "INSERT INTO site_admin (admin_id) SELECT id FROM users WHERE username = ?"
const addTimeReport = "INSERT INTO activity (report_id, activity_nbr, start_time, end_time, break, comment) VALUES (?, ?, ?, ?, ?, ?)" // WIP
const addUserToProject = "INSERT INTO project_member (project_id, user_id, role) VALUES (?, ?, ?)" // WIP
// const changeUserRole = ""
const addTimeReport = `WITH UserLookup AS (SELECT id FROM users WHERE username = ?),
ProjectLookup AS (SELECT id FROM projects WHERE name = ?)
INSERT INTO time_reports (project_id, user_id, start, end)
VALUES ((SELECT id FROM ProjectLookup), (SELECT id FROM UserLookup), ?, ?);`
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 = ?"
// DbConnect connects to the database
func DbConnect(dbpath string) Database {
@ -61,8 +69,8 @@ func DbConnect(dbpath string) Database {
return &Db{db}
}
func (d *Db) AddTimeReport(projectname string, start time.Time, end time.Time, breakTime uint32) error { // WIP
_, err := d.Exec(addTimeReport, projectname, 0, start, end, breakTime, false)
func (d *Db) AddTimeReport(projectName string, userName string, start time.Time, end time.Time) error { // WIP
_, err := d.Exec(addTimeReport, userName, projectName, start, end)
return err
}
@ -79,13 +87,26 @@ func (d *Db) AddUserToProject(username string, projectname string, role string)
panic(err2)
}
_, err3 := d.Exec(addUserToProject, projectid, userid, role)
_, err3 := d.Exec(addUserToProject, userid, projectid, role)
return err3
}
// func (d *Db) ChangeUserRole(username string, projectname string, role string) error {
func (d *Db) ChangeUserRole(username string, projectname string, role string) error {
var userid int
userid, err := d.GetUserId(username)
if err != nil {
panic(err)
}
// }
var projectid int
projectid, err2 := d.GetProjectId(projectname)
if err2 != nil {
panic(err2)
}
_, err3 := d.Exec(changeUserRole, role, userid, projectid)
return err3
}
// AddUser adds a user to the database
func (d *Db) AddUser(username string, password string) error {
@ -110,9 +131,9 @@ func (d *Db) GetUserId(username string) (int, error) {
return id, err
}
func (d *Db) GetProjectId(projectname string) (int, error) { // WIP, denna kan vara goof
func (d *Db) GetProjectId(projectname string) (int, error) {
var id int
err := d.Get(&id, "SELECT id FROM project WHERE project_name = ?", projectname)
err := d.Get(&id, "SELECT id FROM projects WHERE name = ?", projectname)
return id, err
}
@ -122,6 +143,69 @@ func (d *Db) AddProject(name string, description string, username string) error
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.
// This will eventually be used on an embedded directory
func (d *Db) Migrate(dirname string) error {

View file

@ -2,6 +2,7 @@ package database
import (
"testing"
"time"
)
// Tests are not guaranteed to be sequential
@ -92,14 +93,196 @@ func TestPromoteToAdmin(t *testing.T) {
}
}
// func TestAddTimeReport(t *testing.T) {
func TestAddTimeReport(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)
}
// func TestAddUserToProject(t *testing.T) {
err = db.AddProject("testproject", "description", "testuser")
if err != nil {
t.Error("AddProject failed:", err)
}
// }
var now = time.Now()
var then = now.Add(time.Hour)
// func TestChangeUserRole(t *testing.T) {
err = db.AddTimeReport("testproject", "testuser", now, then)
if err != nil {
t.Error("AddTimeReport failed:", err)
}
}
// }
func TestAddUserToProject(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)
}
var now = time.Now()
var then = now.Add(time.Hour)
err = db.AddTimeReport("testproject", "testuser", now, then)
if err != nil {
t.Error("AddTimeReport failed:", err)
}
err = db.AddUserToProject("testuser", "testproject", "user")
if err != nil {
t.Error("AddUserToProject failed:", err)
}
}
func TestChangeUserRole(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)
}
err = db.ChangeUserRole("testuser", "testproject", "admin")
if err != nil {
t.Error("ChangeUserRole failed:", err)
}
}
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")
}
}

View file

@ -1,11 +1,9 @@
CREATE TABLE IF NOT EXISTS projects (
id INTEGER PRIMARY KEY,
projectId TEXT DEFAULT (HEX(RANDOMBLOB(4))) NOT NULL UNIQUE,
name VARCHAR(255) NOT NULL UNIQUE,
description TEXT NOT NULL,
owner_user_id INTEGER NOT NULL,
FOREIGN KEY (owner_user_id) REFERENCES users (id)
);
CREATE INDEX IF NOT EXISTS projects_projectId_index ON projects (projectId);
CREATE INDEX IF NOT EXISTS projects_user_id_index ON projects (owner_user_id);

View file

@ -1,10 +1,11 @@
CREATE TABLE IF NOT EXISTS time_reports (
id INTEGER PRIMARY KEY,
reportId TEXT DEFAULT (HEX(RANDOMBLOB(6))) NOT NULL UNIQUE,
project_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
start DATETIME NOT NULL,
end DATETIME NOT NULL,
FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);
CREATE TRIGGER IF NOT EXISTS time_reports_start_before_end

View file

@ -5,5 +5,5 @@ CREATE TABLE IF NOT EXISTS project_role (
);
-- Insert the possible roles a user can have in a project.
INSERT OR IGNORE INTO project_role (p_role) VALUES ('admin');
INSERT OR IGNORE INTO project_role (p_role) VALUES ('project_manager');
INSERT OR IGNORE INTO project_role (p_role) VALUES ('member');

View file

@ -1,7 +1,7 @@
CREATE TABLE IF NOT EXISTS user_roles (
user_id INTEGER NOT NULL,
project_id INTEGER NOT NULL,
p_role TEXT NOT NULL, -- 'admin' or 'member'
p_role TEXT NOT NULL, -- 'project_manager' or 'member'
FOREIGN KEY (user_id) REFERENCES users (id)
FOREIGN KEY (project_id) REFERENCES projects (id)
FOREIGN KEY (p_role) REFERENCES project_role (p_role)

View file

@ -71,6 +71,7 @@ func main() {
server.Post("/api/loginrenew", gs.LoginRenew)
server.Delete("/api/userdelete", gs.UserDelete) // Perhaps just use POST to avoid headaches
server.Post("/api/project", gs.CreateProject)
// Announce the port we are listening on and start the server
err = server.Listen(fmt.Sprintf(":%d", conf.Port))

View file

@ -1,3 +1,4 @@
import { NewProject, Project } from "../Types/Project";
import { NewUser, User } from "../Types/Users";
// Defines all the methods that an instance of the API must implement
@ -6,6 +7,10 @@ interface API {
registerUser(user: NewUser): Promise<User>;
/** Remove a user */
removeUser(username: string): Promise<User>;
/** Create a project */
createProject(project: NewProject): Promise<Project>;
/** Renew the token */
renewToken(token: string): Promise<string>;
}
// Export an instance of the API
@ -29,4 +34,24 @@ export const api: API = {
body: JSON.stringify(username),
}).then((res) => res.json() as Promise<User>);
},
async createProject(project: NewProject): Promise<Project> {
return fetch("/api/project", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(project),
}).then((res) => res.json() as Promise<Project>);
},
async renewToken(token: string): Promise<string> {
return fetch("/api/loginrenew", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
}).then((res) => res.json() as Promise<string>);
},
};

View file

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

View file

@ -0,0 +1,74 @@
import { useState } from "react";
import { NewUser } from "../Types/Users";
import { api } from "../API/API";
export default function Register(): JSX.Element {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const handleRegister = async (): Promise<void> => {
const newUser: NewUser = { userName: username, password };
await api.registerUser(newUser); // TODO: Handle errors
};
return (
<div>
<div className="w-full max-w-xs">
<form
className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4"
onSubmit={(e) => {
e.preventDefault();
void handleRegister();
}}
>
<h3 className="pb-2">Register new user</h3>
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="username"
>
Username
</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="username"
type="text"
placeholder="Username"
value={username}
onChange={(e) => {
setUsername(e.target.value);
}}
/>
</div>
<div className="mb-6">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="password"
>
Password
</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
id="password"
type="password"
placeholder="Choose your password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
}}
/>
</div>
<div className="flex items-center justify-between">
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>
Register
</button>
</div>
</form>
<p className="text-center text-gray-500 text-xs"></p>
</div>
</div>
);
}

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,5 +1,5 @@
import Button from "../Components/Button";
import Logo from "/src/assets/TTIMElogo.png";
import Logo from "/src/assets/Logo.svg";
import "./LoginPage.css";
import { useEffect } from "react";
import { Link } from "react-router-dom";
@ -69,6 +69,14 @@ function LoginPage(): JSX.Element {
}}
/>
</Link>
<Link to="/register">
<Button
text="Register new user"
onClick={(): void => {
return;
}}
/>
</Link>
</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 {
const content = (
<>
<Link to="/settingsPage">
<h1 className="font-bold text-[30px] mb-[20px]">ProjectNameExample</h1>
</Link>
<h1 className="font-bold text-[30px] mb-[20px]">ProjectNameExample</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]">
<h1 className="font-bold underline text-[30px] cursor-pointer">
Your Time Reports
</h1>
<h1 className="font-bold underline text-[30px] cursor-pointer">
New Time Report
</h1>
<Link to="/new-time-report">
<h1 className="font-bold underline text-[30px] cursor-pointer">
New Time Report
</h1>
</Link>
</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>
</Link>
<h1 className="underline text-[24px] cursor-pointer font-bold">
ProjectNameExample
ProjectNameExample2
</h1>
<h1 className="underline text-[24px] cursor-pointer font-bold">
ProjectNameExample
ProjectNameExample3
</h1>
<h1 className="underline text-[24px] cursor-pointer font-bold">
ProjectNameExample
ProjectNameExample4
</h1>
</div>
</>

View file

@ -0,0 +1,13 @@
export interface Project {
id: number;
name: string;
description: string;
owner: string;
created: string; // This is a date
}
export interface NewProject {
name: string;
description: string;
owner: string;
}

View file

@ -1,11 +1,11 @@
// This is how the API responds
export interface User {
id: number;
name: string;
userName: string;
}
// Used to create a new user
export interface NewUser {
name: string;
userName: string;
password: string;
}

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

@ -5,6 +5,31 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom";
import LoginPage from "./Pages/LoginPage.tsx";
import YourProjectsPage from "./Pages/YourProjectsPage.tsx";
import UserProjectPage from "./Pages/UserPages/UserProjectPage.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
const router = createBrowserRouter([
@ -16,10 +41,114 @@ const router = createBrowserRouter([
path: "/your-projects",
element: <YourProjectsPage />,
},
{
path: "/edit-time-report",
element: <UserEditTimeReportPage />,
},
{
path: "/new-time-report",
element: <UserNewTimeReportPage />,
},
{
path: "/project",
element: <UserProjectPage />,
},
{
path: "/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

6
package-lock.json generated Normal file
View file

@ -0,0 +1,6 @@
{
"name": "TTime",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}