Compare commits
No commits in common. "b036ef906c4251ce0c0498ac657f2a9b759b7362" and "89ba0415f7f89803f54b5fb6c6f5bcbd99bd61da" have entirely different histories.
b036ef906c
...
89ba0415f7
19 changed files with 81 additions and 321 deletions
|
@ -7,8 +7,6 @@ VALUES ("user", "123");
|
||||||
INSERT OR IGNORE INTO users(username, password)
|
INSERT OR IGNORE INTO users(username, password)
|
||||||
VALUES ("user2", "123");
|
VALUES ("user2", "123");
|
||||||
|
|
||||||
INSERT OR IGNORE INTO site_admin VALUES (1);
|
|
||||||
|
|
||||||
INSERT OR IGNORE INTO projects(name,description,owner_user_id)
|
INSERT OR IGNORE INTO projects(name,description,owner_user_id)
|
||||||
VALUES ("projecttest","test project", 1);
|
VALUES ("projecttest","test project", 1);
|
||||||
|
|
||||||
|
|
|
@ -44,11 +44,10 @@ func (gs *GState) DeleteProject(c *fiber.Ctx) error {
|
||||||
|
|
||||||
// GetUserProjects returns all projects that the user is a member of
|
// GetUserProjects returns all projects that the user is a member of
|
||||||
func (gs *GState) GetUserProjects(c *fiber.Ctx) error {
|
func (gs *GState) GetUserProjects(c *fiber.Ctx) error {
|
||||||
username := c.Params("username")
|
// First we get the username from the token
|
||||||
if username == "" {
|
user := c.Locals("user").(*jwt.Token)
|
||||||
log.Info("No username provided")
|
claims := user.Claims.(jwt.MapClaims)
|
||||||
return c.Status(400).SendString("No username provided")
|
username := claims["name"].(string)
|
||||||
}
|
|
||||||
|
|
||||||
// Then dip into the database to get the projects
|
// Then dip into the database to get the projects
|
||||||
projects, err := gs.Db.GetProjectsForUser(username)
|
projects, err := gs.Db.GetProjectsForUser(username)
|
||||||
|
|
|
@ -59,9 +59,9 @@ func (gs *GState) UserDelete(c *fiber.Ctx) error {
|
||||||
// Read username from Locals
|
// Read username from Locals
|
||||||
auth_username := c.Locals("user").(*jwt.Token).Claims.(jwt.MapClaims)["name"].(string)
|
auth_username := c.Locals("user").(*jwt.Token).Claims.(jwt.MapClaims)["name"].(string)
|
||||||
|
|
||||||
if username == auth_username {
|
if username != auth_username {
|
||||||
log.Info("User tried to delete itself")
|
log.Info("User tried to delete another user")
|
||||||
return c.Status(403).SendString("You can't delete yourself")
|
return c.Status(403).SendString("You can only delete yourself")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gs.Db.RemoveUser(username); err != nil {
|
if err := gs.Db.RemoveUser(username); err != nil {
|
||||||
|
|
|
@ -84,7 +84,7 @@ func main() {
|
||||||
|
|
||||||
// Protected routes (require a valid JWT bearer token authentication header)
|
// Protected routes (require a valid JWT bearer token authentication header)
|
||||||
server.Post("/api/submitWeeklyReport", gs.SubmitWeeklyReport)
|
server.Post("/api/submitWeeklyReport", gs.SubmitWeeklyReport)
|
||||||
server.Get("/api/getUserProjects/:username", gs.GetUserProjects)
|
server.Get("/api/getUserProjects", gs.GetUserProjects)
|
||||||
server.Post("/api/loginrenew", gs.LoginRenew)
|
server.Post("/api/loginrenew", gs.LoginRenew)
|
||||||
server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches
|
server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches
|
||||||
server.Delete("api/project/:projectID", gs.DeleteProject) // WIP
|
server.Delete("api/project/:projectID", gs.DeleteProject) // WIP
|
||||||
|
|
|
@ -6,8 +6,6 @@ import {
|
||||||
NewProject,
|
NewProject,
|
||||||
UserProjectMember,
|
UserProjectMember,
|
||||||
WeeklyReport,
|
WeeklyReport,
|
||||||
StrNameChange,
|
|
||||||
NewProjMember,
|
|
||||||
} from "../Types/goTypes";
|
} from "../Types/goTypes";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -114,14 +112,10 @@ interface API {
|
||||||
): Promise<APIResponse<WeeklyReport[]>>;
|
): Promise<APIResponse<WeeklyReport[]>>;
|
||||||
|
|
||||||
/** Gets all the projects of a user
|
/** Gets all the projects of a user
|
||||||
* @param {string} username - The authentication token.
|
|
||||||
* @param {string} token - The authentication token.
|
* @param {string} token - The authentication token.
|
||||||
* @returns {Promise<APIResponse<Project[]>>} A promise containing the API response with the user's projects.
|
* @returns {Promise<APIResponse<Project[]>>} A promise containing the API response with the user's projects.
|
||||||
*/
|
*/
|
||||||
getUserProjects(
|
getUserProjects(token: string): Promise<APIResponse<Project[]>>;
|
||||||
username: string,
|
|
||||||
token: string,
|
|
||||||
): Promise<APIResponse<Project[]>>;
|
|
||||||
|
|
||||||
/** Gets a project by its id.
|
/** Gets a project by its id.
|
||||||
* @param {number} id The id of the project to retrieve.
|
* @param {number} id The id of the project to retrieve.
|
||||||
|
@ -139,20 +133,6 @@ interface API {
|
||||||
projectName: string,
|
projectName: string,
|
||||||
token: string,
|
token: string,
|
||||||
): Promise<APIResponse<UserProjectMember[]>>;
|
): Promise<APIResponse<UserProjectMember[]>>;
|
||||||
/**
|
|
||||||
* 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<APIResponse<void>>} A promise resolving to an API response.
|
|
||||||
*/
|
|
||||||
changeUserName(
|
|
||||||
data: StrNameChange,
|
|
||||||
token: string,
|
|
||||||
): Promise<APIResponse<void>>;
|
|
||||||
addUserToProject(
|
|
||||||
user: NewProjMember,
|
|
||||||
token: string,
|
|
||||||
): Promise<APIResponse<NewProjMember>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An instance of the API */
|
/** An instance of the API */
|
||||||
|
@ -190,17 +170,19 @@ export const api: API = {
|
||||||
): Promise<APIResponse<User>> {
|
): Promise<APIResponse<User>> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/userdelete/${username}`, {
|
const response = await fetch(`/api/userdelete/${username}`, {
|
||||||
method: "DELETE",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: "Bearer " + token,
|
Authorization: "Bearer " + token,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(username),
|
body: JSON.stringify(username),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return { success: false, message: "Could not remove user" };
|
return { success: false, message: "Failed to remove user" };
|
||||||
} else {
|
} else {
|
||||||
return { success: true };
|
const data = (await response.json()) as User;
|
||||||
|
return { success: true, data };
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { success: false, message: "Failed to remove user" };
|
return { success: false, message: "Failed to remove user" };
|
||||||
|
@ -261,30 +243,6 @@ export const api: API = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async addUserToProject(
|
|
||||||
user: NewProjMember,
|
|
||||||
token: string,
|
|
||||||
): Promise<APIResponse<NewProjMember>> {
|
|
||||||
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<APIResponse<string>> {
|
async renewToken(token: string): Promise<APIResponse<string>> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/loginrenew", {
|
const response = await fetch("/api/loginrenew", {
|
||||||
|
@ -306,12 +264,9 @@ export const api: API = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async getUserProjects(
|
async getUserProjects(token: string): Promise<APIResponse<Project[]>> {
|
||||||
username: string,
|
|
||||||
token: string,
|
|
||||||
): Promise<APIResponse<Project[]>> {
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/getUserProjects/${username}`, {
|
const response = await fetch("/api/getUserProjects", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
@ -529,28 +484,4 @@ export const api: API = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async changeUserName(
|
|
||||||
data: StrNameChange,
|
|
||||||
token: string,
|
|
||||||
): Promise<APIResponse<void>> {
|
|
||||||
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" };
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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<NewProjMember>) => {
|
|
||||||
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;
|
|
|
@ -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<string[]>([]);
|
|
||||||
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 (
|
|
||||||
<div className="border-4 border-black bg-white flex flex-col items-center justify-center rounded-3xl content-center pl-20 pr-20 h-[75vh] w-[50vh]">
|
|
||||||
<p className="pb-4 mb-2 text-center font-bold text-[18px]">
|
|
||||||
User chosen: [{name}]
|
|
||||||
</p>
|
|
||||||
<p className="pb-4 mb-2 text-center font-bold text-[18px]">
|
|
||||||
Role chosen: [{role}]
|
|
||||||
</p>
|
|
||||||
<p className="pb-4 mb-2 text-center font-bold text-[18px]">
|
|
||||||
Project chosen: [{localStorage.getItem("projectName") ?? ""}]
|
|
||||||
</p>
|
|
||||||
<p className="p-1">Choose role:</p>
|
|
||||||
<div className="border-2 border-black p-2 rounded-xl text-center h-[10h] w-[16vh]">
|
|
||||||
<ul className="text-center items-center font-medium space-y-2">
|
|
||||||
<li
|
|
||||||
className="h-[10h] w-[14vh] items-start p-1 border-2 border-black rounded-full bg-orange-200 hover:bg-orange-600 hover:text-slate-100 hover:cursor-pointer"
|
|
||||||
onClick={() => {
|
|
||||||
setRole("member");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{"Member"}
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
className="h-[10h] w-[14vh] items-start p-1 border-2 border-black rounded-full bg-orange-200 hover:bg-orange-600 hover:text-slate-100 hover:cursor-pointer"
|
|
||||||
onClick={() => {
|
|
||||||
setRole("project_manager");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{"Project manager"}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<p className="p-1">Choose user:</p>
|
|
||||||
<div className="border-2 border-black p-2 rounded-xl text-center overflow-scroll h-[26vh] w-[26vh]">
|
|
||||||
<ul className="text-center font-medium space-y-2">
|
|
||||||
<div></div>
|
|
||||||
{users.map((user) => (
|
|
||||||
<li
|
|
||||||
className="items-start p-1 border-2 border-black rounded-full bg-orange-200 hover:bg-orange-600 hover:text-slate-100 hover:cursor-pointer"
|
|
||||||
key={user}
|
|
||||||
value={user}
|
|
||||||
onClick={() => {
|
|
||||||
setName(user);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>{user}</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="flex space-x-5 items-center justify-between">
|
|
||||||
<Button
|
|
||||||
text="Add"
|
|
||||||
onClick={(): void => {
|
|
||||||
handleClick();
|
|
||||||
}}
|
|
||||||
type="submit"
|
|
||||||
/>
|
|
||||||
<BackButton />
|
|
||||||
</div>
|
|
||||||
<p className="text-center text-gray-500 text-xs"></p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AddUserToProject;
|
|
|
@ -1,48 +1,23 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import InputField from "./InputField";
|
import InputField from "./InputField";
|
||||||
import { api } from "../API/API";
|
|
||||||
|
|
||||||
function ChangeUsername(): JSX.Element {
|
function ChangeUsername(): JSX.Element {
|
||||||
const [newUsername, setNewUsername] = useState("");
|
const [newUsername, setNewUsername] = useState("");
|
||||||
const [errorMessage, setErrorMessage] = useState("");
|
|
||||||
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
setNewUsername(e.target.value);
|
setNewUsername(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async (): Promise<void> => {
|
// const handleSubmit = async (): Promise<void> => {
|
||||||
try {
|
// try {
|
||||||
// Call the API function to change the username
|
// // Call the API function to update the username
|
||||||
const token = localStorage.getItem("accessToken");
|
// await api.updateUsername(newUsername);
|
||||||
if (!token) {
|
// // Optionally, add a success message or redirect the user
|
||||||
throw new Error("Access token not found");
|
// } catch (error) {
|
||||||
}
|
// console.error("Error updating username:", error);
|
||||||
|
// // Optionally, handle the error
|
||||||
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);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -52,8 +27,6 @@ function ChangeUsername(): JSX.Element {
|
||||||
value={newUsername}
|
value={newUsername}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
{errorMessage && <div>{errorMessage}</div>}
|
|
||||||
<button onClick={handleButtonClick}>Update Username</button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { api, APIResponse } from "../API/API";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function DeleteUser(props: { usernameToDelete: string }): boolean {
|
function DeleteUser(props: { usernameToDelete: string }): boolean {
|
||||||
|
//console.log(props.usernameToDelete); FOR DEBUG
|
||||||
let removed = false;
|
let removed = false;
|
||||||
api
|
api
|
||||||
.removeUser(
|
.removeUser(
|
||||||
|
@ -19,16 +20,12 @@ function DeleteUser(props: { usernameToDelete: string }): boolean {
|
||||||
)
|
)
|
||||||
.then((response: APIResponse<User>) => {
|
.then((response: APIResponse<User>) => {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
alert("User has been deleted!");
|
|
||||||
location.reload();
|
|
||||||
removed = true;
|
removed = true;
|
||||||
} else {
|
} else {
|
||||||
alert("User has not been deleted");
|
|
||||||
console.error(response.message);
|
console.error(response.message);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
alert("User has not been deleted");
|
|
||||||
console.error("An error occurred during creation:", error);
|
console.error("An error occurred during creation:", error);
|
||||||
});
|
});
|
||||||
return removed;
|
return removed;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Project } from "../Types/goTypes";
|
import { Project } from "../Types/goTypes";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import GetProjects from "./GetProjects";
|
import { api } from "../API/API";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a component that displays the projects a user is a part of and links to the projects start-page.
|
* Renders a component that displays the projects a user is a part of and links to the projects start-page.
|
||||||
|
@ -10,10 +10,21 @@ import GetProjects from "./GetProjects";
|
||||||
function DisplayUserProject(): JSX.Element {
|
function DisplayUserProject(): JSX.Element {
|
||||||
const [projects, setProjects] = useState<Project[]>([]);
|
const [projects, setProjects] = useState<Project[]>([]);
|
||||||
|
|
||||||
GetProjects({
|
const getProjects = async (): Promise<void> => {
|
||||||
setProjectsProp: setProjects,
|
const token = localStorage.getItem("accessToken") ?? "";
|
||||||
username: localStorage.getItem("username") ?? "",
|
const response = await api.getUserProjects(token);
|
||||||
});
|
console.log(response);
|
||||||
|
if (response.success) {
|
||||||
|
setProjects(response.data ?? []);
|
||||||
|
} else {
|
||||||
|
console.error(response.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call getProjects when the component mounts
|
||||||
|
useEffect(() => {
|
||||||
|
void getProjects();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -12,7 +12,6 @@ import { api } from "../API/API";
|
||||||
*/
|
*/
|
||||||
function GetProjects(props: {
|
function GetProjects(props: {
|
||||||
setProjectsProp: Dispatch<React.SetStateAction<Project[]>>;
|
setProjectsProp: Dispatch<React.SetStateAction<Project[]>>;
|
||||||
username: string;
|
|
||||||
}): void {
|
}): void {
|
||||||
const setProjects: Dispatch<React.SetStateAction<Project[]>> =
|
const setProjects: Dispatch<React.SetStateAction<Project[]>> =
|
||||||
props.setProjectsProp;
|
props.setProjectsProp;
|
||||||
|
@ -20,7 +19,7 @@ function GetProjects(props: {
|
||||||
const fetchUsers = async (): Promise<void> => {
|
const fetchUsers = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const token = localStorage.getItem("accessToken") ?? "";
|
const token = localStorage.getItem("accessToken") ?? "";
|
||||||
const response = await api.getUserProjects(props.username, token);
|
const response = await api.getUserProjects(token);
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setProjects(response.data ?? []);
|
setProjects(response.data ?? []);
|
||||||
} else {
|
} else {
|
||||||
|
@ -32,7 +31,7 @@ function GetProjects(props: {
|
||||||
};
|
};
|
||||||
|
|
||||||
void fetchUsers();
|
void fetchUsers();
|
||||||
}, [props.username, setProjects]);
|
}, [setProjects]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GetProjects;
|
export default GetProjects;
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { useState } from "react";
|
||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
import { UserProjectMember } from "../Types/goTypes";
|
import { UserProjectMember } from "../Types/goTypes";
|
||||||
import GetUsersInProject from "./GetUsersInProject";
|
import GetUsersInProject from "./GetUsersInProject";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
|
|
||||||
function ProjectInfoModal(props: {
|
function ProjectInfoModal(props: {
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
|
@ -19,12 +18,9 @@ function ProjectInfoModal(props: {
|
||||||
className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm
|
className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm
|
||||||
flex justify-center items-center"
|
flex justify-center items-center"
|
||||||
>
|
>
|
||||||
<div className="border-4 border-black bg-white p-2 rounded-2xl text-center h-[47vh] w-[40] flex flex-col">
|
<div className="border-4 border-black bg-white p-2 rounded-2xl text-center h-[41vh] w-[40vw] flex flex-col">
|
||||||
<div className="pl-20 pr-20">
|
<div className="pl-20 pr-20">
|
||||||
<h1 className="font-bold text-[32px] mb-[20px]">
|
<h1 className="font-bold text-[32px] mb-[20px]">Project members:</h1>
|
||||||
{localStorage.getItem("projectName") ?? ""}
|
|
||||||
</h1>
|
|
||||||
<h2 className="font-bold text-[24px] mb-[20px]">Project members:</h2>
|
|
||||||
<div className="border-2 border-black p-2 rounded-lg text-center overflow-scroll h-[26vh]">
|
<div className="border-2 border-black p-2 rounded-lg text-center overflow-scroll h-[26vh]">
|
||||||
<ul className="text-left font-medium space-y-2">
|
<ul className="text-left font-medium space-y-2">
|
||||||
<div></div>
|
<div></div>
|
||||||
|
@ -54,15 +50,6 @@ function ProjectInfoModal(props: {
|
||||||
}}
|
}}
|
||||||
type="button"
|
type="button"
|
||||||
/>
|
/>
|
||||||
<Link to={"/adminProjectAddMember"}>
|
|
||||||
<Button
|
|
||||||
text={"Add Member"}
|
|
||||||
onClick={function (): void {
|
|
||||||
return;
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
<Button
|
<Button
|
||||||
text={"Close"}
|
text={"Close"}
|
||||||
onClick={function (): void {
|
onClick={function (): void {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { useState } from "react";
|
||||||
import { NewProject } from "../Types/goTypes";
|
import { NewProject } from "../Types/goTypes";
|
||||||
import ProjectInfoModal from "./ProjectInfoModal";
|
import ProjectInfoModal from "./ProjectInfoModal";
|
||||||
import UserInfoModal from "./UserInfoModal";
|
import UserInfoModal from "./UserInfoModal";
|
||||||
|
import DeleteUser from "./DeleteUser";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of projects for admin manage projects page, that sets an onClick
|
* A list of projects for admin manage projects page, that sets an onClick
|
||||||
|
@ -27,9 +28,8 @@ export function ProjectListAdmin(props: {
|
||||||
setUserModalVisible(true);
|
setUserModalVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClickProject = (projectname: string): void => {
|
const handleClickProject = (username: string): void => {
|
||||||
setProjectname(projectname);
|
setProjectname(username);
|
||||||
localStorage.setItem("projectName", projectname);
|
|
||||||
setProjectModalVisible(true);
|
setProjectModalVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,9 +55,7 @@ export function ProjectListAdmin(props: {
|
||||||
manageMember={true}
|
manageMember={true}
|
||||||
onClose={handleCloseUser}
|
onClose={handleCloseUser}
|
||||||
//TODO: CHANGE TO REMOVE USER FROM PROJECT
|
//TODO: CHANGE TO REMOVE USER FROM PROJECT
|
||||||
onDelete={() => {
|
onDelete={() => DeleteUser}
|
||||||
return;
|
|
||||||
}}
|
|
||||||
isVisible={userModalVisible}
|
isVisible={userModalVisible}
|
||||||
username={username}
|
username={username}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { api } from "../API/API";
|
||||||
import Logo from "../assets/Logo.svg";
|
import Logo from "../assets/Logo.svg";
|
||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
import InputField from "./InputField";
|
import InputField from "./InputField";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a registration form for the admin to add new users in.
|
* 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<string>();
|
const [password, setPassword] = useState<string>();
|
||||||
const [errMessage, setErrMessage] = useState<string>();
|
const [errMessage, setErrMessage] = useState<string>();
|
||||||
|
|
||||||
|
const nav = useNavigate();
|
||||||
|
|
||||||
const handleRegister = async (): Promise<void> => {
|
const handleRegister = async (): Promise<void> => {
|
||||||
const newUser: NewUser = {
|
const newUser: NewUser = {
|
||||||
username: username ?? "",
|
username: username ?? "",
|
||||||
|
@ -21,9 +24,8 @@ export default function Register(): JSX.Element {
|
||||||
};
|
};
|
||||||
const response = await api.registerUser(newUser);
|
const response = await api.registerUser(newUser);
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
alert("User added!");
|
nav("/"); // Instantly navigate to the login page
|
||||||
} else {
|
} else {
|
||||||
alert("User not added");
|
|
||||||
setErrMessage(response.message ?? "Unknown error");
|
setErrMessage(response.message ?? "Unknown error");
|
||||||
console.error(errMessage);
|
console.error(errMessage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,20 +42,14 @@ function UserInfoModal(props: {
|
||||||
Member of these projects:
|
Member of these projects:
|
||||||
</h2>
|
</h2>
|
||||||
<div className="pr-6 pl-6">
|
<div className="pr-6 pl-6">
|
||||||
<UserProjectListAdmin username={props.username} />
|
<UserProjectListAdmin />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="items-center space-x-6 pr-6 pl-6">
|
<div className="items-center space-x-6 pr-6 pl-6">
|
||||||
<Button
|
<Button
|
||||||
text={"Delete"}
|
text={"Delete"}
|
||||||
onClick={function (): void {
|
onClick={function (): void {
|
||||||
if (
|
DeleteUser({ usernameToDelete: props.username });
|
||||||
window.confirm("Are you sure you want to delete this user?")
|
|
||||||
) {
|
|
||||||
DeleteUser({
|
|
||||||
usernameToDelete: props.username,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
type="button"
|
type="button"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -2,16 +2,16 @@ import { useEffect, useState } from "react";
|
||||||
import { api } from "../API/API";
|
import { api } from "../API/API";
|
||||||
import { Project } from "../Types/goTypes";
|
import { Project } from "../Types/goTypes";
|
||||||
|
|
||||||
function UserProjectListAdmin(props: { username: string }): JSX.Element {
|
function UserProjectListAdmin(): JSX.Element {
|
||||||
const [projects, setProjects] = useState<Project[]>([]);
|
const [projects, setProjects] = useState<Project[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchProjects = async (): Promise<void> => {
|
const fetchProjects = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const token = localStorage.getItem("accessToken") ?? "";
|
const token = localStorage.getItem("accessToken") ?? "";
|
||||||
const username = props.username;
|
// const username = props.username;
|
||||||
|
|
||||||
const response = await api.getUserProjects(username, token);
|
const response = await api.getUserProjects(token);
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setProjects(response.data ?? []);
|
setProjects(response.data ?? []);
|
||||||
} else {
|
} else {
|
||||||
|
@ -23,7 +23,7 @@ function UserProjectListAdmin(props: { username: string }): JSX.Element {
|
||||||
};
|
};
|
||||||
|
|
||||||
void fetchProjects();
|
void fetchProjects();
|
||||||
}, [props.username]);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-2 border-black bg-white p-2 rounded-lg text-center">
|
<div className="border-2 border-black bg-white p-2 rounded-lg text-center">
|
||||||
|
|
|
@ -9,10 +9,7 @@ import { useState } from "react";
|
||||||
|
|
||||||
function AdminManageProjects(): JSX.Element {
|
function AdminManageProjects(): JSX.Element {
|
||||||
const [projects, setProjects] = useState<Project[]>([]);
|
const [projects, setProjects] = useState<Project[]>([]);
|
||||||
GetProjects({
|
GetProjects({ setProjectsProp: setProjects });
|
||||||
setProjectsProp: setProjects,
|
|
||||||
username: localStorage.getItem("username") ?? "",
|
|
||||||
});
|
|
||||||
const content = (
|
const content = (
|
||||||
<>
|
<>
|
||||||
<h1 className="font-bold text-[30px] mb-[20px]">Manage Projects</h1>
|
<h1 className="font-bold text-[30px] mb-[20px]">Manage Projects</h1>
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
import AddUserToProject from "../../Components/AddUserToProject";
|
import BackButton from "../../Components/BackButton";
|
||||||
import BasicWindow from "../../Components/BasicWindow";
|
import BasicWindow from "../../Components/BasicWindow";
|
||||||
|
import Button from "../../Components/Button";
|
||||||
|
|
||||||
function AdminProjectAddMember(): JSX.Element {
|
function AdminProjectAddMember(): JSX.Element {
|
||||||
const content = <AddUserToProject />;
|
const content = <></>;
|
||||||
|
|
||||||
const buttons = <></>;
|
const buttons = (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
text="Add"
|
||||||
|
onClick={(): void => {
|
||||||
|
return;
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
<BackButton />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return <BasicWindow content={content} buttons={buttons} />;
|
return <BasicWindow content={content} buttons={buttons} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,16 +151,9 @@ export interface NewProject {
|
||||||
*/
|
*/
|
||||||
export interface RoleChange {
|
export interface RoleChange {
|
||||||
username: string;
|
username: string;
|
||||||
role: "project_manager" | "user";
|
role: 'project_manager' | 'user';
|
||||||
projectname: string;
|
projectname: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NewProjMember {
|
|
||||||
username: string;
|
|
||||||
projectname: string;
|
|
||||||
role: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NameChange {
|
export interface NameChange {
|
||||||
id: number /* int */;
|
id: number /* int */;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -193,8 +186,8 @@ export interface PublicUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserProjectMember {
|
export interface UserProjectMember {
|
||||||
Username: string;
|
Username: string;
|
||||||
UserRole: string;
|
UserRole: string;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* wrapper type for token
|
* wrapper type for token
|
||||||
|
|
Loading…
Add table
Reference in a new issue