diff --git a/.gitignore b/.gitignore index a02dfdf..3934c5c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vs +.vscode .idea .code .vite diff --git a/config.php b/config.php index 6e510c8..fdf02a4 100644 --- a/config.php +++ b/config.php @@ -11,6 +11,7 @@ const SUPPORTS_FAST_REFRESH = _SUPPORTS_FAST_REFRESH; * Maps the given relative source uri (relative to the `/front` folder) to its actual location depending on imported profile. * @param string $assetURI relative uri path from `/front` folder * @return string valid url that points to the given uri + */ function asset(string $assetURI): string { return _asset($assetURI); diff --git a/front/assets/account.svg b/front/assets/account.svg new file mode 100644 index 0000000..70d7391 --- /dev/null +++ b/front/assets/account.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front/assets/court/court.svg b/front/assets/court/court.svg new file mode 100644 index 0000000..e01fd58 --- /dev/null +++ b/front/assets/court/court.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/front/components/editor/BasketCourt.tsx b/front/components/editor/BasketCourt.tsx index 525e232..1e3d916 100644 --- a/front/components/editor/BasketCourt.tsx +++ b/front/components/editor/BasketCourt.tsx @@ -9,6 +9,7 @@ import { } from "react" import CourtPlayer from "./CourtPlayer" + import { Player } from "../../model/tactic/Player" import { Action, ActionKind } from "../../model/tactic/Action" import ArrowAction from "../actions/ArrowAction" diff --git a/front/model/team/Team.ts b/front/model/Team.ts similarity index 71% rename from front/model/team/Team.ts rename to front/model/Team.ts index 01a9d10..99d530b 100644 --- a/front/model/team/Team.ts +++ b/front/model/Team.ts @@ -1,3 +1,5 @@ +import {User} from "./User"; + export interface TeamInfo { id: number name: string @@ -15,10 +17,3 @@ export interface Member { user: User role: string } - -export interface User { - id: number - name: string - email: string - profilePicture: string -} diff --git a/front/model/User.ts b/front/model/User.ts new file mode 100644 index 0000000..36bbb67 --- /dev/null +++ b/front/model/User.ts @@ -0,0 +1,6 @@ +export interface User { + id: number + name: string + email: string + profilePicture: string +} diff --git a/front/style/ball.css b/front/style/ball.css index c14c196..5169ec7 100644 --- a/front/style/ball.css +++ b/front/style/ball.css @@ -9,3 +9,6 @@ height: 20px; cursor: pointer; } + +.ball-div:focus-within { +} diff --git a/front/style/colors.css b/front/style/colors.css deleted file mode 100644 index db7edfe..0000000 --- a/front/style/colors.css +++ /dev/null @@ -1,13 +0,0 @@ -:root { - --main-color: #ffffff; - --second-color: #ccde54; - - --background-color: #d2cdd3; - - --selected-team-primarycolor: #ffffff; - --selected-team-secondarycolor: #000000; - - --selection-color: #3f7fc4; - - --arrows-color: #676767; -} diff --git a/front/style/home/home.css b/front/style/home/home.css new file mode 100644 index 0000000..455e3af --- /dev/null +++ b/front/style/home/home.css @@ -0,0 +1,43 @@ +@import url(../theme/dark.css); +@import url(personnal_space.css); +@import url(side_menu.css); +@import url(../template/header.css); + +body { + /* background-color: #303030; */ +} +#main { + /* margin-left : 10%; + margin-right: 10%; */ + /* border : solid 1px #303030; */ + display: flex; + flex-direction: column; + font-family: var(--font-content); + height: 100%; +} + +#body { + display: flex; + flex-direction: row; + margin: 0px; + height: 100%; + background-color: var(--second-color); +} + +.data { + border: 1.5px solid var(--main-contrast-color); + background-color: var(--main-color); + border-radius: 0.75cap; + color: var(--main-contrast-color); +} + +.data:hover { + border-color: var(--accent-color); + cursor: pointer; +} + +.set-button { + width: 80%; + margin-left: 5%; + margin-top: 5%; +} diff --git a/front/style/home/personnal_space.css b/front/style/home/personnal_space.css new file mode 100644 index 0000000..173098e --- /dev/null +++ b/front/style/home/personnal_space.css @@ -0,0 +1,40 @@ +#personal-space { + display: flex; + flex-direction: column; +} + +#title-personal-space h2 { + text-align: center; + color: var(--main-contrast-color); + /* font-family: Helvetica; + font-weight: bold; */ +} + +#body-personal-space { + width: 95%; + /* background-color: #ccc2b7; */ + border: 3px var(--main-color) solid; + border-radius: 0.5cap; + align-self: center; +} + +#body-personal-space table { + width: 100%; + border-collapse: separate; + border-spacing: 1em; + table-layout: fixed; + overflow: hidden; +} + +#body-personal-space td { + width: 80px !important; + padding-bottom: 1%; + padding-top: 1%; + height: fit-content; + text-align: center; + overflow: hidden; +} + +tbody p { + text-align: center; +} diff --git a/front/style/home/side_menu.css b/front/style/home/side_menu.css new file mode 100644 index 0000000..3a23947 --- /dev/null +++ b/front/style/home/side_menu.css @@ -0,0 +1,53 @@ +@import url(../theme/dark.css); + +#side-menu { + background-color: var(--third-color); + display: flex; + flex-direction: column; + align-items: center; + overflow: hidden; +} + +#side-menu h2 { + display: inline-block; + margin-right: 5%; +} + +#side-menu-content { + width: 90%; +} +.titre-side-menu { + border-bottom: var(--main-color) solid 3px; + width: 100%; + margin-bottom: 3%; +} + +#side-menu .title { + font-size: 12px; + font-weight: bold; + color: var(--main-contrast-color); + letter-spacing: 1px; + text-transform: uppercase; + background-color: var(--main-color); + padding: 3%; + margin-bottom: 0px; + margin-right: 3%; +} + +.new { + border-radius: 100%; +} + +.button-side-menu { + /* border : black solid 1px; */ + border-radius: 0.5cap; + width: fit-content; + padding: 2%; + margin-top: 3%; + overflow: hidden; +} + +.button-side-menu:hover { + /* background-color: #c9d1e0; */ + cursor: pointer; +} diff --git a/front/style/template/header.css b/front/style/template/header.css new file mode 100644 index 0000000..2ea8d2f --- /dev/null +++ b/front/style/template/header.css @@ -0,0 +1,65 @@ +#header { + text-align: center; + background-color: var(--main-color); + margin: 0px; + /* border : var(--accent-color) 1px solid; */ + display: flex; + flex-direction: row; + font-family: var(--font-title); + /* border-radius: 0.75cap; */ +} + +#img-account { + width: 100%; + cursor: pointer; +} + +#header-right, +#header-left { + width: 10%; + /* border: yellow 2px solid; */ +} + +#header-right { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +#username { + color: var(--main-contrast-color); + margin: 0; +} + +#clickable-header-right:hover #username { + color: var(--accent-color); +} + +#header-center { + width: 80%; +} + +#clickable-header-right { + width: 40%; + border-radius: 1cap; + padding: 2%; +} + +#clickable-header-right:hover { + border: orange 1px solid; +} + +.clickable { + cursor: pointer; +} + +#img-account { + width: 100%; +} + +#iqball { + color: var(--accent-color); + font-weight: bold; + font-size: 45px; +} diff --git a/front/style/theme/dark.css b/front/style/theme/dark.css new file mode 100644 index 0000000..bdd4824 --- /dev/null +++ b/front/style/theme/dark.css @@ -0,0 +1,9 @@ +:root { + --main-color: #191a21; + --second-color: #282a36; + --third-color: #303341; + --accent-color: #ffa238; + --main-contrast-color: #e6edf3; + --font-title: Helvetica; + --font-content: Helvetica; +} diff --git a/front/views/Home.tsx b/front/views/Home.tsx new file mode 100644 index 0000000..44803a3 --- /dev/null +++ b/front/views/Home.tsx @@ -0,0 +1,267 @@ +import "../style/home/home.css" + +// import AccountSvg from "../assets/account.svg?react" +import { CSSProperties, useRef } from "react" +import { Header } from "./template/Header" + +interface Tactic { + id: number + name: string + creation_date: string +} + +interface Team { + id: number + name: string + picture: string + main_color: string + second_color: string +} + +export default function Home({ + lastTactics, + allTactics, + teams, + username, +}: { + lastTactics: Tactic[] + allTactics: Tactic[] + teams: Team[] + username: string +}) { + return ( +
+
+ +
+ ) +} + +function Body({ + lastTactics, + allTactics, + teams, +}: { + lastTactics: Tactic[] + allTactics: Tactic[] + teams: Team[] +}) { + const widthPersonalSpace = 78 + const widthSideMenu = 100 - widthPersonalSpace + return ( +
+ + +
+ ) +} + +function SideMenu({ + width, + lastTactics, + teams, +}: { + width: number + lastTactics: Tactic[] + teams: Team[] +}) { + return ( +
+
+ + +
+
+ ) +} + +function PersonalSpace({ + width, + allTactics, +}: { + width: number + allTactics: Tactic[] +}) { + return ( +
+ + +
+ ) +} + +function TitlePersonalSpace() { + return ( +
+

Espace Personnel

+
+ ) +} + +function TableData({ allTactics }: { allTactics: Tactic[] }) { + const nbRow = Math.floor(allTactics.length / 3) + 1 + let listTactic = Array(nbRow) + for (let i = 0; i < nbRow; i++) { + listTactic[i] = Array(0) + } + let i = 0 + let j = 0 + allTactics.forEach((tactic) => { + listTactic[i].push(tactic) + j++ + if (j === 3) { + i++ + j = 0 + } + }) + + i = 0 + while (i < nbRow) { + listTactic[i] = listTactic[i].map((tactic: Tactic) => ( + { + location.pathname = "/tactic/" + tactic.id + "/edit" + }}> + {truncateString(tactic.name, 25)} + + )) + i++ + } + if (nbRow == 1) { + if (listTactic[0].length < 3) { + for (let i = 0; i <= 3 - listTactic[0].length; i++) { + listTactic[0].push() + } + } + } + + const data = listTactic.map((tactic, rowIndex) => ( + {tactic} + )) + return data +} + +function BodyPersonalSpace({ allTactics }: { allTactics: Tactic[] }) { + let data + if (allTactics.length == 0) { + data =

Aucune tactique créé !

+ } else { + data = + } + + return ( +
+ + {data} +
+
+ ) +} + +function Team({ teams }: { teams: Team[] }) { + const listTeam = teams.map((team, rowIndex) => ( +
  • + {team.name} + +
  • + )) + return ( +
    +
    +

    Mes équipes

    + +
    + +
    + ) +} + +function Tactic({ lastTactics }: { lastTactics: Tactic[] }) { + return ( +
    +
    +

    Mes dernières stratégies

    + +
    + +
    + ) +} + +function SetButtonTactic({ tactics }: { tactics: Tactic[] }) { + const lastTactics = tactics.map((tactic) => ( + + )) + return
    {lastTactics}
    +} + +function SetButtonTeam({ teams }: { teams: Team[] }) { + const listTeam = teams.map((teams) => ) + return
    {listTeam}
    +} + +function ButtonTeam({ team }: { team: Team }) { + const name = truncateString(team.name, 20) + return ( +
    +
    { + location.pathname = "/team/" + team.id + }}> + {name} +
    +
    + ) +} + +function ButtonLastTactic({ tactic }: { tactic: Tactic }) { + const name = truncateString(tactic.name, 20) + return ( +
    { + location.pathname = "/tactic/" + tactic.id + "/edit" + }}> + {name} +
    + ) +} + +function truncateString(name: string, limit: number): string { + if (name.length > limit) { + name = name.substring(0, limit) + "..." + } + return name +} diff --git a/front/views/TeamPanel.tsx b/front/views/TeamPanel.tsx index 6d873c8..709d7f2 100644 --- a/front/views/TeamPanel.tsx +++ b/front/views/TeamPanel.tsx @@ -1,6 +1,7 @@ import "../style/team_panel.css" import { BASE } from "../Constants" -import { Team, TeamInfo, User, Member } from "../model/team/Team" +import { Team, TeamInfo, Member } from "../model/Team" +import { User } from "../model/User" export default function TeamPanel({ isCoach, diff --git a/front/views/template/Header.tsx b/front/views/template/Header.tsx new file mode 100644 index 0000000..7129153 --- /dev/null +++ b/front/views/template/Header.tsx @@ -0,0 +1,36 @@ +/** + * + * @param param0 username + * @returns Header + */ +export function Header({ username }: { username: string }) { + return ( + + ) +} diff --git a/public/account.svg b/public/account.svg new file mode 100644 index 0000000..70d7391 --- /dev/null +++ b/public/account.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App/Controller/UserController.php b/src/App/Controller/UserController.php index 5ce1318..e33ee61 100644 --- a/src/App/Controller/UserController.php +++ b/src/App/Controller/UserController.php @@ -7,15 +7,19 @@ use IQBall\App\Session\SessionHandle; use IQBall\App\ViewHttpResponse; use IQBall\Core\Http\HttpResponse; use IQBall\Core\Model\TacticModel; +use IQBall\Core\Model\TeamModel; class UserController { private TacticModel $tactics; + private ?TeamModel $teams; + /** * @param TacticModel $tactics */ - public function __construct(TacticModel $tactics) { + public function __construct(TacticModel $tactics, ?TeamModel $teams = null) { $this->tactics = $tactics; + $this->teams = $teams; } /** @@ -23,16 +27,34 @@ class UserController { * @return ViewHttpResponse the home page view */ public function home(SessionHandle $session): ViewHttpResponse { - //TODO use session's account to get the last 5 tactics of the logged-in account - $listTactic = $this->tactics->getLast(5); - return ViewHttpResponse::twig("home.twig", ["recentTactic" => $listTactic]); + $limitNbTactics = 5; + $lastTactics = $this->tactics->getLast($limitNbTactics, $session->getAccount()->getId()); + $allTactics = $this->tactics->getAll($session->getAccount()->getId()); + $name = $session->getAccount()->getName(); + + if ($this->teams != null) { + $teams = $this->teams->getAll($session->getAccount()->getId()); + } else { + $teams = []; + } + + return ViewHttpResponse::react("views/Home.tsx", [ + "lastTactics" => $lastTactics, + "allTactics" => $allTactics, + "teams" => $teams, + "username" => $name, + ]); + } + + public function homeTwig(SessionHandle $session): ViewHttpResponse { + return ViewHttpResponse::twig("home.twig", []); } /** * @return ViewHttpResponse account settings page */ public function settings(SessionHandle $session): ViewHttpResponse { - return ViewHttpResponse::twig("account_settings.twig", []); + return ViewHttpResponse::react("views/Settings.tsx", []); } public function disconnect(MutableSessionHandle $session): HttpResponse { diff --git a/src/Core/Gateway/TacticInfoGateway.php b/src/Core/Gateway/TacticInfoGateway.php index 67cffc4..08302c9 100644 --- a/src/Core/Gateway/TacticInfoGateway.php +++ b/src/Core/Gateway/TacticInfoGateway.php @@ -45,13 +45,40 @@ class TacticInfoGateway { * @param integer $nb * @return array> */ - public function getLast(int $nb): ?array { + public function getLast(int $nb, int $ownerId): ?array { $res = $this->con->fetch( - "SELECT * FROM Tactic ORDER BY creation_date DESC LIMIT :nb ", - [":nb" => [$nb, PDO::PARAM_INT]] + "SELECT * + FROM Tactic + WHERE owner = :ownerId + ORDER BY creation_date DESC + LIMIT :nb", + [ + ":ownerId" => [$ownerId, PDO::PARAM_INT],":nb" => [$nb, PDO::PARAM_INT], + ] ); if (count($res) == 0) { - return null; + return []; + } + return $res; + } + + /** + * Get all the tactics of the owner + * + * @return array> + */ + public function getAll(int $ownerId): ?array { + $res = $this->con->fetch( + "SELECT * + FROM Tactic + WHERE owner = :ownerId + ORDER BY name DESC", + [ + ":ownerId" => [$ownerId, PDO::PARAM_INT], + ] + ); + if (count($res) == 0) { + return []; } return $res; } diff --git a/src/Core/Gateway/TeamGateway.php b/src/Core/Gateway/TeamGateway.php index 71df931..fe0b2bb 100644 --- a/src/Core/Gateway/TeamGateway.php +++ b/src/Core/Gateway/TeamGateway.php @@ -105,7 +105,8 @@ class TeamGateway { * @param string $newSecondColor * @return void */ - public function editTeam(int $idTeam, string $newName, string $newPicture, string $newMainColor, string $newSecondColor) { + public function editTeam(int $idTeam, string $newName, string $newPicture, string $newMainColor, string $newSecondColor) + { $this->con->exec( "UPDATE team SET name = :newName, @@ -121,6 +122,15 @@ class TeamGateway { "newSecondColor" => [$newSecondColor, PDO::PARAM_STR], ] ); + + } + /** + * Get all the user's teams + * @param integer $user + * @return array> + */ + public function getAll(int $user): array { + return $this->con->fetch("SELECT * FROM Team", []); } } diff --git a/src/Core/Model/TacticModel.php b/src/Core/Model/TacticModel.php index 51e5eb8..7057e7f 100644 --- a/src/Core/Model/TacticModel.php +++ b/src/Core/Model/TacticModel.php @@ -3,6 +3,7 @@ namespace IQBall\Core\Model; use IQBall\Core\Data\CourtType; +use IQBall\App\Session\SessionHandle; use IQBall\Core\Data\TacticInfo; use IQBall\Core\Gateway\TacticInfoGateway; use IQBall\Core\Validation\ValidationFail; @@ -57,10 +58,27 @@ class TacticModel { * @param integer $nb * @return array> */ - public function getLast(int $nb): ?array { - return $this->tactics->getLast($nb); + + /** + * Return the nb last tactics + * + * @param integer $nb + * @param integer $ownerId + * @return array> + */ + public function getLast(int $nb, int $ownerId): array { + return $this->tactics->getLast($nb, $ownerId); } + /** + * Get all the tactics of the owner + * + * @param integer $ownerId + * @return array> + */ + public function getAll(int $ownerId): ?array { + return $this->tactics->getAll($ownerId); + } /** * Update the name of a tactic * @param int $id the tactic identifier diff --git a/src/Core/Model/TeamModel.php b/src/Core/Model/TeamModel.php index 9f03953..d6c97ce 100644 --- a/src/Core/Model/TeamModel.php +++ b/src/Core/Model/TeamModel.php @@ -126,8 +126,18 @@ class TeamModel { * @param string $newSecondColor * @return void */ - public function editTeam(int $idTeam, string $newName, string $newPicture, string $newMainColor, string $newSecondColor) { + public function editTeam(int $idTeam, string $newName, string $newPicture, string $newMainColor, string $newSecondColor) + { $this->teams->editTeam($idTeam, $newName, $newPicture, $newMainColor, $newSecondColor); } + /** + * Get all user's teams + * + * @param integer $user + * @return array> + */ + public function getAll(int $user): array { + return $this->teams->getAll($user); + } }