updated this branch comparing to the branch soutenance + added the edit of a team
continuous-integration/drone/push Build is failing Details

pull/84/head
Maël DAIM 1 year ago
parent 5aa6a76027
commit dcf118d12b

@ -22,6 +22,7 @@ header h1 a{
.square { .square {
width: 50px; width: 50px;
height: 50px; height: 50px;
border: 2px white solid;
} }
#main_color { #main_color {
@ -33,7 +34,7 @@ header h1 a{
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
width: 60%; width: 60%;
background-color: #8F8F8F; background-color: #8f8f8f;
padding-bottom: 10px; padding-bottom: 10px;
border-radius: 10px; border-radius: 10px;
} }
@ -66,7 +67,6 @@ header h1 a{
color: white; color: white;
} }
#actualColors { #actualColors {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -74,11 +74,10 @@ header h1 a{
} }
#logo { #logo {
width: 90%;
aspect-ratio: 3/2; aspect-ratio: 3/2;
object-fit: contain; object-fit: contain;
max-width: 50%; max-width: 70%;
max-height: 50%; max-height: 70%;
} }
#delete { #delete {
@ -87,6 +86,15 @@ header h1 a{
color: white; color: white;
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
margin-right: 5px;
}
#edit{
border-radius: 10px;
background-color: orange;
color: white;
margin-top: 10px;
margin-bottom: 10px;
} }
#headMembers { #headMembers {
@ -106,7 +114,7 @@ header h1 a{
#Members { #Members {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background-color: #BCBCBC; background-color: #bcbcbc;
width: 60%; width: 60%;
align-items: center; align-items: center;
justify-content: space-around; justify-content: space-around;
@ -123,11 +131,9 @@ header h1 a{
border-radius: 10px; border-radius: 10px;
margin-top: 5px; margin-top: 5px;
margin-bottom: 5px; margin-bottom: 5px;
} }
#profilePicture { #profilePicture {
height: 40px; height: 40px;
width: 40px; width: 40px;
} }

