feat: ban competition + competition list sort
This commit is contained in:
parent
2a1bdfbdcb
commit
0fc871bd46
@ -264,6 +264,12 @@ public class CompetitionService {
|
|||||||
if ("admin".equals(source))
|
if ("admin".equals(source))
|
||||||
return permService.hasEditPerm(securityCtx, id)
|
return permService.hasEditPerm(securityCtx, id)
|
||||||
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
|
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
|
||||||
|
.call(combModel -> {
|
||||||
|
if (c.getBanMembre() == null)
|
||||||
|
c.setBanMembre(new ArrayList<>());
|
||||||
|
c.getBanMembre().remove(combModel.getId());
|
||||||
|
return Panache.withTransaction(() -> repository.persist(c));
|
||||||
|
})
|
||||||
.chain(combModel -> updateRegister(data, c, combModel, true)))
|
.chain(combModel -> updateRegister(data, c, combModel, true)))
|
||||||
.chain(r -> Mutiny.fetch(r.getMembre().getLicences())
|
.chain(r -> Mutiny.fetch(r.getMembre().getLicences())
|
||||||
.map(licences -> SimpleRegisterComb.fromModel(r, licences)));
|
.map(licences -> SimpleRegisterComb.fromModel(r, licences)));
|
||||||
@ -368,9 +374,20 @@ public class CompetitionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<Void> removeRegisterComb(SecurityCtx securityCtx, Long id, Long combId, String source) {
|
public Uni<Void> removeRegisterComb(SecurityCtx securityCtx, Long id, Long combId, String source, boolean ban) {
|
||||||
if ("admin".equals(source))
|
if ("admin".equals(source))
|
||||||
return permService.hasEditPerm(securityCtx, id)
|
return permService.hasEditPerm(securityCtx, id)
|
||||||
|
.chain(cm -> {
|
||||||
|
if (cm.getBanMembre() == null)
|
||||||
|
cm.setBanMembre(new ArrayList<>());
|
||||||
|
if (ban) {
|
||||||
|
if (!cm.getBanMembre().contains(combId))
|
||||||
|
cm.getBanMembre().add(combId);
|
||||||
|
} else {
|
||||||
|
cm.getBanMembre().remove(combId);
|
||||||
|
}
|
||||||
|
return Panache.withTransaction(() -> repository.persist(cm));
|
||||||
|
})
|
||||||
.chain(c -> deleteRegister(combId, c, true));
|
.chain(c -> deleteRegister(combId, c, true));
|
||||||
if ("club".equals(source))
|
if ("club".equals(source))
|
||||||
return repository.findById(id)
|
return repository.findById(id)
|
||||||
|
|||||||
@ -59,8 +59,8 @@ public class CompetitionEndpoints {
|
|||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Operation(hidden = true)
|
@Operation(hidden = true)
|
||||||
public Uni<Void> removeRegisterComb(@PathParam("id") Long id, @PathParam("comb_id") Long combId,
|
public Uni<Void> removeRegisterComb(@PathParam("id") Long id, @PathParam("comb_id") Long combId,
|
||||||
@PathParam("source") String source) {
|
@PathParam("source") String source, @QueryParam("ban") boolean ban) {
|
||||||
return service.removeRegisterComb(securityCtx, id, combId, source);
|
return service.removeRegisterComb(securityCtx, id, combId, source, ban);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
|||||||
@ -23,11 +23,11 @@ export function CompetitionEdit() {
|
|||||||
toast.promise(
|
toast.promise(
|
||||||
apiAxios.delete(`/competition/${id}`),
|
apiAxios.delete(`/competition/${id}`),
|
||||||
{
|
{
|
||||||
pending: "Suppression de la competition en cours...",
|
pending: "Suppression de la compétition en cours...",
|
||||||
success: "Competition supprimé avec succès 🎉",
|
success: "Compétition supprimé avec succès 🎉",
|
||||||
error: {
|
error: {
|
||||||
render({data}) {
|
render({data}) {
|
||||||
return errFormater(data, "Échec de la suppression de la competition")
|
return errFormater(data, "Échec de la suppression de la compétition")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -47,18 +47,18 @@ export function CompetitionEdit() {
|
|||||||
<Content data={data} refresh={refresh}/>
|
<Content data={data} refresh={refresh}/>
|
||||||
|
|
||||||
{data.id !== null && <button style={{marginBottom: "1.5em", width: "100%"}} className="btn btn-primary"
|
{data.id !== null && <button style={{marginBottom: "1.5em", width: "100%"}} className="btn btn-primary"
|
||||||
onClick={_ => navigate(`/competition/${data.id}/register`)}>Voir/Modifier les participants</button>}
|
onClick={_ => navigate(`/competition/${data.id}/register?type=${data.registerMode}`)}>Voir/Modifier les participants</button>}
|
||||||
|
|
||||||
{data.id !== null && data.system === "SAFCA" && <ContentSAFCA data2={data}/>}
|
{data.id !== null && data.system === "SAFCA" && <ContentSAFCA data2={data}/>}
|
||||||
|
|
||||||
{data.id !== null && <>
|
{data.id !== null && <>
|
||||||
<div className="col" style={{textAlign: 'right', marginTop: '1em'}}>
|
<div className="col" style={{textAlign: 'right', marginTop: '1em'}}>
|
||||||
<button className="btn btn-danger btn-sm" data-bs-toggle="modal"
|
<button className="btn btn-danger btn-sm" data-bs-toggle="modal"
|
||||||
data-bs-target="#confirm-delete">Supprimer la competition
|
data-bs-target="#confirm-delete">Supprimer la compétition
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmDialog title="Supprimer la competition"
|
<ConfirmDialog title="Supprimer la compétition"
|
||||||
message="Êtes-vous sûr de vouloir supprimer cette competition est tout les resultat associer?"
|
message="Êtes-vous sûr de vouloir supprimer cette compétition est tout les resultat associer?"
|
||||||
onConfirm={handleRm}/>
|
onConfirm={handleRm}/>
|
||||||
</>}
|
</>}
|
||||||
</div>
|
</div>
|
||||||
@ -301,7 +301,7 @@ function Content({data}) {
|
|||||||
return <form onSubmit={handleSubmit}>
|
return <form onSubmit={handleSubmit}>
|
||||||
<div className="card mb-4">
|
<div className="card mb-4">
|
||||||
<input name="id" value={data.id || ""} readOnly hidden/>
|
<input name="id" value={data.id || ""} readOnly hidden/>
|
||||||
<div className="card-header">{data.id ? "Edition competition" : "Création competition"}</div>
|
<div className="card-header">{data.id ? "Edition compétition" : "Création compétition"}</div>
|
||||||
<div className="card-body text-center">
|
<div className="card-body text-center">
|
||||||
|
|
||||||
<div className="accordion" id="accordionExample">
|
<div className="accordion" id="accordionExample">
|
||||||
@ -330,7 +330,7 @@ function Content({data}) {
|
|||||||
<h2 className="accordion-header">
|
<h2 className="accordion-header">
|
||||||
<button className="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo"
|
<button className="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo"
|
||||||
aria-expanded="true" aria-controls="collapseTwo">
|
aria-expanded="true" aria-controls="collapseTwo">
|
||||||
Informations générales sur la competition
|
Informations générales sur la compétition
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div id="collapseTwo" className="accordion-collapse collapse show" data-bs-parent="#accordionExample">
|
<div id="collapseTwo" className="accordion-collapse collapse show" data-bs-parent="#accordionExample">
|
||||||
|
|||||||
@ -31,12 +31,23 @@ function MakeCentralPanel({data, navigate}) {
|
|||||||
|
|
||||||
return <>
|
return <>
|
||||||
{userinfo?.roles?.includes("create_compet") &&
|
{userinfo?.roles?.includes("create_compet") &&
|
||||||
<div className="col mb-2" style={{textAlign: 'right', marginTop: '1em'}}>
|
<div className="col mb-2" style={{textAlign: 'right', marginTop: '1em'}}>
|
||||||
<button type="button" className="btn btn-primary" onClick={() => navigate("/competition/0")}>Nouvelle competition</button>
|
<button type="button" className="btn btn-primary" onClick={() => navigate("/competition/0")}>Nouvelle compétition</button>
|
||||||
</div> }
|
</div>}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
|
<h3>Compétition future</h3>
|
||||||
<div className="list-group">
|
<div className="list-group">
|
||||||
{data.map(req => (<MakeRow key={req.id} data={req} navigate={navigate}/>))}
|
{data.filter(req => new Date(req.toDate.split('T')[0]) >= new Date()).sort((a, b) => {
|
||||||
|
return new Date(a.date.split('T')[0]) - new Date(b.date.split(')T')[0])
|
||||||
|
}).map(req => (<MakeRow key={req.id} data={req} navigate={navigate}/>))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mb-4">
|
||||||
|
<h3>Compétition passée</h3>
|
||||||
|
<div className="list-group">
|
||||||
|
{data.filter(req => new Date(req.toDate.split('T')[0]) < new Date()).sort((a, b) => {
|
||||||
|
return new Date(b.date.split('T')[0]) - new Date(a.date.split(')T')[0])
|
||||||
|
}).map(req => (<MakeRow key={req.id} data={req} navigate={navigate}/>))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {useNavigate, useParams} from "react-router-dom";
|
import {useNavigate, useParams, useSearchParams} from "react-router-dom";
|
||||||
import {LoadingProvider, useLoadingSwitcher} from "../../hooks/useLoading.jsx";
|
import {LoadingProvider, useLoadingSwitcher} from "../../hooks/useLoading.jsx";
|
||||||
import {useFetch} from "../../hooks/useFetch.js";
|
import {useFetch} from "../../hooks/useFetch.js";
|
||||||
import {AxiosError} from "../../components/AxiosError.jsx";
|
import {AxiosError} from "../../components/AxiosError.jsx";
|
||||||
@ -8,7 +8,7 @@ import {apiAxios} from "../../utils/Tools.js";
|
|||||||
import {toast} from "react-toastify";
|
import {toast} from "react-toastify";
|
||||||
import {SimpleReducer} from "../../utils/SimpleReducer.jsx";
|
import {SimpleReducer} from "../../utils/SimpleReducer.jsx";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faAdd, faTrashCan} from "@fortawesome/free-solid-svg-icons";
|
import {faAdd, faGavel, faTrashCan} from "@fortawesome/free-solid-svg-icons";
|
||||||
import "./CompetitionRegisterAdmin.css"
|
import "./CompetitionRegisterAdmin.css"
|
||||||
|
|
||||||
export function CompetitionRegisterAdmin({source}) {
|
export function CompetitionRegisterAdmin({source}) {
|
||||||
@ -407,7 +407,15 @@ function FiltreBar({data, clubFilter, setClubFilter, catFilter, setCatFilter, so
|
|||||||
}
|
}
|
||||||
|
|
||||||
function MakeCentralPanel({data, dispatch, id, setModalState, source}) {
|
function MakeCentralPanel({data, dispatch, id, setModalState, source}) {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const registerType = searchParams.get("type") || "FREE";
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
|
{(registerType === "FREE" || registerType === "CLUB_ADMIN") && source === "admin" &&
|
||||||
|
<span>Tips 1: Il est possible de bannir un combattant, ce qui l'empêchera d'être réinscrit par un autre moyen que par un administrateur de cette compétition.
|
||||||
|
Pour cela, cliquez sur la petite <FontAwesomeIcon icon={faGavel}/> à côté de son nom.<br/>
|
||||||
|
Tips 2: Il est aussi possible de verrouiller les modifications de son inscription depuis sa fiche, ce qui l'empêchera d'être modifié/supprimé par lui-même et/ou un responsable de club.
|
||||||
|
</span>}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<div className="list-group">
|
<div className="list-group">
|
||||||
{data.map((req, index) => (<div key={index} className="list-group-item" style={{padding: "0"}}>
|
{data.map((req, index) => (<div key={index} className="list-group-item" style={{padding: "0"}}>
|
||||||
@ -432,13 +440,42 @@ function MakeCentralPanel({data, dispatch, id, setModalState, source}) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-auto" style={{padding: "0 0.5rem 0 0", alignContent: "center"}}>
|
<div className="col-auto" style={{padding: "0 0.5rem 0 0", alignContent: "center"}}>
|
||||||
|
{(registerType === "FREE" || registerType === "CLUB_ADMIN") && source === "admin" &&
|
||||||
|
<button className="btn btn btn-danger no-modal" type="button" disabled={req.data.lockEdit && source !== "admin"}
|
||||||
|
style={{margin: "0 0.25rem 0 0"}}
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
if (req.data.lockEdit && source !== "admin") return;
|
||||||
|
if (!window.confirm("Êtes-vous sûr de vouloir désinscrire et bannir ce combattant de la compétition?\n(Vous pouvez le réinscrire plus tard)"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
toast.promise(apiAxios.delete(`/competition/${id}/register/${req.data.id}/${source}?ban=true`), {
|
||||||
|
pending: "Désinscription en cours", success: "Combattant désinscrit et bannie", error: {
|
||||||
|
render({data}) {
|
||||||
|
return data.response.data || "Erreur"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
dispatch({type: 'REMOVE', payload: req.id})
|
||||||
|
})
|
||||||
|
}}>
|
||||||
|
<FontAwesomeIcon icon={faGavel} className="no-modal"/>
|
||||||
|
</button>}
|
||||||
<button className="btn btn-danger no-modal" type="button" disabled={req.data.lockEdit && source !== "admin"}
|
<button className="btn btn-danger no-modal" type="button" disabled={req.data.lockEdit && source !== "admin"}
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (req.data.lockEdit && source !== "admin") return;
|
if (req.data.lockEdit && source !== "admin") return;
|
||||||
|
if (registerType === "HELLOASSO") {
|
||||||
|
if (!window.confirm("Êtes-vous sûr de vouloir désinscrire ce combattant ?\nCela ne le désinscrira pas de la billetterie HelloAsso et ne le remboursera pas."))
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (!window.confirm("Êtes-vous sûr de vouloir désinscrire ce combattant ?"))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
toast.promise(apiAxios.delete(`/competition/${id}/register/${req.data.id}/${source}`), {
|
toast.promise(apiAxios.delete(`/competition/${id}/register/${req.data.id}/${source}?ban=false`), {
|
||||||
pending: "Désinscription en cours", success: "Combattant désinscrit", error: {
|
pending: "Désinscription en cours", success: "Combattant désinscrit", error: {
|
||||||
render({data}) {
|
render({data}) {
|
||||||
return data.response.data || "Erreur"
|
return data.response.data || "Erreur"
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import {CompetitionView} from "./CompetitionView.jsx";
|
|||||||
|
|
||||||
export function CompetitionRoot() {
|
export function CompetitionRoot() {
|
||||||
return <>
|
return <>
|
||||||
<h1>Competition</h1>
|
<h1>Compétition</h1>
|
||||||
<LoadingProvider>
|
<LoadingProvider>
|
||||||
<Outlet/>
|
<Outlet/>
|
||||||
</LoadingProvider>
|
</LoadingProvider>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user