Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions frontend/react-app/src/api/isMemberOfApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,16 @@ const BASE_URL = "http://localhost:8080/api/memberships";

//Add a member to a team
export const addMemberToTeam = async (teamMemberId, teamId) => {
try {
const response = await fetch(`${BASE_URL}/${teamMemberId}/team/${teamId}`, {
method: 'POST',
headers: { "Content-Type": "application/json" }
});

if (!response.ok) {
console.error(`Failed to add member to team: ${response.status} ${response.statusText}`);
return false;
throw Error(`Failed to add member to team: ${response.status} ${response.statusText}`);
}

return true;
}
catch (error) {
console.error("Error adding member to team: ", error);
return false;
}
};

//Remove a member from a team
Expand Down
74 changes: 74 additions & 0 deletions frontend/react-app/src/components/AddToTeam.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Select from 'react-select'
import {useForm, Controller} from 'react-hook-form'
import { addMemberToTeam } from '../api/isMemberOfApi';
export default function AddToTeam({teamId, allTeamMembers, currentMembers, setTeamMembers}){
console.log('allMembers', allTeamMembers)
const { reset, handleSubmit, formState: {errors}, control} = useForm();
function formatTeamData(){
let returnArr = []
allTeamMembers.map((member)=>{
let isOnTeam = false;
currentMembers.map((currentMember)=>{
if(member.accountId === currentMember){
isOnTeam = true;
}
})
if(!isOnTeam){
returnArr= [...returnArr, {value: member.accountId, label: member.userName, role: member.role}]
}
})
return returnArr;
}
const onSubmit = async (data)=>{
try {
await data.assignees.map(async(assignee)=>{
const response = await addMemberToTeam(assignee.value, teamId);
setTeamMembers((prev)=>([...prev, {accountId: assignee.value, userName: assignee.label, role: assignee.role}]))
})
reset({})
} catch (error) {
console.log(error)
}
}
const customStyles = {
control: (provided) => ({
...provided,
width: '100%',
minWidth: '200px',
maxWidth: '100%',
minHeight: '30px',
maxHeight: '100%',
border: '2px solid grey',
borderRadius: '10px',
paddingLeft: '8px',
backgroundColor: '#BFCDE0',
margin: '10px 0px',
cursor: 'pointer'
}),
option: (provided, state) =>({
...provided,
cursor: state.isSelected ? 'default' : 'pointer',
})
};

return(
<form onSubmit={handleSubmit(onSubmit)} className ='rowFlexbox 'style={{justifyContent: 'stretch', width: '50%'}}>
<Controller
control={control}
className='Select'
name="assignees"
rules={{required:true}}
defaultValue={[]}
render={({field}) => (
<Select
{...field}
options={formatTeamData()}
isMulti
styles={customStyles}
/>)}
/>
<button type="submit" className='smallImportButton'>Add To Team</button>
</form>
)

}
16 changes: 16 additions & 0 deletions frontend/react-app/src/components/RemoveUserButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { removeMemberFromTeam } from "../api/isMemberOfApi"

export default function RemoveUserButton({userId, teamId,setTeamMembers, teamMembers}){
async function removeUser(){
try {
const response = removeMemberFromTeam(userId,teamId)
setTeamMembers(
teamMembers.filter((teamMember)=>(teamMember.accountId !== userId))
)
} catch (error) {
console.log(error)
}
}
return(
<button className='smallImportButton' onClick={removeUser}>Remove User</button>
)}
8 changes: 5 additions & 3 deletions frontend/react-app/src/components/TeamMember.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import "../css/TeamTasks.css";
import RemoveUserButton from "./RemoveUserButton";