@ -1,19 +1,33 @@
import '../style/team_panel.css'; import "../style/team_panel.css"
import {BASE} from "../Constants"; import { BASE } from "../Constants"
import {Team,TeamInfo,Color,User,Member} from "../model/Team/Team" import { Team, TeamInfo, Color, User, Member } from "../model/team/Team"
export default function TeamPanel({isCoach, team,currentUserId}: {isCoach: boolean, team: Team,currentUserId:number}){ export default function TeamPanel({
isCoach,
team,
currentUserId,
}: {
isCoach: boolean
team: Team
currentUserId: number
}) {
return ( return (
<div id="mainDiv"> <div id="mainDiv">
<header> <header>
<h1><a href="/" >IQBall</a></h1> <h1>
<a href={BASE + "/"}>IQBall</a>
</h1>
</header> </header>
<TeamDisplay team={team.info} /> <TeamDisplay team={team.info} />
{isCoach && <CoachOptions id={team.info.id} />} {isCoach && <CoachOptions id={team.info.id} />}
<MembersDisplay
<MembersDisplay members={team.members} isCoach={isCoach} idTeam={team.info.id} currentUserId={currentUserId}/> members={team.members}
isCoach={isCoach}
idTeam={team.info.id}
currentUserId={currentUserId}
/>
</div> </div>
) )
} }
@ -40,44 +54,115 @@ function TeamDisplay({ team}: {team : TeamInfo}) {
} }
function ColorDisplay({ color }: { color: Color }) { function ColorDisplay({ color }: { color: Color }) {
return( return <div className="square" style={{ backgroundColor: color.hex }} />
<div className="square" style={{backgroundColor: color.hex}}/>
)
} }
function CoachOptions({ id }: { id: number }) { function CoachOptions({ id }: { id: number }) {
return ( return (
<div> <div>
<button id="delete" onClick={()=>confirm('Êtes-vous sûr de supprimer cette équipe?') ? window.location.href=`${BASE}/team/${id}/delete` : {}}>Supprimer</button> <button
id="delete"
onClick={() =>
confirm("Êtes-vous sûr de supprimer cette équipe?")
? (window.location.href = `${BASE}/team/${id}/delete`)
: {}
}>
Supprimer
</button>
<button
id="edit"
onClick={() =>
(window.location.href = `${BASE}/team/${id}/edit`)
}>
Modifier
</button>
</div> </div>
) )
} }
function MembersDisplay({members,isCoach,idTeam,currentUserId}:{members : Member[], isCoach : boolean,idTeam : number,currentUserId:number}){ function MembersDisplay({
const listMember = members.map((member) => members,
<MemberDisplay member={member} isCoach={isCoach} idTeam={idTeam} currentUserId={currentUserId}/> isCoach,
); idTeam,
currentUserId,
}: {
members: Member[]
isCoach: boolean
idTeam: number
currentUserId: number
}) {
const listMember = members.map((member) => (
<MemberDisplay
member={member}
isCoach={isCoach}
idTeam={idTeam}
currentUserId={currentUserId}
/>
))
return ( return (
<div id="Members"> <div id="Members">
<div id="headMembers"> <div id="headMembers">
<h2>Membres :</h2> <h2>Membres :</h2>
{isCoach && <button id="addMember" onClick={()=> window.location.href=`${BASE}/team/${idTeam}/addMember`}>+</button>} {isCoach && (
<button
id="addMember"
onClick={() =>
(window.location.href = `${BASE}/team/${idTeam}/addMember`)
}>
+
</button>
)}
</div> </div>
{listMember} {listMember}
</div> </div>
) )
} }
function MemberDisplay({member,isCoach,idTeam,currentUserId}: {member : Member,isCoach : boolean,idTeam:number,currentUserId:number}){ function MemberDisplay({
member,
isCoach,
idTeam,
currentUserId,
}: {
member: Member
isCoach: boolean
idTeam: number
currentUserId: number
}) {
return ( return (
<div className="Member"> <div className="Member">
<img id="profilePicture" src={member.user.profilePicture} alt="Photo de profile"/> <img
id="profilePicture"
src={member.user.profilePicture}
alt="Photo de profile"
/>
<p>{member.user.name}</p> <p>{member.user.name}</p>
<p>{member.role}</p> <p>{member.role}</p>
<p>{member.user.email}</p> <p>{member.user.email}</p>
{isCoach && currentUserId !== member.user.id && <button id="delete" onClick={()=>confirm("Êtes-vous sûr de retirer ce membre de l'équipe?") ? window.location.href=`${BASE}/team/${idTeam}/remove/${member.user.id}` : {}}>Retirer</button>} {isCoach && currentUserId !== member.user.id && (
{isCoach && currentUserId == member.user.id && <button id="delete" onClick={()=>confirm("Êtes-vous sûr de quitter cette équipe?") ? window.location.href=`${BASE}/team/${idTeam}/remove/${member.user.id}` : {}}>Quitter</button>} <button
id="delete"
onClick={() =>
confirm(
"Êtes-vous sûr de retirer ce membre de l'équipe?",
)
? (window.location.href = `${BASE}/team/${idTeam}/remove/${member.user.id}`)
: {}
}>
Retirer
</button>
)}
{isCoach && currentUserId == member.user.id && (
<button
id="delete"
onClick={() =>
confirm("Êtes-vous sûr de quitter cette équipe?")
? (window.location.href = `${BASE}/team/${idTeam}/remove/${member.user.id}`)
: {}
}>
Quitter
</button>
)}
</div> </div>
) )
} }

