From dcf118d12bc5d8650e50455b3f065992d5b4faf2 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Mon, 8 Jan 2024 12:36:29 +0100 Subject: [PATCH] updated this branch comparing to the branch soutenance + added the edit of a team --- front/model/{Team => team}/Team.ts | 0 front/style/team_panel.css | 58 ++++++----- front/views/TeamPanel.tsx | 143 ++++++++++++++++++++------ public/index.php | 19 ++-- src/App/Controller/TeamController.php | 70 +++++++++++-- src/App/Views/edit_team.html.twig | 81 +++++++++++++++ src/Core/Gateway/MemberGateway.php | 2 +- src/Core/Gateway/TeamGateway.php | 34 +++++- src/Core/Model/TeamModel.php | 33 +++++- 9 files changed, 358 insertions(+), 82 deletions(-) rename front/model/{Team => team}/Team.ts (100%) create mode 100644 src/App/Views/edit_team.html.twig diff --git a/front/model/Team/Team.ts b/front/model/team/Team.ts similarity index 100% rename from front/model/Team/Team.ts rename to front/model/team/Team.ts diff --git a/front/style/team_panel.css b/front/style/team_panel.css index 8c1d435..d228d31 100644 --- a/front/style/team_panel.css +++ b/front/style/team_panel.css @@ -5,7 +5,7 @@ height: 100%; } -header{ +header { display: flex; justify-content: center; background-color: #525252; @@ -13,7 +13,7 @@ header{ margin-bottom: 5px; } -header h1 a{ +header h1 a { color: orange; text-decoration: none; font-size: 1.5em; @@ -22,33 +22,34 @@ header h1 a{ .square { width: 50px; height: 50px; + border: 2px white solid; } #main_color { border: solid; } -#teamInfo{ +#teamInfo { display: flex; flex-direction: column; align-items: center; width: 60%; - background-color: #8F8F8F; + background-color: #8f8f8f; padding-bottom: 10px; border-radius: 10px; } -#firstPart{ +#firstPart { display: flex; flex-direction: column; align-items: center; } -#teamName{ +#teamName { font-size: 2.8em; } -#colors{ +#colors { display: flex; flex-direction: column; } @@ -57,7 +58,7 @@ header h1 a{ justify-content: space-between; } -#colorsTitle{ +#colorsTitle { width: 110%; display: flex; flex-direction: row; @@ -66,54 +67,61 @@ header h1 a{ color: white; } - -#actualColors{ +#actualColors { display: flex; flex-direction: row; justify-content: space-around; } #logo { - width: 90%; aspect-ratio: 3/2; object-fit: contain; - max-width: 50%; - max-height: 50%; + max-width: 70%; + max-height: 70%; } -#delete{ - border-radius:10px ; +#delete { + border-radius: 10px; background-color: red; color: white; margin-top: 10px; margin-bottom: 10px; + margin-right: 5px; } -#headMembers{ +#edit{ + border-radius: 10px; + background-color: orange; + color: white; + margin-top: 10px; + margin-bottom: 10px; +} + +#headMembers { width: 33%; display: flex; flex-direction: row; justify-content: space-evenly; } -#addMember{ +#addMember { height: 30px; aspect-ratio: 1/1; border-radius: 100%; align-self: center; } -#Members{ +#Members { display: flex; flex-direction: column; - background-color: #BCBCBC; + background-color: #bcbcbc; width: 60%; align-items: center; justify-content: space-around; border-radius: 10px; } -.Member{ +.Member { width: 60%; background-color: white; display: flex; @@ -123,11 +131,9 @@ header h1 a{ border-radius: 10px; margin-top: 5px; margin-bottom: 5px; - -} - -#profilePicture{ - height:40px; - width:40px; } +#profilePicture { + height: 40px; + width: 40px; +} \ No newline at end of file diff --git a/front/views/TeamPanel.tsx b/front/views/TeamPanel.tsx index 2211145..fc77915 100644 --- a/front/views/TeamPanel.tsx +++ b/front/views/TeamPanel.tsx @@ -1,24 +1,38 @@ -import '../style/team_panel.css'; -import {BASE} from "../Constants"; -import {Team,TeamInfo,Color,User,Member} from "../model/Team/Team" +import "../style/team_panel.css" +import { BASE } from "../Constants" +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 (
-

IQBall

+

+ IQBall +

- + - {isCoach && } + {isCoach && } - - +
) } -function TeamDisplay({ team}: {team : TeamInfo}) { +function TeamDisplay({ team }: { team: TeamInfo }) { return (
@@ -31,53 +45,124 @@ function TeamDisplay({ team}: {team : TeamInfo}) {

Couleur secondaire

- - + +
) } -function ColorDisplay({color}: {color : Color}){ - return( -
- ) +function ColorDisplay({ color }: { color: Color }) { + return
} -function CoachOptions ({id}:{id:number}){ +function CoachOptions({ id }: { id: number }) { return (
- + +
) } -function MembersDisplay({members,isCoach,idTeam,currentUserId}:{members : Member[], isCoach : boolean,idTeam : number,currentUserId:number}){ - const listMember = members.map((member) => - - ); +function MembersDisplay({ + members, + isCoach, + idTeam, + currentUserId, + }: { + members: Member[] + isCoach: boolean + idTeam: number + currentUserId: number +}) { + const listMember = members.map((member) => ( + + )) return (

Membres :

- {isCoach && } + {isCoach && ( + + )}
{listMember}
) } -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 (
- Photo de profile + Photo de profile

{member.user.name}

{member.role}

{member.user.email}

- {isCoach && currentUserId !== member.user.id && } - {isCoach && currentUserId == member.user.id && } - + {isCoach && currentUserId !== member.user.id && ( + + )} + {isCoach && currentUserId == member.user.id && ( + + )}
) -} +} \ No newline at end of file diff --git a/public/index.php b/public/index.php index 4a4f0d4..113e79d 100644 --- a/public/index.php +++ b/public/index.php @@ -1,6 +1,5 @@ map("GET", "/team/search", Action::auth(fn(SessionHandle $s) => getTeamController()->displayListTeamByName($s))); $ar->map("POST", "/team/search", Action::auth(fn(SessionHandle $s) => getTeamController()->listTeamByName($_POST, $s))); $ar->map("GET", "/team/[i:id]", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->displayTeam($id, $s))); - $ar->map("GET", "/team/[i:id]/delete", Action::auth(fn(int $id,SessionHandle $s) => getTeamController()->deleteTeamById($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("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]/delete", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->deleteTeamById($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("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; } @@ -119,11 +121,10 @@ function runMatch($match, MutableSessionHandle $session): HttpResponse { ], 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 $basePath = get_public_path(__DIR__); -App::render(runMatch(getRoutes()->match(), PhpSessionHandle::init()), fn() => getTwig()); +App::render(runMatch(getRoutes()->match(), PhpSessionHandle::init()), fn() => getTwig()); \ No newline at end of file diff --git a/src/App/Controller/TeamController.php b/src/App/Controller/TeamController.php index acc4839..92ecc14 100644 --- a/src/App/Controller/TeamController.php +++ b/src/App/Controller/TeamController.php @@ -64,7 +64,7 @@ class TeamController { } $teamId = $this->model->createTeam($request['name'], $request['picture'], $request['main_color'], $request['second_color']); $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 SessionHandle $session * @return HttpResponse @@ -111,10 +112,11 @@ class TeamController { if($ret != 0){ 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 SessionHandle $session * @return ViewHttpResponse a view that displays given team information @@ -141,6 +143,7 @@ class TeamController { } /** + * @param int $idTeam * @param SessionHandle $session * @return ViewHttpResponse the team panel to add a member */ @@ -150,6 +153,7 @@ class TeamController { /** * add a member to a team + * @param int $idTeam * @param array $request * @param SessionHandle $session * @return HttpResponse @@ -158,7 +162,7 @@ class TeamController { $errors = []; 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 équipe.")], + 'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette action pour cette équipe.")], ], HttpCodes::FORBIDDEN); } $request = HttpRequest::from($request, $errors, [ @@ -175,22 +179,68 @@ class TeamController { case -2: return ViewHttpResponse::twig('add_member.html.twig',['alreadyExisting' => true,'idTeam'=> $idTeam]); default: - return ViewHttpResponse::redirect('/team/'.$idTeam); + return HttpResponse::redirect('/team/'.$idTeam); } } /** - * remove a member from a team - * @param array $request + * remove a member from a team with their ids + * @param int $idTeam + * @param int $idMember * @param SessionHandle $session * @return HttpResponse */ public function deleteMember(int $idTeam,int $idMember, SessionHandle $session): HttpResponse { - $ret = $this->model->deleteMember($idMember,$idTeam); - if($ret == -1 || $session->getAccount()->getUser()->getId() == $idMember ){ - return ViewHttpResponse::redirect('/'); + 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); } - 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); + } } diff --git a/src/App/Views/edit_team.html.twig b/src/App/Views/edit_team.html.twig new file mode 100644 index 0000000..00eef84 --- /dev/null +++ b/src/App/Views/edit_team.html.twig @@ -0,0 +1,81 @@ + + + + + Insertion view + + + + +
+

Modifier votre équipe

+
+
+ + + + + + + + +
+
+ +
+
+
+ + + \ No newline at end of file diff --git a/src/Core/Gateway/MemberGateway.php b/src/Core/Gateway/MemberGateway.php index 9fb289a..aeb2042 100644 --- a/src/Core/Gateway/MemberGateway.php +++ b/src/Core/Gateway/MemberGateway.php @@ -41,7 +41,7 @@ class MemberGateway { */ public function getMembersOfTeam(int $teamId): array { $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], ] diff --git a/src/Core/Gateway/TeamGateway.php b/src/Core/Gateway/TeamGateway.php index 0160e00..601fe25 100644 --- a/src/Core/Gateway/TeamGateway.php +++ b/src/Core/Gateway/TeamGateway.php @@ -23,7 +23,7 @@ class TeamGateway { */ public function insert(string $name, string $picture, string $mainColor, string $secondColor): int { $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], ":picture" => [$picture, PDO::PARAM_STR], @@ -41,7 +41,7 @@ class TeamGateway { */ public function listByName(string $name,int $id): array { $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], "id" => [$id, PDO::PARAM_INT] @@ -56,7 +56,7 @@ class TeamGateway { */ public function getTeamById(int $id): ?TeamInfo { $row = $this->con->fetch( - "SELECT * FROM Team WHERE id = :id", + "SELECT * FROM team WHERE id = :id", [ ":id" => [$id, PDO::PARAM_INT], ] @@ -73,7 +73,7 @@ class TeamGateway { */ public function getTeamIdByName(string $name): ?int { return $this->con->fetch( - "SELECT id FROM Team WHERE name = :name", + "SELECT id FROM team WHERE name = :name", [ ":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], + ] + ); + } + } diff --git a/src/Core/Model/TeamModel.php b/src/Core/Model/TeamModel.php index 2dc1162..6bdd615 100644 --- a/src/Core/Model/TeamModel.php +++ b/src/Core/Model/TeamModel.php @@ -26,6 +26,7 @@ class TeamModel { } /** + * Create a team * @param string $name * @param string $picture * @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 int $teamId * @param string $role @@ -67,7 +68,7 @@ class TeamModel { /** * @param int $idTeam * @param int $idCurrentUser - * @return ?Team + * @return Team|null */ public function getTeam(int $idTeam, int $idCurrentUser): ?Team { if(!$this->members->isMemberOfTeam($idTeam,$idCurrentUser)){ @@ -80,7 +81,7 @@ class TeamModel { /** * delete a member from given team identifier - * @param string $mail + * @param int $idMember * @param int $teamId * @return int */ @@ -93,6 +94,12 @@ class TeamModel { return $teamId; } + /** + * Delete a team + * @param string $email + * @param int $idTeam + * @return int + */ public function deleteTeam(string $email, int $idTeam): int{ if($this->members->isCoach($email,$idTeam)){ $this->teams->deleteTeam($idTeam); @@ -101,7 +108,27 @@ class TeamModel { 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{ 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); + } + }