Merge remote-tracking branch 'origin/frontend' into BumBranch
This commit is contained in:
commit
09f2a2202f
13 changed files with 287 additions and 48 deletions
|
@ -7,6 +7,8 @@ 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);
|
||||
|
||||
|
|
|
@ -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 another user")
|
||||
return c.Status(403).SendString("You can only delete yourself")
|
||||
if username == auth_username {
|
||||
log.Info("User tried to delete itself")
|
||||
return c.Status(403).SendString("You can't delete yourself")
|
||||
}
|
||||
|
||||
if err := gs.Db.RemoveUser(username); err != nil {
|
||||
|
|
|
@ -6,6 +6,8 @@ import {
|
|||
NewProject,
|
||||
UserProjectMember,
|
||||
WeeklyReport,
|
||||
StrNameChange,
|
||||
NewProjMember,
|
||||
} from "../Types/goTypes";
|
||||
|
||||
/**
|
||||
|
@ -132,6 +134,20 @@ interface API {
|
|||
projectName: string,
|
||||
token: string,
|
||||
): 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>>;
|
||||
|
||||
removeProject(
|
||||
projectName: string,
|
||||
|
@ -174,19 +190,17 @@ export const api: API = {
|
|||
): Promise<APIResponse<User>> {
|
||||
try {
|
||||
const response = await fetch(`/api/userdelete/${username}`, {
|
||||
method: "POST",
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer " + token,
|
||||
},
|
||||
body: JSON.stringify(username),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return { success: false, message: "Failed to remove user" };
|
||||
return { success: false, message: "Could not remove user" };
|
||||
} else {
|
||||
const data = (await response.json()) as User;
|
||||
return { success: true, data };
|
||||
return { success: true };
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, message: "Failed to remove user" };
|
||||
|
@ -248,6 +262,30 @@ 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>> {
|
||||
try {
|
||||
const response = await fetch("/api/loginrenew", {
|
||||
|
@ -490,6 +528,30 @@ 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" };
|
||||
}
|
||||
},
|
||||
|
||||
async removeProject(
|
||||
projectName: string,
|
||||
token: string,
|
||||
|
|
39
frontend/src/Components/AddMember.tsx
Normal file
39
frontend/src/Components/AddMember.tsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
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;
|
92
frontend/src/Components/AddUserToProject.tsx
Normal file
92
frontend/src/Components/AddUserToProject.tsx
Normal file
|
@ -0,0 +1,92 @@
|
|||
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,23 +1,48 @@
|
|||
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<HTMLInputElement>): void => {
|
||||
setNewUsername(e.target.value);
|
||||
};
|
||||
|
||||
// const handleSubmit = async (): Promise<void> => {
|
||||
// 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
|
||||
// }
|
||||
// };
|
||||
const handleSubmit = async (): Promise<void> => {
|
||||
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);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -27,6 +52,8 @@ function ChangeUsername(): JSX.Element {
|
|||
value={newUsername}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{errorMessage && <div>{errorMessage}</div>}
|
||||
<button onClick={handleButtonClick}>Update Username</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import { api, APIResponse } from "../API/API";
|
|||
*/
|
||||
|
||||
function DeleteUser(props: { usernameToDelete: string }): boolean {
|
||||
//console.log(props.usernameToDelete); FOR DEBUG
|
||||
let removed = false;
|
||||
api
|
||||
.removeUser(
|
||||
|
@ -20,12 +19,16 @@ function DeleteUser(props: { usernameToDelete: string }): boolean {
|
|||
)
|
||||
.then((response: APIResponse<User>) => {
|
||||
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;
|
||||
|
|
|
@ -2,6 +2,7 @@ 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;
|
||||
|
@ -18,9 +19,12 @@ function ProjectInfoModal(props: {
|
|||
className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm
|
||||
flex justify-center items-center"
|
||||
>
|
||||
<div className="border-4 border-black bg-white p-2 rounded-2xl text-center h-[41vh] w-[40vw] flex flex-col">
|
||||
<div className="border-4 border-black bg-white p-2 rounded-2xl text-center h-[47vh] w-[40] flex flex-col">
|
||||
<div className="pl-20 pr-20">
|
||||
<h1 className="font-bold text-[32px] mb-[20px]">Project members:</h1>
|
||||
<h1 className="font-bold text-[32px] mb-[20px]">
|
||||
{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]">
|
||||
<ul className="text-left font-medium space-y-2">
|
||||
<div></div>
|
||||
|
@ -50,6 +54,15 @@ function ProjectInfoModal(props: {
|
|||
}}
|
||||
type="button"
|
||||
/>
|
||||
<Link to={"/adminProjectAddMember"}>
|
||||
<Button
|
||||
text={"Add Member"}
|
||||
onClick={function (): void {
|
||||
return;
|
||||
}}
|
||||
type="button"
|
||||
/>
|
||||
</Link>
|
||||
<Button
|
||||
text={"Close"}
|
||||
onClick={function (): void {
|
||||
|
|
|
@ -2,7 +2,6 @@ import { useState } from "react";
|
|||
import { NewProject } from "../Types/goTypes";
|
||||
import ProjectInfoModal from "./ProjectInfoModal";
|
||||
import UserInfoModal from "./UserInfoModal";
|
||||
import DeleteUser from "./DeleteUser";
|
||||
|
||||
/**
|
||||
* A list of projects for admin manage projects page, that sets an onClick
|
||||
|
@ -28,8 +27,9 @@ export function ProjectListAdmin(props: {
|
|||
setUserModalVisible(true);
|
||||
};
|
||||
|
||||
const handleClickProject = (username: string): void => {
|
||||
setProjectname(username);
|
||||
const handleClickProject = (projectname: string): void => {
|
||||
setProjectname(projectname);
|
||||
localStorage.setItem("projectName", projectname);
|
||||
setProjectModalVisible(true);
|
||||
};
|
||||
|
||||
|
@ -55,7 +55,9 @@ export function ProjectListAdmin(props: {
|
|||
manageMember={true}
|
||||
onClose={handleCloseUser}
|
||||
//TODO: CHANGE TO REMOVE USER FROM PROJECT
|
||||
onDelete={() => DeleteUser}
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
isVisible={userModalVisible}
|
||||
username={username}
|
||||
/>
|
||||
|
|
|
@ -4,7 +4,6 @@ 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.
|
||||
|
@ -15,8 +14,6 @@ export default function Register(): JSX.Element {
|
|||
const [password, setPassword] = useState<string>();
|
||||
const [errMessage, setErrMessage] = useState<string>();
|
||||
|
||||
const nav = useNavigate();
|
||||
|
||||
const handleRegister = async (): Promise<void> => {
|
||||
const newUser: NewUser = {
|
||||
username: username ?? "",
|
||||
|
@ -24,8 +21,9 @@ export default function Register(): JSX.Element {
|
|||
};
|
||||
const response = await api.registerUser(newUser);
|
||||
if (response.success) {
|
||||
nav("/"); // Instantly navigate to the login page
|
||||
alert("User added!");
|
||||
} else {
|
||||
alert("User not added");
|
||||
setErrMessage(response.message ?? "Unknown error");
|
||||
console.error(errMessage);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,13 @@ function UserInfoModal(props: {
|
|||
<Button
|
||||
text={"Delete"}
|
||||
onClick={function (): void {
|
||||
DeleteUser({ usernameToDelete: props.username });
|
||||
if (
|
||||
window.confirm("Are you sure you want to delete this user?")
|
||||
) {
|
||||
DeleteUser({
|
||||
usernameToDelete: props.username,
|
||||
});
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
/>
|
||||
|
|
|
@ -1,22 +1,10 @@
|
|||
import BackButton from "../../Components/BackButton";
|
||||
import AddUserToProject from "../../Components/AddUserToProject";
|
||||
import BasicWindow from "../../Components/BasicWindow";
|
||||
import Button from "../../Components/Button";
|
||||
|
||||
function AdminProjectAddMember(): JSX.Element {
|
||||
const content = <></>;
|
||||
const content = <AddUserToProject />;
|
||||
|
||||
const buttons = (
|
||||
<>
|
||||
<Button
|
||||
text="Add"
|
||||
onClick={(): void => {
|
||||
return;
|
||||
}}
|
||||
type="button"
|
||||
/>
|
||||
<BackButton />
|
||||
</>
|
||||
);
|
||||
const buttons = <></>;
|
||||
|
||||
return <BasicWindow content={content} buttons={buttons} />;
|
||||
}
|
||||
|
|
|
@ -151,9 +151,16 @@ export interface NewProject {
|
|||
*/
|
||||
export interface RoleChange {
|
||||
username: string;
|
||||
role: 'project_manager' | 'user';
|
||||
role: "project_manager" | "user";
|
||||
projectname: string;
|
||||
}
|
||||
|
||||
export interface NewProjMember {
|
||||
username: string;
|
||||
projectname: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface NameChange {
|
||||
id: number /* int */;
|
||||
name: string;
|
||||
|
|
Loading…
Reference in a new issue