diff --git a/.gitignore b/.gitignore index 3b1c6d3..05f913b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,9 +15,6 @@ backend/*.png backend/*.jpg backend/*.svg -/go.work.sum -/package-lock.json - # Test binary, built with `go test -c` *.test diff --git a/Justfile b/Justfile index 90fabf6..cb905e4 100644 --- a/Justfile +++ b/Justfile @@ -23,13 +23,10 @@ load-release file: # Tests every part of the project testall: - cd frontend && npm install cd frontend && npm test cd frontend && npm run lint - cd frontend && npm run build cd backend && make test cd backend && make lint - cd backend && make itest # Cleans up everything related to the project clean: remove-podman-containers diff --git a/Makefile b/Makefile index 51fb206..97db62e 100644 --- a/Makefile +++ b/Makefile @@ -13,13 +13,10 @@ remove-podman-containers: # Tests every part of the project testall: - cd frontend && npm install cd frontend && npm test cd frontend && npm run lint - cd frontend && npm run build cd backend && make test cd backend && make lint - cd backend && make itest # Cleans up everything related to the project clean: remove-podman-containers diff --git a/backend/Makefile b/backend/Makefile index 3443e94..331f8d5 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -8,19 +8,17 @@ GOGET = $(GOCMD) get # SQLite database filename DB_FILE = db.sqlite3 -PROC_NAME = ttime_server - # Directory containing migration SQL scripts MIGRATIONS_DIR = internal/database/migrations SAMPLE_DATA_DIR = internal/database/sample_data # Build target build: - $(GOBUILD) -o bin/$(PROC_NAME) main.go + $(GOBUILD) -o bin/server main.go # Run target run: build - ./bin/$(PROC_NAME) + ./bin/server watch: build watchexec -c -w . -r make run @@ -39,16 +37,6 @@ clean: test: db.sqlite3 $(GOTEST) ./... -count=1 -# Integration test target -.PHONY: itest -itest: - pgrep $(PROC_NAME) && echo "Server already running" && exit 1 || true - make build - ./bin/$(PROC_NAME) >/dev/null 2>&1 & - sleep 1 # Adjust if needed - python ../testing.py - pkill $(PROC_NAME) - # Get dependencies target deps: $(GOGET) -v ./... diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index f4c0f6e..7410b16 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -40,9 +40,7 @@ type Database interface { SignWeeklyReport(reportId int, projectManagerId int) error IsSiteAdmin(username string) (bool, error) IsProjectManager(username string, projectname string) (bool, error) - GetProjectTimes(projectName string) (map[string]int, error) - UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error - RemoveProject(projectname string) error + GetTotalTimePerActivity(projectName string) (map[string]int, error) } // This struct is a wrapper type that holds the database connection @@ -500,26 +498,6 @@ func (d *Db) IsProjectManager(username string, projectname string) (bool, error) return manager, err } -func (d *Db) UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error { - query := ` - UPDATE weekly_reports - SET - development_time = ?, - meeting_time = ?, - admin_time = ?, - own_work_time = ?, - study_time = ?, - testing_time = ? - WHERE - user_id = (SELECT id FROM users WHERE username = ?) - AND project_id = (SELECT id FROM projects WHERE name = ?) - AND week = ? - ` - - _, err := d.Exec(query, developmentTime, meetingTime, adminTime, ownWorkTime, studyTime, testingTime, userName, projectName, week) - return err -} - // MigrateSampleData applies sample data to the database. func (d *Db) MigrateSampleData() error { // Insert sample data @@ -559,11 +537,11 @@ func (d *Db) MigrateSampleData() error { return nil } -// GetProjectTimes retrieves a map with times per "Activity" for a given project -func (d *Db) GetProjectTimes(projectName string) (map[string]int, error) { +func (d *Db) GetTotalTimePerActivity(projectName string) (map[string]int, error) { + query := ` SELECT development_time, meeting_time, admin_time, own_work_time, study_time, testing_time - FROM weekly_reports + FROM weekly_reports JOIN projects ON weekly_reports.project_id = projects.id WHERE projects.name = ? ` @@ -596,8 +574,3 @@ func (d *Db) GetProjectTimes(projectName string) (map[string]int, error) { return totalTime, nil } - -func (d *Db) RemoveProject(projectname string) error { - _, err := d.Exec("DELETE FROM projects WHERE name = ?", projectname) - return err -} diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index fe3e6cd..cff70a0 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -481,11 +481,6 @@ func TestGetUnsignedWeeklyReports(t *testing.T) { t.Error("AddUser failed:", err) } - err = db.AddUser("testuser1", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - err = db.AddProject("testproject", "description", "testuser") if err != nil { t.Error("AddProject failed:", err) @@ -496,11 +491,6 @@ func TestGetUnsignedWeeklyReports(t *testing.T) { t.Error("AddWeeklyReport failed:", err) } - err = db.AddWeeklyReport("testproject", "testuser1", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - reports, err := db.GetUnsignedWeeklyReports("testproject") if err != nil { t.Error("GetUnsignedWeeklyReports failed:", err) @@ -770,90 +760,27 @@ func TestIsProjectManager(t *testing.T) { } } -func TestGetProjectTimes(t *testing.T) { - // Initialize +func TestGetTotalTimePerActivity(t *testing.T) { + // Initialize your test database connection db, err := setupState() if err != nil { t.Error("setupState failed:", err) - return } - // Create a user - user := "TeaUser" - password := "Vanilla" - err = db.AddUser(user, password) - if err != nil { - t.Error("AddUser failed:", err) - return - } - - // Create a project - projectName := "ProjectVanilla" - projectDescription := "When tea tastes its best" - err = db.AddProject(projectName, projectDescription, user) // Fix the variable name here - if err != nil { - t.Error("AddProject failed:", err) - return - } - - // Tests the func in db.go - totalTime, err := db.GetProjectTimes(projectName) + // Run the query to get total time per activity + totalTime, err := db.GetTotalTimePerActivity("projecttest") if err != nil { t.Error("GetTotalTimePerActivity failed:", err) - return } // Check if the totalTime map is not nil if totalTime == nil { t.Error("Expected non-nil totalTime map, got nil") - return } - // Define the expected valeus - expectedTotalTime := map[string]int{ - "development": 0, - "meeting": 0, - "admin": 0, - "own_work": 0, - "study": 0, - "testing": 0, - } - - // Compare the expectedTotalTime with the totalTime retrieved from the database - for activity, expectedTime := range expectedTotalTime { - if totalTime[activity] != expectedTime { - t.Errorf("Expected %s time to be %d, got %d", activity, expectedTime, totalTime[activity]) - } - } - - // Insert some data into the database for different activities - err = db.AddWeeklyReport(projectName, user, 1, 1, 3, 2, 1, 4, 5) - if err != nil { - t.Error("Failed to insert data into the database:", err) - return - } - - newTotalTime, err := db.GetProjectTimes(projectName) - if err != nil { - t.Error("GetTotalTimePerActivity failed:", err) - return - } - - newExpectedTotalTime := map[string]int{ - "development": 1, - "meeting": 3, - "admin": 2, - "own_work": 1, - "study": 4, - "testing": 5, - } - - for activity, newExpectedTime := range newExpectedTotalTime { - if newTotalTime[activity] != newExpectedTime { - t.Errorf("Expected %s time to be %d, got %d", activity, newExpectedTime, newTotalTime[activity]) - } - } + // ska lägga till fler assertions } + func TestEnsureManagerOfCreatedProject(t *testing.T) { db, err := setupState() if err != nil { @@ -887,81 +814,3 @@ func TestEnsureManagerOfCreatedProject(t *testing.T) { t.Error("Expected testuser to be a project manager, but it's not.") } } - -// TestUpdateWeeklyReport tests the UpdateWeeklyReport function of the database -func TestUpdateWeeklyReport(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Add a user - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add a project - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - // Add a weekly report - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - // Update the weekly report - err = db.UpdateWeeklyReport("testproject", "testuser", 1, 2, 2, 2, 2, 2, 2) - if err != nil { - t.Error("UpdateWeeklyReport failed:", err) - } - - // Retrieve the updated report - updatedReport, err := db.GetWeeklyReport("testuser", "testproject", 1) - if err != nil { - t.Error("GetWeeklyReport failed:", err) - } - - // Check if the report was updated correctly - if updatedReport.DevelopmentTime != 2 || - updatedReport.MeetingTime != 2 || - updatedReport.AdminTime != 2 || - updatedReport.OwnWorkTime != 2 || - updatedReport.StudyTime != 2 || - updatedReport.TestingTime != 2 { - t.Error("UpdateWeeklyReport failed: report not updated correctly") - } -} - -func TestRemoveProject(t *testing.T) { - db, err := setupAdvancedState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Promote user to Admin - err = db.PromoteToAdmin("demouser") - if err != nil { - t.Error("PromoteToAdmin failed:", err) - } - - // Remove project - err = db.RemoveProject("projecttest") - if err != nil { - t.Error("RemoveProject failed:", err) - } - - // Check if the project was removed - projects, err := db.GetAllProjects() - if err != nil { - t.Error("GetAllProjects failed:", err) - } - if len(projects) != 0 { - t.Error("RemoveProject failed: expected 0, got", len(projects)) - } - -} - \ No newline at end of file diff --git a/backend/internal/database/sample_data/0010_sample_data.sql b/backend/internal/database/sample_data/0010_sample_data.sql index ab74f1a..092fbb0 100644 --- a/backend/internal/database/sample_data/0010_sample_data.sql +++ b/backend/internal/database/sample_data/0010_sample_data.sql @@ -7,8 +7,6 @@ VALUES ("user", "123"); INSERT OR IGNORE INTO users(username, password) VALUES ("user2", "123"); -INSERT OR IGNORE INTO site_admin VALUES (1); - INSERT OR IGNORE INTO projects(name,description,owner_user_id) VALUES ("projecttest","test project", 1); diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 0db4340..6b197bb 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -29,8 +29,6 @@ type GlobalState interface { ChangeUserName(c *fiber.Ctx) error // WIP GetAllUsersProject(c *fiber.Ctx) error // WIP GetUnsignedReports(c *fiber.Ctx) error // - UpdateWeeklyReport(c *fiber.Ctx) error - RemoveProject(c *fiber.Ctx) error } // "Constructor" diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index d63d7eb..603f4cd 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -233,83 +233,3 @@ func (gs *GState) IsProjectManagerHandler(c *fiber.Ctx) error { // Return the result as JSON return c.JSON(fiber.Map{"isProjectManager": isManager}) } - -func (gs *GState) GetProjectTimesHandler(c *fiber.Ctx) error { - // Get the username from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Get project - projectName := c.Params("projectName") - if projectName == "" { - log.Info("No project name provided") - return c.Status(400).SendString("No project name provided") - } - - // Get all users in the project and roles - userProjects, err := gs.Db.GetAllUsersProject(projectName) - if err != nil { - log.Info("Error getting users in project:", err) - return c.Status(500).SendString(err.Error()) - } - - // If the user is member - isMember := false - for _, userProject := range userProjects { - if userProject.Username == username { - isMember = true - break - } - } - - // If the user is admin - if !isMember { - isAdmin, err := gs.Db.IsSiteAdmin(username) - if err != nil { - log.Info("Error checking admin status:", err) - return c.Status(500).SendString(err.Error()) - } - if !isAdmin { - log.Info("User is neither a project member nor a site admin:", username) - return c.Status(403).SendString("User is neither a project member nor a site admin") - } - } - - // Get project times - projectTimes, err := gs.Db.GetProjectTimes(projectName) - if err != nil { - log.Info("Error getting project times:", err) - return c.Status(500).SendString(err.Error()) - } - - // Return project times as JSON - log.Info("Returning project times for project:", projectName) - return c.JSON(projectTimes) -} - -func (gs *GState) RemoveProject(c *fiber.Ctx) error { - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Check if the user is a site admin - isAdmin, err := gs.Db.IsSiteAdmin(username) - if err != nil { - log.Info("Error checking admin status:", err) - return c.Status(500).SendString(err.Error()) - } - - if !isAdmin { - log.Info("User is not a site admin:", username) - return c.Status(403).SendString("User is not a site admin") - } - - projectName := c.Params("projectName") - - if err := gs.Db.RemoveProject(projectName); err != nil { - return c.Status(500).SendString((err.Error())) - } - - return c.Status(200).SendString("Project deleted") -} diff --git a/backend/internal/handlers/handlers_report_related.go b/backend/internal/handlers/handlers_report_related.go index 52e1564..534050a 100644 --- a/backend/internal/handlers/handlers_report_related.go +++ b/backend/internal/handlers/handlers_report_related.go @@ -122,7 +122,7 @@ func (gs *GState) GetUnsignedReports(c *fiber.Ctx) error { projectManagerUsername := claims["name"].(string) // Extract project name and week from query parameters - projectName := c.Params("projectName") + projectName := c.Query("projectName") log.Info("Getting unsigned reports for") @@ -177,37 +177,3 @@ func (gs *GState) GetWeeklyReportsUserHandler(c *fiber.Ctx) error { // Return the list of reports as JSON return c.JSON(reports) } - -func (gs *GState) UpdateWeeklyReport(c *fiber.Ctx) error { - // Extract the necessary parameters from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Parse the request body into an UpdateWeeklyReport struct - var updateReport types.UpdateWeeklyReport - if err := c.BodyParser(&updateReport); err != nil { - log.Info("Error parsing weekly report") - return c.Status(400).SendString(err.Error()) - } - - // Make sure all the fields of the report are valid - if updateReport.Week < 1 || updateReport.Week > 52 { - log.Info("Invalid week number") - return c.Status(400).SendString("Invalid week number") - } - - if updateReport.DevelopmentTime < 0 || updateReport.MeetingTime < 0 || updateReport.AdminTime < 0 || updateReport.OwnWorkTime < 0 || updateReport.StudyTime < 0 || updateReport.TestingTime < 0 { - log.Info("Invalid time report") - return c.Status(400).SendString("Invalid time report") - } - - // Update the weekly report in the database - if err := gs.Db.UpdateWeeklyReport(updateReport.ProjectName, username, updateReport.Week, updateReport.DevelopmentTime, updateReport.MeetingTime, updateReport.AdminTime, updateReport.OwnWorkTime, updateReport.StudyTime, updateReport.TestingTime); err != nil { - log.Info("Error updating weekly report in db:", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("Weekly report updated") - return c.Status(200).SendString("Weekly report updated") -} diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index bc4ae2d..116ce90 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -59,9 +59,9 @@ func (gs *GState) UserDelete(c *fiber.Ctx) error { // Read username from Locals auth_username := c.Locals("user").(*jwt.Token).Claims.(jwt.MapClaims)["name"].(string) - if username == auth_username { - log.Info("User tried to delete itself") - return c.Status(403).SendString("You can't delete yourself") + if username != auth_username { + log.Info("User tried to delete another user") + return c.Status(403).SendString("You can only delete yourself") } if err := gs.Db.RemoveUser(username); err != nil { @@ -207,8 +207,8 @@ func (gs *GState) GetAllUsersProject(c *fiber.Ctx) error { // @Accept json // @Produce plain // @Param NewUser body types.NewUser true "user info" -// @Success 200 {json} json "Successfully promoted user" -// @Failure 400 {string} string "Bad request" +// @Success 200 {json} json "Successfully prometed user" +// @Failure 400 {string} string "bad request" // @Failure 401 {string} string "Unauthorized" // @Failure 500 {string} string "Internal server error" // @Router /promoteToAdmin [post] @@ -234,33 +234,33 @@ func (gs *GState) PromoteToAdmin(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) } -// ChangeUserName changes a user's username in the database +// Changes a users name in the database func (gs *GState) ChangeUserName(c *fiber.Ctx) error { - // Check token and get username of current user + + //check token and get username of current user user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) - adminUsername := claims["name"].(string) - log.Info(adminUsername) - + projectManagerUsername := claims["name"].(string) + log.Info(projectManagerUsername) // Extract the necessary parameters from the request - data := new(types.StrNameChange) + data := new(types.NameChange) if err := c.BodyParser(data); err != nil { - log.Info("Error parsing username") + log.Info("error parsing username, project or role") return c.Status(400).SendString(err.Error()) } - // Check if the current user is an admin - isAdmin, err := gs.Db.IsSiteAdmin(adminUsername) - if err != nil { - log.Warn("Error checking if admin:", err) + // dubble diping and checcking if current user is + + if ismanager, err := gs.Db.IsProjectManager(projectManagerUsername, c.Params(data.Name)); err != nil { + log.Warn("Error checking if projectmanager:", err) return c.Status(500).SendString(err.Error()) - } else if !isAdmin { - log.Warn("Tried changing name when not admin") - return c.Status(401).SendString("You cannot change name unless you are an admin") + } else if !ismanager { + log.Warn("tried changing name when not projectmanager:", err) + return c.Status(401).SendString("you can not change name when not projectmanager") } - // Change the user's name in the database - if err := gs.Db.ChangeUserName(data.PrevName, data.NewName); err != nil { + // Change the user's name within the project in the database + if err := gs.Db.ChangeUserName(projectManagerUsername, data.Name); err != nil { return c.Status(500).SendString(err.Error()) } diff --git a/backend/internal/types/WeeklyReport.go b/backend/internal/types/WeeklyReport.go index 234781b..8d22b6a 100644 --- a/backend/internal/types/WeeklyReport.go +++ b/backend/internal/types/WeeklyReport.go @@ -65,24 +65,3 @@ type WeeklyReport struct { // The project manager who signed it SignedBy *int `json:"signedBy" db:"signed_by"` } - -type UpdateWeeklyReport struct { - // The name of the project, as it appears in the database - ProjectName string `json:"projectName"` - // The name of the user - UserName string `json:"userName"` - // The week number - Week int `json:"week"` - // Total time spent on development - DevelopmentTime int `json:"developmentTime"` - // Total time spent in meetings - MeetingTime int `json:"meetingTime"` - // Total time spent on administrative tasks - AdminTime int `json:"adminTime"` - // Total time spent on personal projects - OwnWorkTime int `json:"ownWorkTime"` - // Total time spent on studying - StudyTime int `json:"studyTime"` - // Total time spent on testing - TestingTime int `json:"testingTime"` -} diff --git a/backend/main.go b/backend/main.go index 669bbc7..835524c 100644 --- a/backend/main.go +++ b/backend/main.go @@ -33,12 +33,6 @@ import ( // @externalDocs.description OpenAPI // @externalDocs.url https://swagger.io/resources/open-api/ -/** -Main function for starting the server and initializing configurations. -Reads configuration from file, pretty prints it, connects to the database, -migrates it, and sets up routes for the server. -*/ - func main() { conf, err := config.ReadConfigFromFile("config.toml") if err != nil { @@ -98,7 +92,7 @@ func main() { server.Get("/api/project/:projectId", gs.GetProject) server.Get("/api/project/getAllUsers", gs.GetAllUsersProject) server.Get("/api/getWeeklyReport", gs.GetWeeklyReport) - server.Get("/api/getUnsignedReports/:projectName", gs.GetUnsignedReports) + server.Get("/api/getUnsignedReports", gs.GetUnsignedReports) server.Post("/api/signReport", gs.SignReport) server.Put("/api/addUserToProject", gs.AddUserToProjectHandler) server.Put("/api/changeUserName", gs.ChangeUserName) @@ -108,8 +102,6 @@ func main() { server.Get("/api/checkIfProjectManager/:projectName", gs.IsProjectManagerHandler) server.Post("/api/ProjectRoleChange", gs.ProjectRoleChange) server.Get("/api/getUsersProject/:projectName", gs.ListAllUsersProject) - server.Put("/api/updateWeeklyReport", gs.UpdateWeeklyReport) - server.Delete("/api/removeProject/:projectName", gs.RemoveProject) // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) diff --git a/container/Containerfile b/container/Containerfile index f9cb39d..ecd2f84 100644 --- a/container/Containerfile +++ b/container/Containerfile @@ -13,6 +13,7 @@ FROM docker.io/golang:alpine as go RUN apk add gcompat RUN apk add gcc RUN apk add musl-dev +RUN apk add make RUN apk add sqlite WORKDIR /build ADD backend/go.mod backend/go.sum ./ @@ -23,7 +24,9 @@ RUN go mod download # Add the source code ADD backend . -RUN go build -o server +RUN make migrate + +# RUN go build -o server RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -o ./server ./main.go # Strip the binary for a smaller image @@ -34,7 +37,6 @@ FROM docker.io/alpine:latest as runner RUN adduser -D nonroot RUN addgroup nonroot nonroot WORKDIR /app -RUN chown nonroot:nonroot /app # Copy the frontend SPA build into public COPY --from=client /build/dist static @@ -42,6 +44,9 @@ COPY --from=client /build/dist static # Copy the server binary COPY --from=go /build/server server +# Copy the database +COPY --from=go /build/db.sqlite3 db.sqlite3 + # Expose port 8080 EXPOSE 8080 diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 0160e15..0859748 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -4,10 +4,7 @@ import { User, Project, NewProject, - UserProjectMember, WeeklyReport, - StrNameChange, - NewProjMember, } from "../Types/goTypes"; /** @@ -49,6 +46,7 @@ interface API { * @returns {Promise>} A promise containing the API response indicating if the user is a project manager. */ checkIfProjectManager( + username: string, projectName: string, token: string, ): Promise>; @@ -86,7 +84,7 @@ interface API { submitWeeklyReport( weeklyReport: NewWeeklyReport, token: string, - ): Promise>; + ): Promise>; /** Gets a weekly report for a specific user, project and week * @param {string} projectName The name of the project. @@ -129,30 +127,6 @@ interface API { * @returns {Promise>} A promise resolving to an API response containing the list of users. */ getAllUsers(token: string): Promise>; - /** Gets all users in a project from name*/ - getAllUsersProject( - projectName: string, - token: string, - ): Promise>; - /** - * Changes the username of a user in the database. - * @param {StrNameChange} data The object containing the previous and new username. - * @param {string} token The authentication token. - * @returns {Promise>} A promise resolving to an API response. - */ - changeUserName( - data: StrNameChange, - token: string, - ): Promise>; - addUserToProject( - user: NewProjMember, - token: string, - ): Promise>; - - removeProject( - projectName: string, - token: string, - ): Promise>; } /** An instance of the API */ @@ -190,17 +164,19 @@ export const api: API = { ): Promise> { try { const response = await fetch(`/api/userdelete/${username}`, { - method: "DELETE", + method: "POST", headers: { "Content-Type": "application/json", Authorization: "Bearer " + token, }, body: JSON.stringify(username), }); + if (!response.ok) { - return { success: false, message: "Could not remove user" }; + return { success: false, message: "Failed to remove user" }; } else { - return { success: true }; + const data = (await response.json()) as User; + return { success: true, data }; } } catch (e) { return { success: false, message: "Failed to remove user" }; @@ -208,20 +184,19 @@ export const api: API = { }, async checkIfProjectManager( + username: string, projectName: string, token: string, ): Promise> { try { - const response = await fetch( - `/api/checkIfProjectManager/${projectName}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, + const response = await fetch("/api/checkIfProjectManager", { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, }, - ); + body: JSON.stringify({ username, projectName }), + }); if (!response.ok) { return { @@ -233,7 +208,7 @@ export const api: API = { return { success: true, data }; } } catch (e) { - return { success: false, message: "Failed to check if project manager" }; + return { success: false, message: "fuck" }; } }, @@ -262,30 +237,6 @@ export const api: API = { } }, - async addUserToProject( - user: NewProjMember, - token: string, - ): Promise> { - try { - const response = await fetch("/api/addUserToProject", { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - body: JSON.stringify(user), - }); - - if (!response.ok) { - return { success: false, message: "Failed to add member" }; - } else { - return { success: true, message: "Added member" }; - } - } catch (e) { - return { success: false, message: "Failed to add member" }; - } - }, - async renewToken(token: string): Promise> { try { const response = await fetch("/api/loginrenew", { @@ -337,7 +288,7 @@ export const api: API = { async submitWeeklyReport( weeklyReport: NewWeeklyReport, token: string, - ): Promise> { + ): Promise> { try { const response = await fetch("/api/submitWeeklyReport", { method: "POST", @@ -355,8 +306,8 @@ export const api: API = { }; } - const data = await response.text(); - return { success: true, message: data }; + const data = (await response.json()) as NewWeeklyReport; + return { success: true, data }; } catch (e) { return { success: false, @@ -497,88 +448,4 @@ export const api: API = { }); } }, - //Gets all users in a project - async getAllUsersProject( - projectName: string, - token: string, - ): Promise> { - try { - const response = await fetch(`/api/getUsersProject/${projectName}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return Promise.resolve({ - success: false, - message: "Failed to get users", - }); - } else { - const data = (await response.json()) as UserProjectMember[]; - return Promise.resolve({ success: true, data }); - } - } catch (e) { - return Promise.resolve({ - success: false, - message: "API is not ok", - }); - } - }, - - async changeUserName( - data: StrNameChange, - token: string, - ): Promise> { - try { - const response = await fetch("/api/changeUserName", { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - body: JSON.stringify(data), - }); - - if (!response.ok) { - return { success: false, message: "Failed to change username" }; - } else { - return { success: true }; - } - } catch (e) { - return { success: false, message: "Failed to change username" }; - } - }, - - async removeProject( - projectName: string, - token: string, - ): Promise> { - try { - const response = await fetch(`/api/projectdelete/${projectName}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return Promise.resolve({ - success: false, - message: "Failed to remove project", - }); - } else { - const data = await response.text(); - return Promise.resolve({ success: true, message: data }); - } - } catch (e) { - return Promise.resolve({ - success: false, - message: "Failed to remove project", - }); - } - }, }; diff --git a/frontend/src/Components/AddMember.tsx b/frontend/src/Components/AddMember.tsx deleted file mode 100644 index d29be68..0000000 --- a/frontend/src/Components/AddMember.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { APIResponse, api } from "../API/API"; -import { NewProjMember } from "../Types/goTypes"; - -/** - * Tries to add a member to a project - * @param {Object} props - A NewProjMember - * @returns {boolean} True if added, false if not - */ -function AddMember(props: { memberToAdd: NewProjMember }): boolean { - let added = false; - if ( - props.memberToAdd.username === "" || - props.memberToAdd.role === "" || - props.memberToAdd.projectname === "" - ) { - alert("All fields must be filled before adding"); - return added; - } - api - .addUserToProject( - props.memberToAdd, - localStorage.getItem("accessToken") ?? "", - ) - .then((response: APIResponse) => { - if (response.success) { - alert("Member added"); - added = true; - } else { - alert("Member not added"); - console.error(response.message); - } - }) - .catch((error) => { - console.error("An error occurred during member add:", error); - }); - return added; -} - -export default AddMember; diff --git a/frontend/src/Components/AddUserToProject.tsx b/frontend/src/Components/AddUserToProject.tsx deleted file mode 100644 index 9f4439b..0000000 --- a/frontend/src/Components/AddUserToProject.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useState } from "react"; -import { NewProjMember } from "../Types/goTypes"; -import Button from "./Button"; -import GetAllUsers from "./GetAllUsers"; -import AddMember from "./AddMember"; -import BackButton from "./BackButton"; - -/** - * Provides UI for adding a member to a project. - * @returns {JSX.Element} - Returns the component UI for adding a member - */ -function AddUserToProject(): JSX.Element { - const [name, setName] = useState(""); - const [users, setUsers] = useState([]); - const [role, setRole] = useState(""); - GetAllUsers({ setUsersProp: setUsers }); - - const handleClick = (): boolean => { - const newMember: NewProjMember = { - username: name, - projectname: localStorage.getItem("projectName") ?? "", - role: role, - }; - return AddMember({ memberToAdd: newMember }); - }; - - return ( -
-

- User chosen: [{name}] -

-

- Role chosen: [{role}] -

-

- Project chosen: [{localStorage.getItem("projectName") ?? ""}] -

-

Choose role:

-
-
    -
  • { - setRole("member"); - }} - > - {"Member"} -
  • -
  • { - setRole("project_manager"); - }} - > - {"Project manager"} -
  • -
-
-

Choose user:

-
-
    -
    - {users.map((user) => ( -
  • { - setName(user); - }} - > - {user} -
  • - ))} -
-
-
-
-

-
- ); -} - -export default AddUserToProject; diff --git a/frontend/src/Components/AdminUserList.tsx b/frontend/src/Components/AdminUserList.tsx new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/Components/AllTimeReportsInProject.tsx b/frontend/src/Components/AllTimeReportsInProject.tsx index 4fa9ad8..14d3af9 100644 --- a/frontend/src/Components/AllTimeReportsInProject.tsx +++ b/frontend/src/Components/AllTimeReportsInProject.tsx @@ -13,24 +13,24 @@ function AllTimeReportsInProject(): JSX.Element { const { projectName } = useParams(); const [weeklyReports, setWeeklyReports] = useState([]); + const getWeeklyReports = async (): Promise => { + const token = localStorage.getItem("accessToken") ?? ""; + const response = await api.getWeeklyReportsForUser( + projectName ?? "", + token, + ); + console.log(response); + if (response.success) { + setWeeklyReports(response.data ?? []); + } else { + console.error(response.message); + } + }; + // Call getProjects when the component mounts useEffect(() => { - const getWeeklyReports = async (): Promise => { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getWeeklyReportsForUser( - projectName ?? "", - token, - ); - console.log(response); - if (response.success) { - setWeeklyReports(response.data ?? []); - } else { - console.error(response.message); - } - }; - void getWeeklyReports(); - }, [projectName]); + }, []); return ( <> diff --git a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx deleted file mode 100644 index 09ca6dc..0000000 --- a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx +++ /dev/null @@ -1,103 +0,0 @@ -//Info: This component is used to display all the time reports for a project. It will display the week number, -//total time spent, and if the report has been signed or not. The user can click on a report to edit it. -import { useEffect, useState } from "react"; -import { NewWeeklyReport } from "../Types/goTypes"; -import { Link, useParams } from "react-router-dom"; - -/** - * Renders a component that displays all the time reports for a specific project. - * @returns {JSX.Element} representing the component. - */ -function AllTimeReportsInProject(): JSX.Element { - const { username } = useParams(); - const { projectName } = useParams(); - const [weeklyReports, setWeeklyReports] = useState([]); - - /* // Call getProjects when the component mounts - useEffect(() => { - const getWeeklyReports = async (): Promise => { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getWeeklyReportsForUser( - projectName ?? "", - token, - ); - console.log(response); - if (response.success) { - setWeeklyReports(response.data ?? []); - } else { - console.error(response.message); - } - }; */ - // Mock data - const getWeeklyReports = async (): Promise => { - // Simulate a delay - await Promise.resolve(); - const mockWeeklyReports: NewWeeklyReport[] = [ - { - projectName: "Project 1", - week: 1, - developmentTime: 10, - meetingTime: 2, - adminTime: 1, - ownWorkTime: 3, - studyTime: 4, - testingTime: 5, - }, - { - projectName: "Project 1", - week: 2, - developmentTime: 8, - meetingTime: 2, - adminTime: 1, - ownWorkTime: 3, - studyTime: 4, - testingTime: 5, - }, - // Add more reports as needed - ]; - - // Use the mock data instead of the real data - setWeeklyReports(mockWeeklyReports); - }; - useEffect(() => { - void getWeeklyReports(); - }, []); - - return ( - <> -

{username}'s Time Reports

-
- {weeklyReports.map((newWeeklyReport, index) => ( - -
-

- {"Week: "} - {newWeeklyReport.week} -

-

- {"Total Time: "} - {newWeeklyReport.developmentTime + - newWeeklyReport.meetingTime + - newWeeklyReport.adminTime + - newWeeklyReport.ownWorkTime + - newWeeklyReport.studyTime + - newWeeklyReport.testingTime}{" "} - min -

-

- {"Signed: "} - NO -

-
- - ))} -
- - ); -} - -export default AllTimeReportsInProject; diff --git a/frontend/src/Components/ChangeUsername.tsx b/frontend/src/Components/ChangeUsername.tsx index e297a04..3c35e94 100644 --- a/frontend/src/Components/ChangeUsername.tsx +++ b/frontend/src/Components/ChangeUsername.tsx @@ -1,48 +1,23 @@ import React, { useState } from "react"; import InputField from "./InputField"; -import { api } from "../API/API"; function ChangeUsername(): JSX.Element { const [newUsername, setNewUsername] = useState(""); - const [errorMessage, setErrorMessage] = useState(""); const handleChange = (e: React.ChangeEvent): void => { setNewUsername(e.target.value); }; - const handleSubmit = async (): Promise => { - try { - // Call the API function to change the username - const token = localStorage.getItem("accessToken"); - if (!token) { - throw new Error("Access token not found"); - } - - const response = await api.changeUserName( - { prevName: "currentName", newName: newUsername }, - token, - ); - - if (response.success) { - // Optionally, add a success message or redirect the user - console.log("Username changed successfully"); - } else { - // Handle the error message - console.error("Failed to change username:", response.message); - setErrorMessage(response.message ?? "Failed to change username"); - } - } catch (error) { - console.error("Error changing username:", error); - // Optionally, handle the error - setErrorMessage("Failed to change username"); - } - }; - - const handleButtonClick = (): void => { - handleSubmit().catch((error) => { - console.error("Error in handleSubmit:", error); - }); - }; + // const handleSubmit = async (): Promise => { + // try { + // // Call the API function to update the username + // await api.updateUsername(newUsername); + // // Optionally, add a success message or redirect the user + // } catch (error) { + // console.error("Error updating username:", error); + // // Optionally, handle the error + // } + // }; return (
@@ -52,8 +27,6 @@ function ChangeUsername(): JSX.Element { value={newUsername} onChange={handleChange} /> - {errorMessage &&
{errorMessage}
} -
); } diff --git a/frontend/src/Components/DeleteUser.tsx b/frontend/src/Components/DeleteUser.tsx index d1dbc7f..db49724 100644 --- a/frontend/src/Components/DeleteUser.tsx +++ b/frontend/src/Components/DeleteUser.tsx @@ -11,6 +11,7 @@ import { api, APIResponse } from "../API/API"; */ function DeleteUser(props: { usernameToDelete: string }): boolean { + //console.log(props.usernameToDelete); FOR DEBUG let removed = false; api .removeUser( @@ -19,16 +20,12 @@ function DeleteUser(props: { usernameToDelete: string }): boolean { ) .then((response: APIResponse) => { if (response.success) { - alert("User has been deleted!"); - location.reload(); removed = true; } else { - alert("User has not been deleted"); console.error(response.message); } }) .catch((error) => { - alert("User has not been deleted"); console.error("An error occurred during creation:", error); }); return removed; diff --git a/frontend/src/Components/DisplayUnsignedReports.tsx b/frontend/src/Components/DisplayUnsignedReports.tsx deleted file mode 100644 index 780f20c..0000000 --- a/frontend/src/Components/DisplayUnsignedReports.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { useState, useEffect } from "react"; -import { Link, useParams } from "react-router-dom"; - -interface UnsignedReports { - projectName: string; - username: string; - week: number; - signed: boolean; -} - -/** - * Renders a component that displays the projects a user is a part of and links to the projects start-page. - * @returns The JSX element representing the component. - */ -function DisplayUserProject(): JSX.Element { - const { projectName } = useParams(); - const [unsignedReports, setUnsignedReports] = useState([]); - //const navigate = useNavigate(); - - // const getUnsignedReports = async (): Promise => { - // const token = localStorage.getItem("accessToken") ?? ""; - // const response = await api.getUserProjects(token); - // console.log(response); - // if (response.success) { - // setUnsignedReports(response.data ?? []); - // } else { - // console.error(response.message); - // } - // }; - - // const handleReportClick = async (projectName: string): Promise => { - // const username = localStorage.getItem("username") ?? ""; - // const token = localStorage.getItem("accessToken") ?? ""; - // const response = await api.checkIfProjectManager( - // username, - // projectName, - // token, - // ); - // if (response.success) { - // if (response.data) { - // navigate(`/PMProjectPage/${projectName}`); - // } else { - // navigate(`/project/${projectName}`); - // } - // } else { - // // handle error - // console.error(response.message); - // } - // }; - - const getUnsignedReports = async (): Promise => { - // Simulate a delay - await Promise.resolve(); - - // Use mock data - const reports: UnsignedReports[] = [ - { - projectName: "projecttest", - username: "user1", - week: 2, - signed: false, - }, - { - projectName: "projecttest", - username: "user2", - week: 2, - signed: false, - }, - { - projectName: "projecttest", - username: "user3", - week: 2, - signed: false, - }, - { - projectName: "projecttest", - username: "user4", - week: 2, - signed: false, - }, - ]; - - // Set the state with the mock data - setUnsignedReports(reports); - }; - - // Call getProjects when the component mounts - useEffect(() => { - void getUnsignedReports(); - }, []); - - return ( - <> -

- All Unsigned Reports In: {projectName}{" "} -

-
- {unsignedReports.map( - (unsignedReport: UnsignedReports, index: number) => ( -

-
-
-

{unsignedReport.username}

- Week: -

{unsignedReport.week}

- Signed: -

NO

-
-
-
- -

- View Report -

- -
-
-
-

- ), - )} -
- - ); -} - -export default DisplayUserProject; diff --git a/frontend/src/Components/DisplayUserProjects.tsx b/frontend/src/Components/DisplayUserProjects.tsx index 29e4bcb..f4fd782 100644 --- a/frontend/src/Components/DisplayUserProjects.tsx +++ b/frontend/src/Components/DisplayUserProjects.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; import { Project } from "../Types/goTypes"; -import { useNavigate } from "react-router-dom"; +import { Link } from "react-router-dom"; import { api } from "../API/API"; /** @@ -9,7 +9,6 @@ import { api } from "../API/API"; */ function DisplayUserProject(): JSX.Element { const [projects, setProjects] = useState([]); - const navigate = useNavigate(); const getProjects = async (): Promise => { const token = localStorage.getItem("accessToken") ?? ""; @@ -22,21 +21,6 @@ function DisplayUserProject(): JSX.Element { } }; - const handleProjectClick = async (projectName: string): Promise => { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.checkIfProjectManager(projectName, token); - if (response.success) { - if (response.data) { - navigate(`/PMProjectPage/${projectName}`); - } else { - navigate(`/project/${projectName}`); - } - } else { - // handle error - console.error(response.message); - } - }; - // Call getProjects when the component mounts useEffect(() => { void getProjects(); @@ -46,15 +30,12 @@ function DisplayUserProject(): JSX.Element { <>

Your Projects

- {projects.map((project) => ( -
void handleProjectClick(project.name)} - key={project.id} - > + {projects.map((project, index) => ( +

{project.name}

-
+ ))}
diff --git a/frontend/src/Components/EditWeeklyReport.tsx b/frontend/src/Components/EditWeeklyReport.tsx index 384359e..76c2b94 100644 --- a/frontend/src/Components/EditWeeklyReport.tsx +++ b/frontend/src/Components/EditWeeklyReport.tsx @@ -18,47 +18,46 @@ export default function GetWeeklyReport(): JSX.Element { const [testingTime, setTestingTime] = useState(0); const token = localStorage.getItem("accessToken") ?? ""; - const { projectName, fetchedWeek } = useParams<{ - projectName: string; - fetchedWeek: string; - }>(); - console.log(projectName, fetchedWeek); + const username = localStorage.getItem("username") ?? ""; + const { projectName } = useParams(); + const { fetchedWeek } = useParams(); + + const fetchWeeklyReport = async (): Promise => { + const response = await api.getWeeklyReport( + username, + projectName ?? "", + fetchedWeek?.toString() ?? "0", + token, + ); + + if (response.success) { + const report: WeeklyReport = response.data ?? { + reportId: 0, + userId: 0, + projectId: 0, + week: 0, + developmentTime: 0, + meetingTime: 0, + adminTime: 0, + ownWorkTime: 0, + studyTime: 0, + testingTime: 0, + }; + setWeek(report.week); + setDevelopmentTime(report.developmentTime); + setMeetingTime(report.meetingTime); + setAdminTime(report.adminTime); + setOwnWorkTime(report.ownWorkTime); + setStudyTime(report.studyTime); + setTestingTime(report.testingTime); + } else { + console.error("Failed to fetch weekly report:", response.message); + } + }; useEffect(() => { - const fetchWeeklyReport = async (): Promise => { - const response = await api.getWeeklyReport( - projectName ?? "", - fetchedWeek ?? "", - token, - ); - - if (response.success) { - const report: WeeklyReport = response.data ?? { - reportId: 0, - userId: 0, - projectId: 0, - week: 0, - developmentTime: 0, - meetingTime: 0, - adminTime: 0, - ownWorkTime: 0, - studyTime: 0, - testingTime: 0, - }; - setWeek(report.week); - setDevelopmentTime(report.developmentTime); - setMeetingTime(report.meetingTime); - setAdminTime(report.adminTime); - setOwnWorkTime(report.ownWorkTime); - setStudyTime(report.studyTime); - setTestingTime(report.testingTime); - } else { - console.error("Failed to fetch weekly report:", response.message); - } - }; - void fetchWeeklyReport(); - }, [projectName, fetchedWeek, token]); + }); const handleNewWeeklyReport = async (): Promise => { const newWeeklyReport: NewWeeklyReport = { @@ -79,7 +78,6 @@ export default function GetWeeklyReport(): JSX.Element { return ( <> -

Edit Time Report

{ @@ -94,10 +92,24 @@ export default function GetWeeklyReport(): JSX.Element { }} >
-
-

Week: {week}

-
- + { + const weekNumber = parseInt(e.target.value.split("-W")[1]); + setWeek(weekNumber); + }} + onKeyDown={(event) => { + event.preventDefault(); + }} + onPaste={(event) => { + event.preventDefault(); + }} + /> @@ -117,14 +129,9 @@ export default function GetWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={developmentTime === 0 ? "" : developmentTime} + value={developmentTime} onChange={(e) => { - if (e.target.value === "") { - setDevelopmentTime(0); - return; - } else { - setDevelopmentTime(parseInt(e.target.value)); - } + setDevelopmentTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -141,14 +148,9 @@ export default function GetWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={meetingTime === 0 ? "" : meetingTime} + value={meetingTime} onChange={(e) => { - if (e.target.value === "") { - setMeetingTime(0); - return; - } else { - setMeetingTime(parseInt(e.target.value)); - } + setMeetingTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -165,14 +167,9 @@ export default function GetWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={adminTime === 0 ? "" : adminTime} + value={adminTime} onChange={(e) => { - if (e.target.value === "") { - setAdminTime(0); - return; - } else { - setAdminTime(parseInt(e.target.value)); - } + setAdminTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -189,14 +186,9 @@ export default function GetWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={ownWorkTime === 0 ? "" : ownWorkTime} + value={ownWorkTime} onChange={(e) => { - if (e.target.value === "") { - setOwnWorkTime(0); - return; - } else { - setOwnWorkTime(parseInt(e.target.value)); - } + setOwnWorkTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -213,14 +205,9 @@ export default function GetWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={studyTime === 0 ? "" : studyTime} + value={studyTime} onChange={(e) => { - if (e.target.value === "") { - setStudyTime(0); - return; - } else { - setStudyTime(parseInt(e.target.value)); - } + setStudyTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -237,14 +224,9 @@ export default function GetWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={testingTime === 0 ? "" : testingTime} + value={testingTime} onChange={(e) => { - if (e.target.value === "") { - setTestingTime(0); - return; - } else { - setTestingTime(parseInt(e.target.value)); - } + setTestingTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; diff --git a/frontend/src/Components/GetProjects.tsx b/frontend/src/Components/GetProjects.tsx deleted file mode 100644 index d6ab1f7..0000000 --- a/frontend/src/Components/GetProjects.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Dispatch, useEffect } from "react"; -import { Project } from "../Types/goTypes"; -import { api } from "../API/API"; - -/** - * Gets all projects that user is a member of - * @param props - A setStateAction for the array you want to put projects in - * @returns {void} Nothing - * @example - * const [projects, setProjects] = useState([]); - * GetAllUsers({ setProjectsProp: setProjects }); - */ -function GetProjects(props: { - setProjectsProp: Dispatch>; -}): void { - const setProjects: Dispatch> = - props.setProjectsProp; - useEffect(() => { - const fetchUsers = async (): Promise => { - try { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getUserProjects(token); - if (response.success) { - setProjects(response.data ?? []); - } else { - console.error("Failed to fetch projects:", response.message); - } - } catch (error) { - console.error("Error fetching projects:", error); - } - }; - - void fetchUsers(); - }, [setProjects]); -} - -export default GetProjects; diff --git a/frontend/src/Components/GetUsersInProject.tsx b/frontend/src/Components/GetUsersInProject.tsx deleted file mode 100644 index acdd965..0000000 --- a/frontend/src/Components/GetUsersInProject.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Dispatch, useEffect } from "react"; -import { UserProjectMember } from "../Types/goTypes"; -import { api } from "../API/API"; - -/** - * Gets all projects that user is a member of - * @param props - A setStateAction for the array you want to put projects in - * @returns {void} Nothing - * @example - * const [projects, setProjects] = useState([]); - * GetAllUsers({ setProjectsProp: setProjects }); - */ -function GetUsersInProject(props: { - projectName: string; - setUsersProp: Dispatch>; -}): void { - const setUsers: Dispatch> = - props.setUsersProp; - useEffect(() => { - const fetchUsers = async (): Promise => { - try { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getAllUsersProject(props.projectName, token); - if (response.success) { - setUsers(response.data ?? []); - } else { - console.error("Failed to fetch projects:", response.message); - } - } catch (error) { - console.error("Error fetching projects:", error); - } - }; - void fetchUsers(); - }, [props.projectName, setUsers]); -} - -export default GetUsersInProject; diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx index f684b0c..292ddf5 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -12,103 +12,66 @@ import Button from "./Button"; */ export default function NewWeeklyReport(): JSX.Element { const [week, setWeek] = useState(0); - const [developmentTime, setDevelopmentTime] = useState(0); - const [meetingTime, setMeetingTime] = useState(0); - const [adminTime, setAdminTime] = useState(0); - const [ownWorkTime, setOwnWorkTime] = useState(0); - const [studyTime, setStudyTime] = useState(0); - const [testingTime, setTestingTime] = useState(0); + const [developmentTime, setDevelopmentTime] = useState(); + const [meetingTime, setMeetingTime] = useState(); + const [adminTime, setAdminTime] = useState(); + const [ownWorkTime, setOwnWorkTime] = useState(); + const [studyTime, setStudyTime] = useState(); + const [testingTime, setTestingTime] = useState(); const { projectName } = useParams(); const token = localStorage.getItem("accessToken") ?? ""; - const handleNewWeeklyReport = async (): Promise => { + const handleNewWeeklyReport = async (): Promise => { const newWeeklyReport: NewWeeklyReport = { projectName: projectName ?? "", week: week, - developmentTime: developmentTime, - meetingTime: meetingTime, - adminTime: adminTime, - ownWorkTime: ownWorkTime, - studyTime: studyTime, - testingTime: testingTime, + developmentTime: developmentTime ?? 0, + meetingTime: meetingTime ?? 0, + adminTime: adminTime ?? 0, + ownWorkTime: ownWorkTime ?? 0, + studyTime: studyTime ?? 0, + testingTime: testingTime ?? 0, }; - const response = await api.submitWeeklyReport(newWeeklyReport, token); - console.log(response); - if (response.success) { - return true; - } else { - return false; - } + await api.submitWeeklyReport(newWeeklyReport, token); }; const navigate = useNavigate(); - // Check if the browser is Chrome or Edge - const isChromeOrEdge = /Chrome|Edg/.test(navigator.userAgent); return ( <>
{ + if (week === 0) { + alert("Please enter a week number"); + e.preventDefault(); + return; + } e.preventDefault(); - void (async (): Promise => { - if (week === 0 || week > 53 || week < 1) { - alert("Please enter a valid week number"); - return; - } - - const success = await handleNewWeeklyReport(); - if (!success) { - alert( - "A Time Report for this week already exists, please go to the edit page to edit it or change week number.", - ); - return; - } - alert("Weekly report submitted successfully"); - navigate(-1); - })(); + void handleNewWeeklyReport(); + navigate(-1); }} >
- {isChromeOrEdge ? ( - { - const weekNumber = parseInt(e.target.value.split("-W")[1]); - setWeek(weekNumber); - }} - onKeyDown={(event) => { - const keyValue = event.key; - if (!/\d/.test(keyValue) && keyValue !== "Backspace") - event.preventDefault(); - }} - onPaste={(event) => { + { + const weekNumber = parseInt(e.target.value.split("-W")[1]); + setWeek(weekNumber); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") event.preventDefault(); - }} - /> - ) : ( - { - const weekNumber = parseInt(e.target.value); - setWeek(weekNumber); - }} - onKeyDown={(event) => { - const keyValue = event.key; - if (!/\d/.test(keyValue) && keyValue !== "Backspace") - event.preventDefault(); - }} - onPaste={(event) => { - event.preventDefault(); - }} - /> - )} + }} + onPaste={(event) => { + event.preventDefault(); + }} + />
@@ -128,14 +91,9 @@ export default function NewWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={developmentTime === 0 ? "" : developmentTime} + value={developmentTime} onChange={(e) => { - if (e.target.value === "") { - setDevelopmentTime(0); - return; - } else { - setDevelopmentTime(parseInt(e.target.value)); - } + setDevelopmentTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -152,14 +110,9 @@ export default function NewWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={meetingTime === 0 ? "" : meetingTime} + value={meetingTime} onChange={(e) => { - if (e.target.value === "") { - setMeetingTime(0); - return; - } else { - setMeetingTime(parseInt(e.target.value)); - } + setMeetingTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -176,14 +129,9 @@ export default function NewWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={adminTime === 0 ? "" : adminTime} + value={adminTime} onChange={(e) => { - if (e.target.value === "") { - setAdminTime(0); - return; - } else { - setAdminTime(parseInt(e.target.value)); - } + setAdminTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -200,14 +148,9 @@ export default function NewWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={ownWorkTime === 0 ? "" : ownWorkTime} + value={ownWorkTime} onChange={(e) => { - if (e.target.value === "") { - setOwnWorkTime(0); - return; - } else { - setOwnWorkTime(parseInt(e.target.value)); - } + setOwnWorkTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -224,14 +167,9 @@ export default function NewWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={studyTime === 0 ? "" : studyTime} + value={studyTime} onChange={(e) => { - if (e.target.value === "") { - setStudyTime(0); - return; - } else { - setStudyTime(parseInt(e.target.value)); - } + setStudyTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; @@ -248,14 +186,9 @@ export default function NewWeeklyReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={testingTime === 0 ? "" : testingTime} + value={testingTime} onChange={(e) => { - if (e.target.value === "") { - setTestingTime(0); - return; - } else { - setTestingTime(parseInt(e.target.value)); - } + setTestingTime(parseInt(e.target.value)); }} onKeyDown={(event) => { const keyValue = event.key; diff --git a/frontend/src/Components/OtherUsersTR.tsx b/frontend/src/Components/OtherUsersTR.tsx deleted file mode 100644 index 2b00e16..0000000 --- a/frontend/src/Components/OtherUsersTR.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import { useState, useEffect } from "react"; -import { WeeklyReport } from "../Types/goTypes"; -import { api } from "../API/API"; -import { useParams } from "react-router-dom"; - -/** - * Renders the component for editing a weekly report. - * @returns JSX.Element - */ - -//This component does not yet work as intended. It is supposed to display the weekly report of a user in a project. -export default function OtherUsersTR(): JSX.Element { - const [week, setWeek] = useState(0); - const [developmentTime, setDevelopmentTime] = useState(0); - const [meetingTime, setMeetingTime] = useState(0); - const [adminTime, setAdminTime] = useState(0); - const [ownWorkTime, setOwnWorkTime] = useState(0); - const [studyTime, setStudyTime] = useState(0); - const [testingTime, setTestingTime] = useState(0); - - const token = localStorage.getItem("accessToken") ?? ""; - const { projectName } = useParams(); - const { username } = useParams(); - const { fetchedWeek } = useParams(); - - useEffect(() => { - const fetchUsersWeeklyReport = async (): Promise => { - const response = await api.getWeeklyReport( - projectName ?? "", - fetchedWeek?.toString() ?? "0", - token, - ); - - if (response.success) { - const report: WeeklyReport = response.data ?? { - reportId: 0, - userId: 0, - projectId: 0, - week: 0, - developmentTime: 0, - meetingTime: 0, - adminTime: 0, - ownWorkTime: 0, - studyTime: 0, - testingTime: 0, - }; - setWeek(report.week); - setDevelopmentTime(report.developmentTime); - setMeetingTime(report.meetingTime); - setAdminTime(report.adminTime); - setOwnWorkTime(report.ownWorkTime); - setStudyTime(report.studyTime); - setTestingTime(report.testingTime); - } else { - console.error("Failed to fetch weekly report:", response.message); - } - }; - - void fetchUsersWeeklyReport(); - }); - - return ( - <> -

{username}'s Report

-
-
-
-

Week: {week}

-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Activity - Total Time (min) -
Development - -
Meeting - -
Administration - -
Own Work - -
Studies - -
Testing - -
-
-
- - ); -} diff --git a/frontend/src/Components/ProjectInfoModal.tsx b/frontend/src/Components/ProjectInfoModal.tsx deleted file mode 100644 index 3075b19..0000000 --- a/frontend/src/Components/ProjectInfoModal.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useState } from "react"; -import Button from "./Button"; -import { UserProjectMember } from "../Types/goTypes"; -import GetUsersInProject from "./GetUsersInProject"; -import { Link } from "react-router-dom"; - -function ProjectInfoModal(props: { - isVisible: boolean; - projectname: string; - onClose: () => void; - onClick: (username: string) => void; -}): JSX.Element { - const [users, setUsers] = useState([]); - GetUsersInProject({ projectName: props.projectname, setUsersProp: setUsers }); - if (!props.isVisible) return <>; - - return ( -
-
-
-

- {localStorage.getItem("projectName") ?? ""} -

-

Project members:

-
-
    -
    - {users.map((user) => ( -
  • { - props.onClick(user.Username); - }} - > - - Name: {user.Username} -
    - Role: {user.UserRole} -
    -
  • - ))} -
-
-
-
-
-
-
- ); -} - -export default ProjectInfoModal; diff --git a/frontend/src/Components/ProjectListAdmin.tsx b/frontend/src/Components/ProjectListAdmin.tsx deleted file mode 100644 index f25ee47..0000000 --- a/frontend/src/Components/ProjectListAdmin.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { useState } from "react"; -import { NewProject } from "../Types/goTypes"; -import ProjectInfoModal from "./ProjectInfoModal"; -import UserInfoModal from "./UserInfoModal"; - -/** - * A list of projects for admin manage projects page, that sets an onClick - * function for eact project
  • element, which displays a modul with - * user info. - * @param props - An array of projects to display - * @returns {JSX.Element} The project list - * @example - * const projects: NewProject[] = [{ name: "Project", description: "New" }]; - * return - */ - -export function ProjectListAdmin(props: { - projects: NewProject[]; -}): JSX.Element { - const [projectModalVisible, setProjectModalVisible] = useState(false); - const [projectname, setProjectname] = useState(""); - const [userModalVisible, setUserModalVisible] = useState(false); - const [username, setUsername] = useState(""); - - const handleClickUser = (username: string): void => { - setUsername(username); - setUserModalVisible(true); - }; - - const handleClickProject = (projectname: string): void => { - setProjectname(projectname); - localStorage.setItem("projectName", projectname); - setProjectModalVisible(true); - }; - - const handleCloseProject = (): void => { - setProjectname(""); - setProjectModalVisible(false); - }; - - const handleCloseUser = (): void => { - setProjectname(""); - setUserModalVisible(false); - }; - - return ( - <> - - { - return; - }} - isVisible={userModalVisible} - username={username} - /> -
    -
      - {props.projects.map((project) => ( -
    • { - handleClickProject(project.name); - }} - > - {project.name} -
    • - ))} -
    -
    - - ); -} diff --git a/frontend/src/Components/ProjectMembers.tsx b/frontend/src/Components/ProjectMembers.tsx index 60ffcd9..73e29e5 100644 --- a/frontend/src/Components/ProjectMembers.tsx +++ b/frontend/src/Components/ProjectMembers.tsx @@ -1,55 +1,91 @@ import { useEffect, useState } from "react"; import { Link, useParams } from "react-router-dom"; -import { api } from "../API/API"; -import { UserProjectMember } from "../Types/goTypes"; function ProjectMembers(): JSX.Element { const { projectName } = useParams(); - const [projectMembers, setProjectMembers] = useState([]); + const [projectMembers, setProjectMembers] = useState([]); - useEffect(() => { - const getProjectMembers = async (): Promise => { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getAllUsersProject(projectName ?? "", token); - console.log(response); - if (response.success) { - setProjectMembers(response.data ?? []); - } else { - console.error(response.message); - } - }; - - void getProjectMembers(); - }, [projectName]); + // const getProjectMembers = async (): Promise => { + // const token = localStorage.getItem("accessToken") ?? ""; + // const response = await api.getProjectMembers(projectName ?? "", token); + // console.log(response); + // if (response.success) { + // setProjectMembers(response.data ?? []); + // } else { + // console.error(response.message); + // } + // }; interface ProjectMember { - Username: string; - UserRole: string; + username: string; + role: string; } + const mockProjectMembers = [ + { + username: "username1", + role: "Project Manager", + }, + { + username: "username2", + role: "System Manager", + }, + { + username: "username3", + role: "Developer", + }, + { + username: "username4", + role: "Tester", + }, + { + username: "username5", + role: "Tester", + }, + { + username: "username6", + role: "Tester", + }, + ]; + + const getProjectMembers = async (): Promise => { + // Use the mock data + setProjectMembers(mockProjectMembers); + + await Promise.resolve(); + }; + + useEffect(() => { + void getProjectMembers(); + }); + return ( <> -

    - All Members In: {projectName}{" "} -

    - {projectMembers.map((projectMember: ProjectMember, index: number) => ( + {projectMembers.map((projectMember, index) => (

    -

    {projectMember.Username}

    +

    {projectMember.username}

    Role: -

    {projectMember.UserRole}

    +

    {projectMember.role}

    View Reports

    + +

    + Change Role +

    +
    diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx index 6192637..df07c6e 100644 --- a/frontend/src/Components/Register.tsx +++ b/frontend/src/Components/Register.tsx @@ -4,6 +4,7 @@ import { api } from "../API/API"; import Logo from "../assets/Logo.svg"; import Button from "./Button"; import InputField from "./InputField"; +import { useNavigate } from "react-router-dom"; /** * Renders a registration form for the admin to add new users in. @@ -14,6 +15,8 @@ export default function Register(): JSX.Element { const [password, setPassword] = useState(); const [errMessage, setErrMessage] = useState(); + const nav = useNavigate(); + const handleRegister = async (): Promise => { const newUser: NewUser = { username: username ?? "", @@ -21,9 +24,8 @@ export default function Register(): JSX.Element { }; const response = await api.registerUser(newUser); if (response.success) { - alert("User added!"); + nav("/"); // Instantly navigate to the login page } else { - alert("User not added"); setErrMessage(response.message ?? "Unknown error"); console.error(errMessage); } diff --git a/frontend/src/Components/TimePerActivity.tsx b/frontend/src/Components/TimePerActivity.tsx deleted file mode 100644 index 3dc1a6b..0000000 --- a/frontend/src/Components/TimePerActivity.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import { useState, useEffect } from "react"; -import { useParams } from "react-router-dom"; - -/** - * Renders the component for showing total time per role in a project. - * @returns JSX.Element - */ -export default function TimePerRole(): JSX.Element { - const [developmentTime, setDevelopmentTime] = useState(); - const [meetingTime, setMeetingTime] = useState(); - const [adminTime, setAdminTime] = useState(); - const [ownWorkTime, setOwnWorkTime] = useState(); - const [studyTime, setStudyTime] = useState(); - const [testingTime, setTestingTime] = useState(); - - // const token = localStorage.getItem("accessToken") ?? ""; - // const username = localStorage.getItem("username") ?? ""; - const { projectName } = useParams(); - - // const fetchTimePerRole = async (): Promise => { - // const response = await api.getTimePerRole( - // username, - // projectName ?? "", - // token, - // ); - // { - // if (response.success) { - // const report: TimePerRole = response.data ?? { - // PManagerTime: 0, - // SManagerTime: 0, - // DeveloperTime: 0, - // TesterTime: 0, - // }; - // } else { - // console.error("Failed to fetch weekly report:", response.message); - // } - // } - - interface TimePerActivity { - developmentTime: number; - meetingTime: number; - adminTime: number; - ownWorkTime: number; - studyTime: number; - testingTime: number; - } - - const fetchTimePerActivity = async (): Promise => { - // Use mock data - const report: TimePerActivity = { - developmentTime: 100, - meetingTime: 200, - adminTime: 300, - ownWorkTime: 50, - studyTime: 75, - testingTime: 110, - }; - - // Set the state with the mock data - setDevelopmentTime(report.developmentTime); - setMeetingTime(report.meetingTime); - setAdminTime(report.adminTime); - setOwnWorkTime(report.ownWorkTime); - setStudyTime(report.studyTime); - setTestingTime(report.testingTime); - - await Promise.resolve(); - }; - - useEffect(() => { - void fetchTimePerActivity(); - }); - - return ( - <> -

    - Total Time Per Activity In: {projectName}{" "} -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Activity - Total Time (min) -
    Development - { - event.preventDefault(); - }} - /> -
    Meeting - { - event.preventDefault(); - }} - /> -
    Administration - { - event.preventDefault(); - }} - /> -
    Own Work - { - event.preventDefault(); - }} - /> -
    Studies - { - event.preventDefault(); - }} - /> -
    Testing - { - event.preventDefault(); - }} - /> -
    -
    -
    - - ); -} diff --git a/frontend/src/Components/TimePerRole.tsx b/frontend/src/Components/TimePerRole.tsx deleted file mode 100644 index f62d83a..0000000 --- a/frontend/src/Components/TimePerRole.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { useState, useEffect } from "react"; -import { useParams } from "react-router-dom"; - -/** - * Renders the component for showing total time per role in a project. - * @returns JSX.Element - */ -export default function TimePerRole(): JSX.Element { - const [PManagerTime, setPManagerTime] = useState(0); - const [SManagerTime, setSManagerTime] = useState(0); - const [DeveloperTime, setDeveloperTime] = useState(0); - const [TesterTime, setTesterTime] = useState(0); - - // const token = localStorage.getItem("accessToken") ?? ""; - // const username = localStorage.getItem("username") ?? ""; - const { projectName } = useParams(); - - // const fetchTimePerRole = async (): Promise => { - // const response = await api.getTimePerRole( - // username, - // projectName ?? "", - // token, - // ); - // { - // if (response.success) { - // const report: TimePerRole = response.data ?? { - // PManagerTime: 0, - // SManagerTime: 0, - // DeveloperTime: 0, - // TesterTime: 0, - // }; - // } else { - // console.error("Failed to fetch weekly report:", response.message); - // } - // } - - interface TimePerRole { - PManager: number; - SManager: number; - Developer: number; - Tester: number; - } - - const fetchTimePerRole = async (): Promise => { - // Use mock data - const report: TimePerRole = { - PManager: 120, - SManager: 80, - Developer: 200, - Tester: 150, - }; - - // Set the state with the mock data - setPManagerTime(report.PManager); - setSManagerTime(report.SManager); - setDeveloperTime(report.Developer); - setTesterTime(report.Tester); - - await Promise.resolve(); - }; - - useEffect(() => { - void fetchTimePerRole(); - }); - - return ( - <> -

    - Total Time Per Role In: {projectName}{" "} -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Role - Total Time (min) -
    Project Manager - { - event.preventDefault(); - }} - /> -
    System Manager - { - event.preventDefault(); - }} - /> -
    Administration - { - event.preventDefault(); - }} - /> -
    Own Work - { - event.preventDefault(); - }} - /> -
    -
    -
    - - ); -} diff --git a/frontend/src/Components/UserInfoModal.tsx b/frontend/src/Components/UserInfoModal.tsx index 9695899..a22ef01 100644 --- a/frontend/src/Components/UserInfoModal.tsx +++ b/frontend/src/Components/UserInfoModal.tsx @@ -5,38 +5,23 @@ import UserProjectListAdmin from "./UserProjectListAdmin"; function UserInfoModal(props: { isVisible: boolean; - manageMember: boolean; username: string; onClose: () => void; - onDelete: (username: string) => void; }): JSX.Element { if (!props.isVisible) return <>; - const ManageUserOrMember = (check: boolean): JSX.Element => { - if (check) { - return ( - -

    - (Change Role) -

    - - ); - } - return ( - -

    - (Change Username) -

    - - ); - }; + return (
    -
    +

    {props.username}

    - {ManageUserOrMember(props.manageMember)} + +

    + (Change Username) +

    +

    Member of these projects: @@ -49,13 +34,7 @@ function UserInfoModal(props: {

    - -
    - - ); -} diff --git a/frontend/src/Pages/AdminPages/AdminManageProjects.tsx b/frontend/src/Pages/AdminPages/AdminManageProjects.tsx index 7ea45df..177f55b 100644 --- a/frontend/src/Pages/AdminPages/AdminManageProjects.tsx +++ b/frontend/src/Pages/AdminPages/AdminManageProjects.tsx @@ -2,22 +2,9 @@ import { Link } from "react-router-dom"; import BackButton from "../../Components/BackButton"; import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; -import { ProjectListAdmin } from "../../Components/ProjectListAdmin"; -import { Project } from "../../Types/goTypes"; -import GetProjects from "../../Components/GetProjects"; -import { useState } from "react"; function AdminManageProjects(): JSX.Element { - const [projects, setProjects] = useState([]); - GetProjects({ setProjectsProp: setProjects }); - const content = ( - <> -

    Manage Projects

    -
    - -
    - - ); + const content = <>; const buttons = ( <> diff --git a/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx b/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx index 893bdad..96167cb 100644 --- a/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx +++ b/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx @@ -1,10 +1,27 @@ -import AddUserToProject from "../../Components/AddUserToProject"; import BasicWindow from "../../Components/BasicWindow"; +import Button from "../../Components/Button"; function AdminProjectAddMember(): JSX.Element { - const content = ; + const content = <>; - const buttons = <>; + const buttons = ( + <> +