push all my shareTactic work since 2 weeks, (can share and unshare tactic to team, draggable element for share, share icon on tactic hover to share to an account, just need to fix api tomorrow)
continuous-integration/drone/push Build is failing Details

shareTactic
Vivien DUFOUR 1 year ago
parent e962c17f4d
commit bd244c8dde

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

@ -22,6 +22,7 @@ body {
margin: 0px; margin: 0px;
height: 100%; height: 100%;
background-color: var(--second-color); background-color: var(--second-color);
overflow: hidden;
} }
.data { .data {
@ -29,6 +30,7 @@ body {
background-color: var(--main-color); background-color: var(--main-color);
border-radius: 0.75cap; border-radius: 0.75cap;
color: var(--main-contrast-color); color: var(--main-contrast-color);
position: relative;
} }
.data:hover { .data:hover {

@ -31,8 +31,31 @@
padding-top: 1%; padding-top: 1%;
height: fit-content; height: fit-content;
text-align: center; text-align: center;
overflow: hidden;
} }
tbody p { tbody p {
text-align: center; text-align: center;
} }
.share-icon-container {
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
padding: 5px;
}
.share-button {
cursor: pointer;
}
.share-icon-button {
background-color: transparent;
border: none;
}
.share-icon {
color: white;
font-size: 2em;
}

@ -16,6 +16,7 @@
#side-menu-content { #side-menu-content {
width: 90%; width: 90%;
} }
.titre-side-menu { .titre-side-menu {
border-bottom: var(--main-color) solid 3px; border-bottom: var(--main-color) solid 3px;
width: 100%; width: 100%;

@ -30,6 +30,7 @@ body {
align-items: center; align-items: center;
width: 68%; width: 68%;
margin-right: 2%; margin-right: 2%;
margin-bottom: 15px;
} }
#right-panel { #right-panel {
@ -37,6 +38,7 @@ body {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
width: 30%; width: 30%;
flex-grow: 1;
} }
header { header {
@ -55,6 +57,10 @@ header h1 a {
font-size: 1.4em; font-size: 1.4em;
} }
html, body, #main-div, #content-container, #right-panel, #tactics {
height: 100%;
}
.square { .square {
width: 50px; width: 50px;
height: 50px; height: 50px;
@ -181,6 +187,8 @@ header h1 a {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 10px; border-radius: 10px;
overflow: auto;
margin-bottom: 5px;
} }
#head-tactics { #head-tactics {

@ -5,9 +5,10 @@ import { BASE } from "../Constants"
import Draggable from "react-draggable"; import Draggable from "react-draggable";
import {NULL_POS} from "../components/arrows/Pos"; import {NULL_POS} from "../components/arrows/Pos";
import {contains} from "../components/arrows/Box"; import {contains} from "../components/arrows/Box";
import {useRef, useState} from "react"; import React, {useRef, useState} from "react";
import { fetchAPI } from "../Fetcher" import { fetchAPI } from "../Fetcher"
import {User} from "../model/User"; import {User} from "../model/User";
import { FaShare } from "react-icons/fa";
import {SaveStates} from "../components/editor/SavingState"; import {SaveStates} from "../components/editor/SavingState";
interface Tactic { interface Tactic {
@ -83,7 +84,7 @@ function SideMenu({
teams: Team[] teams: Team[]
}) { }) {
return ( return (
<div id="sideMenu" style={{ <div id="side-menu" style={{
width : width + "%", width : width + "%",
}}> }}>
<div id="side-menu-content"> <div id="side-menu-content">
@ -153,7 +154,7 @@ function TableData({
i = 0 i = 0
while (i < nbRow) { while (i < nbRow) {
listTactic[i] = listTactic[i].map((tactic: Tactic, i) => ( listTactic[i] = listTactic[i].map((tactic: Tactic, i) => (
<DraggableTableDataElement key={i} tactic={tactic} teams={teams} user={user}/> <DraggableTableDataElement key={i} tactic={tactic} teams={teams}/>
)) ))
i++ i++
} }
@ -175,23 +176,35 @@ function TableData({
function DraggableTableDataElement({ function DraggableTableDataElement({
tactic, tactic,
teams, teams,
user,
} : { } : {
tactic : Tactic tactic : Tactic
teams : Team[] teams : Team[]
user : User
}) { }) {
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)
const [dragging, setDragging] = useState(false) const [dragging, setDragging] = useState(false)
const [hovered, setHovered] = useState(false);
const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
if (!dragging) {
const userEmail = window.prompt("Entrez l'email à qui partager la tactique :");
if(userEmail != null) {
onShareTactic(userEmail, tactic);
}
} else {
setDragging(false);
}
};
return ( return (
<Draggable position={NULL_POS} <Draggable position={NULL_POS}
nodeRef={ref} nodeRef={ref}
onDrag={() => setDragging(true)} onDrag={() => setDragging(true)}
onStop={() => { if(dragging) { onStop={() => { if(dragging) {
onDropTactic(ref.current.getBoundingClientRect(), tactic, teams, user) onDropTactic(ref.current.getBoundingClientRect(), tactic, teams)
} }
}} }}
> >
<td key={tactic.id} <td key={tactic.id}
ref={ref} ref={ref}
className="data" className="data"
@ -202,8 +215,18 @@ function DraggableTableDataElement({
setDragging(false) setDragging(false)
} }
}} }}
> onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
{truncateString(tactic.name, 25)} {truncateString(tactic.name, 25)}
{hovered && (
<div className="share-icon-container">
<button className="share-button share-icon-button" onClick={handleButtonClick}>
<FaShare className="share-icon" />
</button>
</div>
)}
</td> </td>
</Draggable> </Draggable>
) )
@ -316,22 +339,35 @@ function truncateString(name: string, limit: number): string {
return name return name
} }
function onDropTactic(ref : DOMRect, tactic : Tactic, teams : Team[], user : User) { function onDropTactic(ref : DOMRect, tactic : Tactic, teams : Team[]) {
let shared = false; let shared = false;
for (const team of teams) { for (const team of teams) {
if (contains(ref, document.getElementById(`button-team-${team.id}`)!.getBoundingClientRect())) { if (contains(ref, document.getElementById(`button-team-${team.id}`)!.getBoundingClientRect())) {
if (!shared) { if (!shared) {
shareTacticToTeam(tactic, team, user); shareTacticToTeam(tactic, team);
shared = true; shared = true;
} }
} }
} }
} }
async function shareTacticToTeam(tactic : Tactic, team : Team, user : User) { async function onShareTactic(email: string, tactic: Tactic) {
const canShare = await fetchAPI(`tactic/${tactic.id}/can-share-to-team`, team).then((r) => r.ok); const canShareResponse = await fetchAPI(`tactic/${tactic.id}/can-share`, tactic);
if (canShareResponse.ok) {
const shareToAccountResponse = await fetchAPI(`tactic/${tactic.id}/share-to-account`, email);
if (!shareToAccountResponse.ok) {
alert("Une erreur s'est produite lors du partage de la tactique avec ce compte");
}
} else {
alert("Vous ne pouvez pas partager cette tactique");
}
}
async function shareTacticToTeam(tactic : Tactic, team : Team) {
const canShare = await fetchAPI(`tactic/${tactic.id}/can-share-to-team`, team).then((r) => r.ok)
if(canShare && confirm("Etes-vous sûr de vouloir partager la tactique " + tactic.name + " avec l'équipe " + team.name)) { if(canShare && confirm("Etes-vous sûr de vouloir partager la tactique " + tactic.name + " avec l'équipe " + team.name)) {
fetchAPI(`tactic/${tactic.id}/share`, team) fetchAPI(`tactic/${tactic.id}/share-to-team`, team)
} }
if(!canShare) { if(!canShare) {
alert("Vous ne pouvez pas partager cette tactique à cette équipe") alert("Vous ne pouvez pas partager cette tactique à cette équipe")

@ -19,7 +19,7 @@ export default function TeamPanel({
const [teamTactics, setTeamTactics] = useState(tactics); const [teamTactics, setTeamTactics] = useState(tactics);
function handleTacticDelete(tacticId) { function handleTacticDelete(tacticId) {
fetchAPI(`tactic/${tacticId}/unshare`, team.info) fetchAPI(`tactic/${tacticId}/unshare-to-team`, team.info)
const updatedTactics = teamTactics.filter((tactic) => tactic.id !== tacticId); const updatedTactics = teamTactics.filter((tactic) => tactic.id !== tacticId);
setTeamTactics(updatedTactics); setTeamTactics(updatedTactics);
} }
@ -114,6 +114,7 @@ function MembersDisplay({
}) { }) {
const listMember = members.map((member) => ( const listMember = members.map((member) => (
<MemberDisplay <MemberDisplay
key={member.user.id}
member={member} member={member}
isCoach={isCoach} isCoach={isCoach}
idTeam={idTeam} idTeam={idTeam}
@ -220,16 +221,15 @@ function TacticDisplay({
onTacticDelete: () => void onTacticDelete: () => void
}) { }) {
return ( return (
<div className="tactic"> <div className="tactic" onClick={() => location.pathname = BASE + "/tactic/" + tactic.id + "/edit"}>
<p>{tactic.name}</p> <p>{tactic.name}</p>
{isCoach && ( {isCoach && (
<button <button
id="delete" id="delete"
onClick={() => onClick={(event) => {
confirm("Êtes-vous sûr de vouloir supprimer cette tactique de cette équipe ?") event.stopPropagation();
? onTacticDelete() confirm("Êtes-vous sûr de vouloir supprimer le partage cette tactique ?") && onTacticDelete();
: {} }}
}
> >
Retirer Retirer
</button> </button>

@ -13,6 +13,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-draggable": "^4.4.6", "react-draggable": "^4.4.6",
"react-icons": "^5.0.1",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vite": "^4.5.0", "vite": "^4.5.0",
"vite-plugin-css-injected-by-js": "^3.3.0" "vite-plugin-css-injected-by-js": "^3.3.0"

@ -39,9 +39,11 @@ function getRoutes(): AltoRouter {
$router->map("POST", "/tactic/[i:id]/can-share", Action::auth(fn(int $id, Account $acc) => getTacticController()->canShareTactic($id, $acc))); $router->map("POST", "/tactic/[i:id]/can-share", Action::auth(fn(int $id, Account $acc) => getTacticController()->canShareTactic($id, $acc)));
$router->map("POST", "/tactic/[i:id]/can-share-to-team", Action::auth(fn(int $id, Account $acc) => getTacticController()->canShareTacticToTeam($id, $acc))); $router->map("POST", "/tactic/[i:id]/can-share-to-team", Action::auth(fn(int $id, Account $acc) => getTacticController()->canShareTacticToTeam($id, $acc)));
$router->map("POST", "/tactic/[i:id]/share", Action::auth(fn(int $id, Account $acc) => getTacticController()->shareTacticToTeam($id, $acc))); $router->map("POST", "/tactic/[i:id]/share-to-team", Action::auth(fn(int $id, Account $acc) => getTacticController()->shareTacticToTeam($id, $acc)));
$router->map("POST", "/tactic/[i:id]/share-to-account", Action::auth(fn(int $id, Account $acc) => getTacticController()->shareTacticToAccount($id, $acc)));
$router->map("POST", "/tactic/[i:id]/unshare", Action::auth(fn(int $id, Account $acc) => getTacticController()->unshareTactic($id, $acc)));
$router->map("POST", "/tactic/[i:id]/unshare-to-team", Action::auth(fn(int $id, Account $acc) => getTacticController()->unshareTacticToTeam($id, $acc)));
return $router; return $router;
} }

@ -68,14 +68,15 @@ class APITacticController {
} }
public function canShareTactic(int $tacticId, Account $account) : bool { public function canShareTactic(int $tacticId, Account $account): HttpResponse {
if ($this->tacticModel->canShareTactic($tacticId, $account)) { if ($this->tacticModel->canShareTactic($tacticId, $account)) {
return true; return HttpResponse::fromCode(HttpCodes::OK);
} }
return false; return new JsonHttpResponse(["message" => "Vous ne pouvez pas partager cette tactique"], HttpCodes::FORBIDDEN);
} }
public function canShareTacticToTeam(int $tacticId, Account $account) {
public function canShareTacticToTeam(int $tacticId, Account $account): HttpResponse {
return Control::runChecked([ return Control::runChecked([
"id" => [], "id" => [],
"name" => [], "name" => [],
@ -83,17 +84,17 @@ class APITacticController {
"main_color" => [], "main_color" => [],
"second_color" => [] "second_color" => []
], function (HttpRequest $request) use ($tacticId, $account) { ], function (HttpRequest $request) use ($tacticId, $account) {
if($this->canShareTactic($tacticId, $account)) { if ($this->canShareTactic($tacticId, $account)) {
if($this->teamModel->canShareTacticToTeam($request["id"], $account->getUser()->getEmail())) { if ($this->teamModel->canShareTacticToTeam($request["id"], $account->getUser()->getEmail())) {
return HttpResponse::fromCode(HttpCodes::OK); return HttpResponse::fromCode(HttpCodes::OK);
} }
} }
return HttpResponse::fromCode(HttpCodes::BAD_REQUEST); return new JsonHttpResponse(["message" => "Action non autorisée"], HttpCodes::FORBIDDEN);
} });
);
} }
public function shareTacticToTeam(int $tacticId, Account $account) {
public function shareTacticToTeam(int $tacticId, Account $account): HttpResponse {
return Control::runChecked([ return Control::runChecked([
"id" => [], "id" => [],
"name" => [], "name" => [],
@ -103,11 +104,19 @@ class APITacticController {
], function (HttpRequest $request) use ($tacticId, $account) { ], function (HttpRequest $request) use ($tacticId, $account) {
$this->teamModel->shareTacticToTeam($request["id"], $tacticId); $this->teamModel->shareTacticToTeam($request["id"], $tacticId);
return HttpResponse::fromCode(HttpCodes::OK); return HttpResponse::fromCode(HttpCodes::OK);
} });
);
} }
public function unshareTactic(int $tacticId, Account $account) { public function shareTacticToAccount(int $tacticId, Account $account): HttpResponse {
return Control::runChecked([
"email" => [],
], function (HttpRequest $request) use ($tacticId, $account) {
$this->tacticModel->shareTacticToAccountMail($request["email"], $tacticId);
return HttpResponse::fromCode(HttpCodes::OK);
});
}
public function unshareTacticToTeam(int $tacticId, Account $account): HttpResponse {
return Control::runChecked([ return Control::runChecked([
"id" => [], "id" => [],
"name" => [], "name" => [],
@ -115,13 +124,12 @@ class APITacticController {
"mainColor" => [], "mainColor" => [],
"secondColor" => [] "secondColor" => []
], function (HttpRequest $request) use ($tacticId, $account) { ], function (HttpRequest $request) use ($tacticId, $account) {
if($this->teamModel->canShareTacticToTeam($request["id"], $account->getUser()->getEmail())) { if ($this->teamModel->canShareTacticToTeam($request["id"], $account->getUser()->getEmail())) {
$this->teamModel->unshareTacticToTeam($tacticId, $request["id"]); $this->teamModel->unshareTacticToTeam($tacticId, $request["id"]);
return HttpResponse::fromCode(HttpCodes::OK); return HttpResponse::fromCode(HttpCodes::OK);
} }
return HttpResponse::fromCode(HttpCodes::BAD_REQUEST); return new JsonHttpResponse(["message" => "Action non autorisée"], HttpCodes::FORBIDDEN);
} });
);
} }
} }

Loading…
Cancel
Save