@ -1,6 +1,5 @@
<?php <?php
require "../vendor/autoload.php"; require "../vendor/autoload.php";
require "../config.php"; require "../config.php";
require "../sql/database.php"; require "../sql/database.php";
@ -39,7 +38,7 @@ function getConnection(): Connection {
} }
function getUserController(): UserController { function getUserController(): UserController {
return new UserController(new TacticModel(new TacticInfoGateway(getConnection()))); return new UserController(new TacticModel(new TacticInfoGateway(getConnection())), new TeamModel(new TeamGateway(getConnection()), new MemberGateway(getConnection()), new AccountGateway(getConnection())));
} }
function getVisualizerController(): VisualizerController { function getVisualizerController(): VisualizerController {
@ -107,6 +106,9 @@ function getRoutes(): AltoRouter {
$ar->map("GET", "/team/[i:id]/addMember", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->displayAddMember($id, $s))); $ar->map("GET", "/team/[i:id]/addMember", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->displayAddMember($id, $s)));
$ar->map("POST", "/team/[i:id]/addMember", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->addMember($id, $_POST, $s))); $ar->map("POST", "/team/[i:id]/addMember", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->addMember($id, $_POST, $s)));
$ar->map("GET", "/team/[i:idTeam]/remove/[i:idMember]", Action::auth(fn(int $idTeam, int $idMember, SessionHandle $s) => getTeamController()->deleteMember($idTeam, $idMember, $s))); $ar->map("GET", "/team/[i:idTeam]/remove/[i:idMember]", Action::auth(fn(int $idTeam, int $idMember, SessionHandle $s) => getTeamController()->deleteMember($idTeam, $idMember, $s)));
$ar->map("GET", "/team/[i:id]/edit", Action::auth(fn(int $idTeam,SessionHandle $s) => getTeamController()->displayEditTeam($idTeam,$s)));
$ar->map("POST", "/team/[i:id]/edit", Action::auth(fn(int $idTeam,SessionHandle $s) => getTeamController()->editTeam($idTeam,$_POST,$s)));
return $ar; return $ar;
} }
@ -119,10 +121,9 @@ function runMatch($match, MutableSessionHandle $session): HttpResponse {
], HttpCodes::NOT_FOUND); ], HttpCodes::NOT_FOUND);
} }
return App::runAction($basePath . '/login', $match['target'], $match['params'], $session); return App::runAction('/login', $match['target'], $match['params'], $session);
} }
//this is a global variable //this is a global variable
$basePath = get_public_path(__DIR__); $basePath = get_public_path(__DIR__);

