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;
height: 100%;
background-color: var(--second-color);
overflow: hidden;
}
.data {
@ -29,6 +30,7 @@ body {
background-color: var(--main-color);
border-radius: 0.75cap;
color: var(--main-contrast-color);
position: relative;
}
.data:hover {

@ -31,8 +31,31 @@
padding-top: 1%;
height: fit-content;
text-align: center;
overflow: hidden;
}
tbody p {
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 {
width: 90%;
}
.titre-side-menu {
border-bottom: var(--main-color) solid 3px;
width: 100%;

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

@ -5,9 +5,10 @@ import { BASE } from "../Constants"
import Draggable from "react-draggable";
import {NULL_POS} from "../components/arrows/Pos";
import {contains} from "../components/arrows/Box";
import {useRef, useState} from "react";
import React, {useRef, useState} from "react";
import { fetchAPI } from "../Fetcher"
import {User} from "../model/User";
import { FaShare } from "react-icons/fa";
import {SaveStates} from "../components/editor/SavingState";
interface Tactic {
@ -83,7 +84,7 @@ function SideMenu({
teams: Team[]
}) {
return (
<div id="sideMenu" style={{
<div id="side-menu" style={{
width : width + "%",
}}>
<div id="side-menu-content">
@ -153,7 +154,7 @@ function TableData({
i = 0
while (i < nbRow) {
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++
}
@ -175,20 +176,32 @@ function TableData({
function DraggableTableDataElement({
tactic,
teams,
user,
} : {
tactic : Tactic
teams : Team[]
user : User
}) {
const ref = useRef<HTMLDivElement>(null)
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 (
<Draggable position={NULL_POS}
nodeRef={ref}
onDrag={() => setDragging(true)}
onStop={() => { if(dragging) {
onDropTactic(ref.current.getBoundingClientRect(), tactic, teams, user)
onDropTactic(ref.current.getBoundingClientRect(), tactic, teams)
}
}}
>
@ -202,8 +215,18 @@ function DraggableTableDataElement({
setDragging(false)
}
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
{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>
</Draggable>
)
@ -316,22 +339,35 @@ function truncateString(name: string, limit: number): string {
return name
}
function onDropTactic(ref : DOMRect, tactic : Tactic, teams : Team[], user : User) {
function onDropTactic(ref : DOMRect, tactic : Tactic, teams : Team[]) {
let shared = false;
for (const team of teams) {
if (contains(ref, document.getElementById(`button-team-${team.id}`)!.getBoundingClientRect())) {
if (!shared) {
shareTacticToTeam(tactic, team, user);
shareTacticToTeam(tactic, team);
shared = true;
}
}
}
}
async function shareTacticToTeam(tactic : Tactic, team : Team, user : User) {
const canShare = await fetchAPI(`tactic/${tactic.id}/can-share-to-team`, team).then((r) => r.ok);
async function onShareTactic(email: string, tactic: Tactic) {
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)) {
fetchAPI(`tactic/${tactic.id}/share`, team)
fetchAPI(`tactic/${tactic.id}/share-to-team`, team)
}
if(!canShare) {
alert("Vous ne pouvez pas partager cette tactique à cette équipe")

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

@ -13,6 +13,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.6",
"react-icons": "^5.0.1",
"typescript": "^5.2.2",
"vite": "^4.5.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-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;
}

@ -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)) {
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([
"id" => [],
"name" => [],
@ -88,12 +89,12 @@ class APITacticController {
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([
"id" => [],
"name" => [],
@ -103,11 +104,19 @@ class APITacticController {
], function (HttpRequest $request) use ($tacticId, $account) {
$this->teamModel->shareTacticToTeam($request["id"], $tacticId);
return HttpResponse::fromCode(HttpCodes::OK);
});
}
);
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 unshareTactic(int $tacticId, Account $account) {
public function unshareTacticToTeam(int $tacticId, Account $account): HttpResponse {
return Control::runChecked([
"id" => [],
"name" => [],
@ -119,9 +128,8 @@ class APITacticController {
$this->teamModel->unshareTacticToTeam($tacticId, $request["id"]);
return HttpResponse::fromCode(HttpCodes::OK);
}
return HttpResponse::fromCode(HttpCodes::BAD_REQUEST);
}
);
return new JsonHttpResponse(["message" => "Action non autorisée"], HttpCodes::FORBIDDEN);
});
}
}

Loading…
Cancel
Save