Compare commits

..

26 commits

Author SHA1 Message Date
Imbus
7f5270f536 Merge branch 'gruppDM' of github.com:imbus64/TTime into gruppDM 2024-03-16 18:38:50 +01:00
Davenludd
d58720cdbd Refactor Register component to use InputField component 2024-03-16 18:38:42 +01:00
Mattias
92a4808cdd Created new type for timereport 2024-03-16 18:38:42 +01:00
Mattias
f3c5ce57eb Submit-button has been moved to the timereport 2024-03-16 18:38:42 +01:00
Mattias
3419898c66 Created groundwork for the timereport 2024-03-16 18:38:42 +01:00
Mattias
aae05acedb Added the register component to content 2024-03-16 18:38:42 +01:00
Mattias
a225020f6f Visual fixes 2024-03-16 18:38:42 +01:00
Mattias
2ce480707a Removed duplicate path 2024-03-16 18:38:42 +01:00
Imbus
3526decbad Merge imbs 2024-03-16 17:46:08 +01:00
Imbus
151d6de39b Tygo for go->typescript type generation 2024-03-16 17:43:38 +01:00
Imbus
3c87fd4d8c New api interface 2024-03-16 17:42:28 +01:00
Imbus
17c8a17ebf IF EXISTS condition in salts table 2024-03-16 17:32:51 +01:00
Imbus
47d7d9fe3c Activity type database changes with interface changes in go 2024-03-16 17:22:55 +01:00
Davenludd
7fb220f768 Refactor Register component to use InputField component 2024-03-16 15:14:03 +01:00
Mattias
d71752ad6f Created new type for timereport 2024-03-16 13:11:39 +01:00
Mattias
87c044b5bf Submit-button has been moved to the timereport 2024-03-16 13:11:26 +01:00
Mattias
966f8540df Created groundwork for the timereport 2024-03-16 13:10:46 +01:00
Mattias
d227ffc6ae Added the register component to content 2024-03-16 02:47:39 +01:00
Mattias
a67c44564f Visual fixes 2024-03-16 02:47:17 +01:00
Mattias
148af4e499 Removed duplicate path 2024-03-16 02:46:58 +01:00
dDogge
04e17a1721 Added comments to various functions 2024-03-15 16:57:42 +01:00
Imbus
976ce5900c Merge branch 'dev' of github.com:imbus64/TTime into dev 2024-03-15 16:45:33 +01:00
dDogge
018dc24516 Handler for GetProject from db.go added in global_stage.go 2024-03-15 16:45:26 +01:00
dDogge
581209742a Added GetProject in db.go and corresponding test 2024-03-15 16:45:26 +01:00
dDogge
78f5415d9a Handler for GetProject from db.go added in global_stage.go 2024-03-15 15:28:45 +01:00
dDogge
2468fe8fab Added GetProject in db.go and corresponding test 2024-03-15 15:14:45 +01:00
16 changed files with 459 additions and 151 deletions

View file

@ -118,3 +118,7 @@ uml: plantuml.jar
install-just:
@echo "Installing just"
@curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin
.PHONY: types
types:
tygo generate

View file