@ -64,7 +64,7 @@ class TeamController {
} }
$teamId = $this->model->createTeam($request['name'], $request['picture'], $request['main_color'], $request['second_color']); $teamId = $this->model->createTeam($request['name'], $request['picture'], $request['main_color'], $request['second_color']);
$this->model->addMember($session->getAccount()->getUser()->getEmail(),$teamId,'COACH'); $this->model->addMember($session->getAccount()->getUser()->getEmail(),$teamId,'COACH');
return ViewHttpResponse::redirect('/team/'.$teamId); return HttpResponse::redirect('/team/'.$teamId);
} }
/** /**
@ -101,6 +101,7 @@ class TeamController {
} }
/** /**
* Delete a team with its id
* @param int $id * @param int $id
* @param SessionHandle $session * @param SessionHandle $session
* @return HttpResponse * @return HttpResponse
@ -111,10 +112,11 @@ class TeamController {
if($ret != 0){ if($ret != 0){
return ViewHttpResponse::twig('display_team.html.twig',['notDeleted' => true]); return ViewHttpResponse::twig('display_team.html.twig',['notDeleted' => true]);
} }
return ViewHttpResponse::redirect('/'); return HttpResponse::redirect('/');
} }
/** /**
* Display a team with its id
* @param int $id * @param int $id
* @param SessionHandle $session * @param SessionHandle $session
* @return ViewHttpResponse a view that displays given team information * @return ViewHttpResponse a view that displays given team information
@ -141,6 +143,7 @@ class TeamController {
} }
/** /**
* @param int $idTeam
* @param SessionHandle $session * @param SessionHandle $session
* @return ViewHttpResponse the team panel to add a member * @return ViewHttpResponse the team panel to add a member
*/ */
@ -150,6 +153,7 @@ class TeamController {
/** /**
* add a member to a team * add a member to a team
* @param int $idTeam
* @param array<string, mixed> $request * @param array<string, mixed> $request
* @param SessionHandle $session * @param SessionHandle $session
* @return HttpResponse * @return HttpResponse
@ -158,7 +162,7 @@ class TeamController {
$errors = []; $errors = [];
if(!$this->model->isCoach($idTeam,$session->getAccount()->getUser()->getEmail())){ if(!$this->model->isCoach($idTeam,$session->getAccount()->getUser()->getEmail())){
return ViewHttpResponse::twig('error.html.twig', [ return ViewHttpResponse::twig('error.html.twig', [
'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette équipe.")], 'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette action pour cette équipe.")],
], HttpCodes::FORBIDDEN); ], HttpCodes::FORBIDDEN);
} }
$request = HttpRequest::from($request, $errors, [ $request = HttpRequest::from($request, $errors, [
@ -175,22 +179,68 @@ class TeamController {
case -2: case -2:
return ViewHttpResponse::twig('add_member.html.twig',['alreadyExisting' => true,'idTeam'=> $idTeam]); return ViewHttpResponse::twig('add_member.html.twig',['alreadyExisting' => true,'idTeam'=> $idTeam]);
default: default:
return ViewHttpResponse::redirect('/team/'.$idTeam); return HttpResponse::redirect('/team/'.$idTeam);
} }
} }
/** /**
* remove a member from a team * remove a member from a team with their ids
* @param array<string, mixed> $request * @param int $idTeam
* @param int $idMember
* @param SessionHandle $session * @param SessionHandle $session
* @return HttpResponse * @return HttpResponse
*/ */
public function deleteMember(int $idTeam,int $idMember, SessionHandle $session): HttpResponse { public function deleteMember(int $idTeam,int $idMember, SessionHandle $session): HttpResponse {
$ret = $this->model->deleteMember($idMember,$idTeam); if(!$this->model->isCoach($idTeam,$session->getAccount()->getUser()->getEmail())){
if($ret == -1 || $session->getAccount()->getUser()->getId() == $idMember ){ return ViewHttpResponse::twig('error.html.twig', [
return ViewHttpResponse::redirect('/'); 'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette action pour cette équipe.")],
], HttpCodes::FORBIDDEN);
} }
return $this->displayTeam($ret,$session); $teamId = $this->model->deleteMember($idMember,$idTeam);
if($teamId == -1 || $session->getAccount()->getUser()->getId() == $idMember ){
return HttpResponse::redirect('/');
}
return $this->displayTeam($teamId,$session);
}
/**
* @param int $idTeam
* @param SessionHandle $session
* @return ViewHttpResponse
*/
public function displayEditTeam(int $idTeam,SessionHandle $session): ViewHttpResponse {
return ViewHttpResponse::twig("edit_team.html.twig", ['team' => $this->model->getTeam($idTeam,$session->getAccount()->getUser()->getId())]);
} }
/**
* @param int $idTeam
* @param array $request
* @param SessionHandle $session
* @return HttpResponse
*/
public function editTeam(int $idTeam,array $request,SessionHandle $session): HttpResponse{
if(!$this->model->isCoach($idTeam,$session->getAccount()->getUser()->getEmail())){
return ViewHttpResponse::twig('error.html.twig', [
'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette action pour cette équipe.")],
], HttpCodes::FORBIDDEN);
}
$failures = [];
$request = HttpRequest::from($request, $failures, [
"name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()],
"main_color" => [Validators::hexColor()],
"second_color" => [Validators::hexColor()],
"picture" => [Validators::isURL()],
]);
if (!empty($failures)) {
$badFields = [];
foreach ($failures as $e) {
if ($e instanceof FieldValidationFail) {
$badFields[] = $e->getFieldName();
}
}
return ViewHttpResponse::twig('edit_team.html.twig', ['bad_fields' => $badFields]);
}
$this->model->editTeam($idTeam,$request['name'], $request['picture'], $request['main_color'], $request['second_color']);
return HttpResponse::redirect('/team/'.$idTeam);
}
} }

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Insertion view</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f1f1f1;
}
.container {
max-width: 400px;
margin: 5px auto;
padding: 20px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h2 {
text-align: center;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
}
{% for item in bad_fields %}
#{{ item }}{
border-color: red;
}{% endfor %} input[type="text"], input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
input[type="submit"] {
background-color: #007bff;
color: #fff;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
}
input[type="submit"]:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<h2>Modifier votre équipe</h2>
<form action="{{ path('/team/' ~ team.getInfo().getId() ~ '/edit') }}" method="post">
<div class="form-group">
<label for="name">Nom de l'équipe :</label>
<input type="text" id="name" name="name" value="{{ team.getInfo().getName() }}" required>
<label for="picture">Logo:</label>
<input type="text" id="picture" name="picture" value="{{ team.getInfo().getPicture() }}" required>
<label for="main_color">Couleur principale</label>
<input type="color" value="{{ team.getInfo().getMainColor().getValue() }}" id="main_color" name="main_color" required>
<label for="second_color">Couleur secondaire</label>
<input type="color" id="second_color" name="second_color" value="{{ team.getInfo().getSecondColor().getValue() }}" required>
</div>
<div class="form-group">
<input type="submit" value="Confirmer">
</div>
</form>
</div>
</body>
</html>