function TeamMember({ member,isAdminPage }){
function TeamMember({ member,isAdminPage, teamLeadId, setTeamMembers, teamId, teamMembers}){
return(
<div className="team-member">
<span className="avatar">👤</span>
<span className="name">{member.userName} {member.role === "You" && "(You)"}</span>
{member.role === "Admin" && <span className="role admin">Admin</span>}
{isAdminPage && member.role !== "Admin" && (<button className="remove-btn">Remove</button>)}
{member.role === "ADMIN" && <span className="role" >Admin</span>}
{member.accountId === teamLeadId && <span className="role" style={{backgroundColor:'#2b0fa7'}}>Team Lead</span>}
{isAdminPage && member.accountId !== teamLeadId && (<RemoveUserButton userId={member.accountId} setTeamMembers = {setTeamMembers} teamMembers={teamMembers} teamId={teamId}/>)}
</div>
);
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/react-app/src/components/Teams.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Link} from 'react-router-dom'
function Teams({team}){
return(
<div className="teams">
<Link className="teamButton headerText1" to='/team-tasks' state={{ teamId: team.teamId}} key={team.teamId} >
<Link className="teamButton headerText1" to='/team-tasks' state={{ team: team}} key={team.teamId} >
{team.teamName}
</Link>

Expand Down
11 changes: 4 additions & 7 deletions frontend/react-app/src/css/TeamTasks.css
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,12 @@



.team-list {
width: 250px;
}


.team-member {
display: flex;
align-items: center;
width: 100%;
padding: 8px 0;
}

Expand All @@ -56,15 +55,13 @@
margin-right: 10px;
}

.name {
flex-grow: 1;
}

.role.admin {
.role {
background-color: #ff7043;
color: white;
font-size: 12px;
padding: 3px 8px;
margin-left: 10px;
border-radius: 12px;
font-weight: bold;
}
Expand Down
17 changes: 17 additions & 0 deletions frontend/react-app/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ html {
.headerText1{
font-size: calc(1rem + 1vw);
}
.subHeader1{
font-size: calc(0.7rem + 0.7vw);
}
.pageContainer{
position: absolute;
margin: 0;
Expand Down Expand Up @@ -83,3 +86,17 @@ li.logo a{
background-color: #3f5d8b;
cursor: pointer;
}
.smallImportButton{
padding-left: 5px;
padding-right: 5px;
padding-top: 3px;
padding-bottom: 3px;
font-size: 1em;
margin-top: 5px;
margin-left: 5px;
margin-bottom: 5px;
border: none;
border-radius: 10px;
background-color: #3f5d8b;
cursor: pointer;
}
9 changes: 7 additions & 2 deletions frontend/react-app/src/pages/AdminPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ export default function AdminPanel(){
console.log(data)
//send to teamTasks with team id as prop.
//For now because I do not have admin team tasks I will send them to team tasks
navigate('/team-tasks', {state: {teamId: data.teamId}})
teams.map((team)=>{
console.log(team.teamId, data.teamId)
if(String(team.teamId) === data.teamId){
navigate('/team-tasks', {state: {team: team}})
}
})
};
useEffect(()=>{
async function allTeams() {
Expand Down Expand Up @@ -48,7 +53,7 @@ export default function AdminPanel(){
<form onSubmit={handleSubmit(onSubmit)} className="goToTeamTaskForm">
<select name="" id="" {...register("teamId")}>
{teams.map((team)=>(
<option value={team.teamId}>{team.teamName}</option>
<option value={team.teamId} key={team.teamId}>{team.teamName}</option>
))}
</select>
<input type="submit" value="Go to team task page" className="button"/>
Expand Down
2 changes: 1 addition & 1 deletion frontend/react-app/src/pages/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ const Home = () => {

<div id="teamButtons" className='teamButtonContainer'>
{teams.map((team)=>(
<Link className="teamButton headerText1" to='/team-tasks' state={{ teamId: team.teamId}} key={team.teamId} >
<Link className="teamButton headerText1" to='/team-tasks' state={{ team: team}} key={team.teamId} >
{team.teamName}
</Link>

Expand Down
57 changes: 50 additions & 7 deletions frontend/react-app/src/pages/TeamTasks.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import fakeData from "../FakeData/fakeTaskData.json"
import { useCookies } from 'react-cookie';
import { useState, useEffect } from 'react';
import { getTeamMembers } from "../api/teamApi";
import { getTeamMembers as getAllTeamMembers}from "../api/teamMemberAccountApi";
import { useLocation } from 'react-router-dom';
import { getTeamTasks } from "../api/teamApi";
import DeleteTeamButton from "../components/DeleteTeamButton";
import { getAdmins } from "../api/adminApi";
import AddToTeam from "../components/AddToTeam";

function getAssigneesNames(taskItem) {
return taskItem.assignedMembers.map((member) => member.userName).join(", ");
Expand Down Expand Up @@ -100,16 +103,21 @@ function TeamTasks(){
const [teamMembers, setTeamMembers ] = useState([]);
const [loadingNames, setLoadingNames] = useState(true);
const [loadingTasks, setLoadingTasks] = useState(true);
const [allUsersLoading, setallUsersLoading] = useState(true)

const location = useLocation();
const { teamId } = location.state;
const { team } = location.state;
const teamId = team.teamId;
console.log("teamId:", teamId);

const [tasksToDo, setTasksToDo ] = useState([]);

const [allUsers, setAllUsers] = useState();

async function fetchData(){
try{
if(cookies.userInfo.role === 'admin'){
getAllUsers();
}
const results = await getTeamTasks(teamId);
console.log("Team Tasks Results:", results);
setTasksToDo(results);
Expand All @@ -119,6 +127,20 @@ function TeamTasks(){
setLoadingTasks(false)
}
}

async function getAllUsers() {
try {
const adminResponse = await getAdmins();
const teamMemberResposne = await getAllTeamMembers()
setAllUsers(adminResponse.concat(teamMemberResposne))
console.log(teamMemberResposne)
} catch (error) {
await alert("FAILED TO LOAD CONTACT NETWORK ADMIN")
}finally{
setallUsersLoading(false)
}
}



useEffect(()=>{
Expand Down Expand Up @@ -151,7 +173,7 @@ if(loadingNames || loadingTasks){


//mock
const isAdmin = false;
const isAdmin = cookies.userInfo.role ==='admin';

//mock
const members = [
Expand All @@ -171,7 +193,7 @@ if(loadingNames || loadingTasks){
<div className='pageContainer'>
<Header/>
<div className='pageBody'>
<h2>Team 1 Tasks</h2>
<h2>{team.teamName}</h2>
{tasksToDoData.length > 0 ? (
<TaskList
dataToUse={tasksToDoData}
Expand All @@ -198,14 +220,35 @@ if(loadingNames || loadingTasks){
<h2>Team Members</h2>
<div className="team-list">
{teamMembers.map((member) => (
<TeamMember key={member.teamId} member={member} isAdminPage={isAdmin}/>
<TeamMember key={member.teamId} member={member} teamLeadId = {team.teamLeadId}
setTeamMembers = {setTeamMembers}
teamId={teamId}
isAdminPage={isAdmin}
teamMembers={teamMembers}
/>
))}
</div>
{cookies.userInfo.role === 'admin'&&
{allUsersLoading&&(<div>..Loading</div>)}
{cookies.userInfo.role === 'admin'&& !allUsersLoading &&
(
<DeleteTeamButton
<div style={{marginTop: '30px'}}>
<h2>Add New Team Member</h2>
<div>
<AddToTeam
teamId={teamId}
allTeamMembers={allUsers}
currentMembers = {
(teamMembers.map((member)=>member.accountId))
}
setTeamMembers = {setTeamMembers}
/>
</div>
<div>
<DeleteTeamButton
teamId={teamId}
/>
</div>
</div>
)
}

Expand Down