@ -21,13 +21,14 @@ type Database interface {
AddProject(name string, description string, username string) error
Migrate(dirname string) error
GetProjectId(projectname string) (int, error)
AddTimeReport(projectName string, userName string, start time.Time, end time.Time) error
AddTimeReport(projectName string, userName string, activityType 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)
GetProjectsForUser(username string) ([]types.Project, error)
GetAllProjects() ([]types.Project, error)
GetProject(projectId int) (types.Project, error)
GetUserRole(username string, projectname string) (string, error)
}
@ -51,8 +52,8 @@ const projectInsert = "INSERT INTO projects (name, description, owner_user_id) S
const promoteToAdmin = "INSERT INTO site_admin (admin_id) SELECT id FROM users WHERE username = ?"
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), ?, ?);`
INSERT INTO time_reports (project_id, user_id, activity_type, 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 = ?"
@ -88,23 +89,34 @@ func DbConnect(dbpath string) Database {
return &Db{db}
}
// GetProjectsForUser retrieves all projects associated with a specific user.
func (d *Db) GetProjectsForUser(username string) ([]types.Project, error) {
var projects []types.Project
err := d.Select(&projects, getProjectsForUser, username)
return projects, err
}
// GetAllProjects retrieves all projects from the database.
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
_, err := d.Exec(addTimeReport, userName, projectName, start, end)
// GetProject retrieves a specific project by its ID.
func (d *Db) GetProject(projectId int) (types.Project, error) {
var project types.Project
err := d.Select(&project, "SELECT * FROM projects WHERE id = ?")
return project, err
}
// AddTimeReport adds a time report for a specific project and user.
func (d *Db) AddTimeReport(projectName string, userName string, activityType string, start time.Time, end time.Time) error { // WIP
_, err := d.Exec(addTimeReport, userName, projectName, activityType, start, end)
return err
}
// AddUserToProject adds a user to a project with a specified role.
func (d *Db) AddUserToProject(username string, projectname string, role string) error { // WIP
var userid int
userid, err := d.GetUserId(username)
@ -122,23 +134,28 @@ func (d *Db) AddUserToProject(username string, projectname string, role string)
return err3
}
// ChangeUserRole changes the role of a user within a project.
func (d *Db) ChangeUserRole(username string, projectname string, role string) error {
// Get the user ID
var userid int
userid, err := d.GetUserId(username)
if err != nil {
panic(err)
}
// Get the project ID
var projectid int
projectid, err2 := d.GetProjectId(projectname)
if err2 != nil {
panic(err2)
}
// Execute the SQL query to change the user's role
_, err3 := d.Exec(changeUserRole, role, userid, projectid)
return err3
}
// GetUserRole retrieves the role of a user within a project.
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)

View file

@ -112,7 +112,7 @@ func TestAddTimeReport(t *testing.T) {
var now = time.Now()
var then = now.Add(time.Hour)
err = db.AddTimeReport("testproject", "testuser", now, then)
err = db.AddTimeReport("testproject", "testuser", "activity", now, then)
if err != nil {
t.Error("AddTimeReport failed:", err)
}
@ -137,7 +137,7 @@ func TestAddUserToProject(t *testing.T) {
var now = time.Now()
var then = now.Add(time.Hour)
err = db.AddTimeReport("testproject", "testuser", now, then)
err = db.AddTimeReport("testproject", "testuser", "activity", now, then)
if err != nil {
t.Error("AddTimeReport failed:", err)
}
@ -343,3 +343,38 @@ func TestGetProjectsForUser(t *testing.T) {
t.Error("GetProjectsForUser failed: expected 1, got", len(projects))
}
}
func TestAddProject(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)
}
// Retrieve the added project to verify its existence
projects, err := db.GetAllProjects()
if err != nil {
t.Error("GetAllProjects failed:", err)
}
// Check if the project was added successfully
found := false
for _, project := range projects {
if project.Name == "testproject" {
found = true
break
}
}
if !found {
t.Error("Added project not found")
}
}

View file

@ -2,10 +2,12 @@ CREATE TABLE IF NOT EXISTS time_reports (
id INTEGER PRIMARY KEY,
project_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
activity_type TEXT 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
FOREIGN KEY (activity_type) REFERENCES activity_types (name) ON DELETE CASCADE
);
CREATE TRIGGER IF NOT EXISTS time_reports_start_before_end

View file

@ -1,7 +1,7 @@
-- It is unclear weather this table will be used
-- Create the table to store hash salts
CREATE TABLE salts (
CREATE TABLE IF NOT EXISTS salts (
id INTEGER PRIMARY KEY,
salt TEXT NOT NULL
);

View file

@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS activity_types (
name TEXT PRIMARY KEY
);
INSERT OR IGNORE INTO activity_types (name) VALUES ('Development');
INSERT OR IGNORE INTO activity_types (name) VALUES ('Meeting');
INSERT OR IGNORE INTO activity_types (name) VALUES ('Administration');
INSERT OR IGNORE INTO activity_types (name) VALUES ('Own Work');
INSERT OR IGNORE INTO activity_types (name) VALUES ('Studies');
INSErt OR IGNORE INTO activity_types (name) VALUES ('Testing');

View file

@ -1,6 +1,7 @@
package handlers
import (
"strconv"
"time"
"ttime/internal/database"
"ttime/internal/types"
@ -225,3 +226,24 @@ func (gs *GState) ProjectRoleChange(c *fiber.Ctx) error {
// Return a success message
return c.SendStatus(fiber.StatusOK)
}
// GetProject retrieves a specific project by its ID
func (gs *GState) GetProject(c *fiber.Ctx) error {
// Extract the project ID from the request parameters or body
projectID := c.Params("projectID")
// Parse the project ID into an integer
projectIDInt, err := strconv.Atoi(projectID)
if err != nil {
return c.Status(400).SendString("Invalid project ID")
}
// Get the project from the database by its ID
project, err := gs.Db.GetProject(projectIDInt)
if err != nil {
return c.Status(500).SendString(err.Error())
}
// Return the project as JSON
return c.JSON(project)
}

9
backend/tygo.yaml Normal file
View file

@ -0,0 +1,9 @@
packages:
- path: "ttime/internal/types"
output_path: "../frontend/src/Types/goTypes.ts"
type_mappings:
time.Time: "string /* RFC3339 */"
null.String: "null | string"
null.Bool: "null | boolean"
uuid.UUID: "string /* uuid */"
uuid.NullUUID: "null | string /* uuid */"

View file

@ -9,7 +9,7 @@ module.exports = {
'plugin:react-hooks/recommended',
'plugin:prettier/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs', 'tailwind.config.js', 'postcss.config.js', 'jest.config.cjs'],
ignorePatterns: ['dist', '.eslintrc.cjs', 'tailwind.config.js', 'postcss.config.js', 'jest.config.cjs', 'goTypes.ts'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh', 'prettier'],
rules: {

View file

@ -1,57 +1,120 @@
import { NewProject, Project } from "../Types/Project";
import { NewUser, User } from "../Types/Users";
// This type of pattern should be hard to misuse
interface APIResponse<T> {
success: boolean;
message?: string;
data?: T;
}
// Note that all protected routes also require a token
// Defines all the methods that an instance of the API must implement
interface API {
/** Register a new user */
registerUser(user: NewUser): Promise<User>;
registerUser(user: NewUser): Promise<APIResponse<User>>;
/** Remove a user */
removeUser(username: string): Promise<User>;
removeUser(username: string, token: string): Promise<APIResponse<User>>;
/** Create a project */
createProject(project: NewProject): Promise<Project>;
createProject(
project: NewProject,
token: string,
): Promise<APIResponse<Project>>;
/** Renew the token */
renewToken(token: string): Promise<string>;
renewToken(token: string): Promise<APIResponse<string>>;
}
// Export an instance of the API
export const api: API = {
async registerUser(user: NewUser): Promise<User> {
return fetch("/api/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(user),
}).then((res) => res.json() as Promise<User>);
async registerUser(user: NewUser): Promise<APIResponse<User>> {
try {
const response = await fetch("/api/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(user),
});
if (!response.ok) {
return { success: false, message: "Failed to register user" };
} else {
const data = (await response.json()) as User;
return { success: true, data };
}
} catch (e) {
return { success: false, message: "Failed to register user" };
}
},
async removeUser(username: string): Promise<User> {
return fetch("/api/userdelete", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(username),
}).then((res) => res.json() as Promise<User>);
async removeUser(
username: string,
token: string,
): Promise<APIResponse<User>> {
try {
const response = await fetch("/api/userdelete", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
body: JSON.stringify(username),
});
if (!response.ok) {
return { success: false, message: "Failed to remove user" };
} else {
const data = (await response.json()) as User;
return { success: true, data };
}
} catch (e) {
return { success: false, message: "Failed to remove 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 createProject(
project: NewProject,
token: string,
): Promise<APIResponse<Project>> {
try {
const response = await fetch("/api/project", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
body: JSON.stringify(project),
});
if (!response.ok) {
return { success: false, message: "Failed to create project" };
} else {
const data = (await response.json()) as Project;
return { success: true, data };
}
} catch (e) {
return { success: false, message: "Failed to create 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>);
async renewToken(token: string): Promise<APIResponse<string>> {
try {
const response = await fetch("/api/loginrenew", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
});
if (!response.ok) {
return { success: false, message: "Failed to renew token" };
} else {
const data = (await response.json()) as string;
return { success: true, data };
}
} catch (e) {
return { success: false, message: "Failed to renew token" };
}
},
};

View file

@ -4,6 +4,32 @@ import { api } from "../API/API";
import Logo from "../assets/Logo.svg";
import Button from "./Button";
function InputField(props: {
label: string;
type: string;
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}): JSX.Element {
return (
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-sans font-bold mb-2"
htmlFor={props.label}
>
{props.label}
</label>
<input
className="appearance-none border-2 border-black rounded-2xl w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id={props.label}
type={props.type}
placeholder={props.label}
value={props.value}
onChange={props.onChange}
/>
</div>
);
}
export default function Register(): JSX.Element {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
@ -14,7 +40,7 @@ export default function Register(): JSX.Element {
};
return (
<div className="flex flex-col h-screen w-screen items-center justify-center">
<div className="flex flex-col h-fit w-screen items-center justify-center">
<div className="border-4 border-black bg-white flex flex-col items-center justify-center h-fit w-fit rounded-3xl content-center pl-20 pr-20">
<form
className="bg-white rounded px-8 pt-6 pb-8 mb-4 items-center justify-center flex flex-col w-fit h-fit"
@ -31,42 +57,22 @@ export default function Register(): JSX.Element {
<h3 className="pb-4 mb-2 text-center font-bold text-[18px]">
Register New User
</h3>
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-sans font-bold mb-2"
htmlFor="username"
>
Username
</label>
<input
className="appearance-none border-2 border-black rounded-2xl 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-sans font-bold mb-2"
htmlFor="password"
>
Password
</label>
<input
className="appearance-none border-2 border-black rounded-2xl w-full py-2 px-3 text-gray-700 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>
<InputField
label="Username"
type="text"
value={username}
onChange={(e) => {
setUsername(e.target.value);
}}
/>
<InputField
label="Password"
type="password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
}}
/>
<div className="flex items-center justify-between">
<Button
text="Register"

View file

@ -1,59 +1,203 @@
function NewTimeReport(): JSX.Element {
const activities = [
"Development",
"Meeting",
"Administration",
"Own Work",
"Studies",
"Testing",
];
import { useState } from "react";
import { TimeReport } from "../Types/TimeReport";
import { api } from "../API/API";
import { useNavigate } from "react-router-dom";
import Button from "./Button";
export default function NewTimeReport(): JSX.Element {
const [week, setWeek] = useState("");
const [development, setDevelopment] = useState("0");
const [meeting, setMeeting] = useState("0");
const [administration, setAdministration] = useState("0");
const [ownwork, setOwnWork] = useState("0");
const [studies, setStudies] = useState("0");
const [testing, setTesting] = useState("0");
const handleNewTimeReport = async (): Promise<void> => {
const newTimeReport: TimeReport = {
week,
development,
meeting,
administration,
ownwork,
studies,
testing,
};
await Promise.resolve();
// await api.registerTimeReport(newTimeReport); This needs to be implemented!
};
const navigate = useNavigate();
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();
<form
onSubmit={(e) => {
if (week === "") {
alert("Please enter a week number");
e.preventDefault();
return;
}
e.preventDefault();
void handleNewTimeReport();
navigate("/project");
}}
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 className="flex flex-col 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"
onChange={(e) => {
const weekNumber = e.target.value.split("-W")[1];
setWeek(weekNumber);
}}
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">
<tr className="h-[10vh]">
<td>Development</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={development}
onChange={(e) => {
setDevelopment(e.target.value);
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
<tr className="h-[10vh]">
<td>Meeting</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={meeting}
onChange={(e) => {
setMeeting(e.target.value);
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
<tr className="h-[10vh]">
<td>Administration</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={administration}
onChange={(e) => {
setAdministration(e.target.value);
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
<tr className="h-[10vh]">
<td>Own Work</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={ownwork}
onChange={(e) => {
setOwnWork(e.target.value);
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
<tr className="h-[10vh]">
<td>Studies</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={studies}
onChange={(e) => {
setStudies(e.target.value);
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
<tr className="h-[10vh]">
<td>Testing</td>
<td>
<input
type="number"
min="0"
className="border-2 border-black rounded-md text-center w-1/2"
value={testing}
onChange={(e) => {
setTesting(e.target.value);
}}
onKeyDown={(event) => {
const keyValue = event.key;
if (!/\d/.test(keyValue) && keyValue !== "Backspace")
event.preventDefault();
}}
/>
</td>
</tr>
</tbody>
</table>
<Button
text="Submit"
onClick={(): void => {
return;
}}
type="submit"
/>
</div>
</form>
</div>
</>
);
}
export default NewTimeReport;

View file

@ -1,18 +1,16 @@
import BasicWindow from "../../Components/BasicWindow";
import Button from "../../Components/Button";
import Register from "../../Components/Register";
function AdminAddUser(): JSX.Element {
const content = <></>;
const content = (
<>
<Register />
</>
);
const buttons = (
<>
<Button
text="Finish"
onClick={(): void => {
return;
}}
type="button"
/>
<Button
text="Back"
onClick={(): void => {

View file

@ -13,13 +13,6 @@ function UserNewTimeReportPage(): JSX.Element {
const buttons = (
<>
<Button
text="Submit"
onClick={(): void => {
return;
}}
type="button"
/>
<Link to="/project">
<Button
text="Back"

View file

@ -0,0 +1,9 @@
export interface TimeReport {
week: string;
development: string;
meeting: string;
administration: string;
ownwork: string;
studies: string;
testing: string;
}

View file

@ -57,10 +57,6 @@ const router = createBrowserRouter([
path: "/register",
element: <Register />,
},
{
path: "/admin-menu",
element: <AdminMenuPage />,
},
{
path: "/project-page",
element: <UserViewTimeReportsPage />,