@ -41,7 +41,7 @@ class MemberGateway {
*/ */
public function getMembersOfTeam(int $teamId): array { public function getMembersOfTeam(int $teamId): array {
$rows = $this->con->fetch( $rows = $this->con->fetch(
"SELECT a.id,a.email,a.username,a.profilePicture,m.role FROM Account a,Team t,Member m WHERE t.id = :id AND m.id_team = t.id AND m.id_user = a.id", "SELECT a.id,a.email,a.username,a.profilePicture,m.role FROM Account a,team t,Member m WHERE t.id = :id AND m.id_team = t.id AND m.id_user = a.id",
[ [
":id" => [$teamId, PDO::PARAM_INT], ":id" => [$teamId, PDO::PARAM_INT],
] ]

@ -23,7 +23,7 @@ class TeamGateway {
*/ */
public function insert(string $name, string $picture, string $mainColor, string $secondColor): int { public function insert(string $name, string $picture, string $mainColor, string $secondColor): int {
$this->con->exec( $this->con->exec(
"INSERT INTO Team(name, picture, main_color, second_color) VALUES (:team_name , :picture, :main_color, :second_color)", "INSERT INTO team(name, picture, main_color, second_color) VALUES (:team_name , :picture, :main_color, :second_color)",
[ [
":team_name" => [$name, PDO::PARAM_STR], ":team_name" => [$name, PDO::PARAM_STR],
":picture" => [$picture, PDO::PARAM_STR], ":picture" => [$picture, PDO::PARAM_STR],
@ -41,7 +41,7 @@ class TeamGateway {
*/ */
public function listByName(string $name,int $id): array { public function listByName(string $name,int $id): array {
$result = $this->con->fetch( $result = $this->con->fetch(
"SELECT t.* FROM Team t, Member m WHERE t.name LIKE '%' || :name || '%' AND t.id=m.id_team AND m.id_user=:id", "SELECT t.* FROM team t, Member m WHERE t.name LIKE '%' || :name || '%' AND t.id=m.id_team AND m.id_user=:id",
[ [
":name" => [$name, PDO::PARAM_STR], ":name" => [$name, PDO::PARAM_STR],
"id" => [$id, PDO::PARAM_INT] "id" => [$id, PDO::PARAM_INT]
@ -56,7 +56,7 @@ class TeamGateway {
*/ */
public function getTeamById(int $id): ?TeamInfo { public function getTeamById(int $id): ?TeamInfo {
$row = $this->con->fetch( $row = $this->con->fetch(
"SELECT * FROM Team WHERE id = :id", "SELECT * FROM team WHERE id = :id",
[ [
":id" => [$id, PDO::PARAM_INT], ":id" => [$id, PDO::PARAM_INT],
] ]
@ -73,7 +73,7 @@ class TeamGateway {
*/ */
public function getTeamIdByName(string $name): ?int { public function getTeamIdByName(string $name): ?int {
return $this->con->fetch( return $this->con->fetch(
"SELECT id FROM Team WHERE name = :name", "SELECT id FROM team WHERE name = :name",
[ [
":name" => [$name, PDO::PARAM_INT], ":name" => [$name, PDO::PARAM_INT],
] ]
@ -98,4 +98,30 @@ class TeamGateway {
); );
} }
/**
* @param int $idTeam
* @param string $newName
* @param string $newPicture
* @param string $newMainColor
* @param string $newSecondColor
* @return void
*/
public function editTeam(int $idTeam,string $newName,string $newPicture, string $newMainColor, string $newSecondColor){
$this->con->exec(
"UPDATE team
SET name = :newName,
picture = :newPicture,
main_color = :newMainColor,
second_color = :newSecondColor
WHERE id = :team",
[
"team" => [$idTeam, PDO::PARAM_INT],
"newName" => [$newName, PDO::PARAM_STR],
"newPicture" => [$newPicture, PDO::PARAM_STR],
"newMainColor" => [$newMainColor, PDO::PARAM_STR],
"newSecondColor" => [$newSecondColor, PDO::PARAM_STR],
]
);
}
} }

@ -26,6 +26,7 @@ class TeamModel {
} }
/** /**
* Create a team
* @param string $name * @param string $name
* @param string $picture * @param string $picture
* @param string $mainColor * @param string $mainColor
@ -37,7 +38,7 @@ class TeamModel {
} }
/** /**
* adds a member to a team * add a member to a team
* @param string $mail * @param string $mail
* @param int $teamId * @param int $teamId
* @param string $role * @param string $role
@ -67,7 +68,7 @@ class TeamModel {
/** /**
* @param int $idTeam * @param int $idTeam
* @param int $idCurrentUser * @param int $idCurrentUser
* @return ?Team * @return Team|null
*/ */
public function getTeam(int $idTeam, int $idCurrentUser): ?Team { public function getTeam(int $idTeam, int $idCurrentUser): ?Team {
if(!$this->members->isMemberOfTeam($idTeam,$idCurrentUser)){ if(!$this->members->isMemberOfTeam($idTeam,$idCurrentUser)){
@ -80,7 +81,7 @@ class TeamModel {
/** /**
* delete a member from given team identifier * delete a member from given team identifier
* @param string $mail * @param int $idMember
* @param int $teamId * @param int $teamId
* @return int * @return int
*/ */
@ -93,6 +94,12 @@ class TeamModel {
return $teamId; return $teamId;
} }
/**
* Delete a team
* @param string $email
* @param int $idTeam
* @return int
*/
public function deleteTeam(string $email, int $idTeam): int{ public function deleteTeam(string $email, int $idTeam): int{
if($this->members->isCoach($email,$idTeam)){ if($this->members->isCoach($email,$idTeam)){
$this->teams->deleteTeam($idTeam); $this->teams->deleteTeam($idTeam);
@ -101,7 +108,27 @@ class TeamModel {
return -1; return -1;
} }
/**
* Verify if the account associated to an email is in a specific team indicated with its id
* @param int $idTeam
* @param string $email
* @return bool
*/
public function isCoach(int $idTeam, string $email): bool{ public function isCoach(int $idTeam, string $email): bool{
return $this->members->isCoach($email,$idTeam); return $this->members->isCoach($email,$idTeam);
} }
/**
* Edit a team with its id, and replace the current attributes with the new ones
* @param int $idTeam
* @param string $newName
* @param string $newPicture
* @param string $newMainColor
* @param string $newSecondColor
* @return void
*/
public function editTeam(int $idTeam,string $newName,string $newPicture, string $newMainColor, string $newSecondColor){
$this->teams->editTeam($idTeam,$newName,$newPicture, $newMainColor, $newSecondColor);
}
} }

Loading…
Cancel
Save