feat: rework competition perm, naming, competition data
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m6s
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m6s
This commit is contained in:
BIN
src/main/webapp/public/img/HA-help-2.png
Normal file
BIN
src/main/webapp/public/img/HA-help-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
src/main/webapp/public/img/HA-help-3.png
Normal file
BIN
src/main/webapp/public/img/HA-help-3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
BIN
src/main/webapp/public/img/HA-help-4.png
Normal file
BIN
src/main/webapp/public/img/HA-help-4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
@@ -4,9 +4,7 @@ import {AxiosError} from "./AxiosError.jsx";
|
||||
|
||||
export function ClubSelect({defaultValue, name, na = false, disabled = false}) {
|
||||
return <LoadingProvider>
|
||||
<div className="input-group mb-3">
|
||||
<ClubSelect_ defaultValue={defaultValue} name={name} na={na} disabled={disabled}/>
|
||||
</div>
|
||||
<ClubSelect_ defaultValue={defaultValue} name={name} na={na} disabled={disabled}/>
|
||||
</LoadingProvider>
|
||||
}
|
||||
|
||||
@@ -19,7 +17,7 @@ function ClubSelect_({defaultValue, name, na, disabled}) {
|
||||
? <div className="input-group mb-3">
|
||||
<label className="input-group-text" id="inputGroupSelect02">Club</label>
|
||||
<select className="form-select" id="inputGroupSelect02" disabled={disabled}
|
||||
defaultValue={defaultValue? defaultValue : -1} name={name}>
|
||||
defaultValue={defaultValue ? defaultValue : -1} name={name}>
|
||||
<option>Sélectionner...</option>
|
||||
{na && <option value={-1}>-- Non licencier --</option>}
|
||||
{data.map(club => (<option key={club.id} value={club.id}>{club.name}</option>))}
|
||||
|
||||
@@ -2,7 +2,7 @@ import {useNavigate, useParams} from "react-router-dom";
|
||||
import {useLoadingSwitcher} from "../../hooks/useLoading.jsx";
|
||||
import {useFetch} from "../../hooks/useFetch.js";
|
||||
import {AxiosError} from "../../components/AxiosError.jsx";
|
||||
import {Checkbox, CheckField, OptionField, TextField} from "../../components/MemberCustomFiels.jsx";
|
||||
import {CheckField, OptionField, TextField} from "../../components/MemberCustomFiels.jsx";
|
||||
import {ClubSelect} from "../../components/ClubSelect.jsx";
|
||||
import {ConfirmDialog} from "../../components/ConfirmDialog.jsx";
|
||||
import {toast} from "react-toastify";
|
||||
@@ -10,14 +10,14 @@ import {apiAxios, errFormater} from "../../utils/Tools.js";
|
||||
import {useEffect, useReducer, useState} from "react";
|
||||
import {SimpleReducer} from "../../utils/SimpleReducer.jsx";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faAdd, faPen, faTrashCan} from "@fortawesome/free-solid-svg-icons";
|
||||
import {faAdd, faTrashCan} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
export function CompetitionEdit() {
|
||||
const {id} = useParams()
|
||||
const navigate = useNavigate();
|
||||
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, refresh, error} = useFetch(`/competition/${id}`, setLoading, 1)
|
||||
const {data, refresh, error} = useFetch(`/competition/${id}?light=false`, setLoading, 1)
|
||||
|
||||
const handleRm = () => {
|
||||
toast.promise(
|
||||
@@ -49,7 +49,7 @@ export function CompetitionEdit() {
|
||||
{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>}
|
||||
|
||||
{data.id !== null && <ContentSAFCA data2={data}/>}
|
||||
{data.id !== null && data.system === "SAFCA" && <ContentSAFCA data2={data}/>}
|
||||
|
||||
{data.id !== null && <>
|
||||
<div className="col" style={{textAlign: 'right', marginTop: '1em'}}>
|
||||
@@ -212,17 +212,74 @@ function ContentSAFCA({data2}) {
|
||||
|
||||
function Content({data}) {
|
||||
const navigate = useNavigate();
|
||||
const [registerMode, setRegisterMode] = useState(data.registerMode || "FREE");
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
let err = false;
|
||||
|
||||
const out = {}
|
||||
out['id'] = (data.id === "") ? null : data.id
|
||||
out['name'] = event.target.name?.value
|
||||
out['date'] = event.target.date?.value
|
||||
out['toDate'] = event.target.toDate?.value
|
||||
out['system'] = event.target.system?.value
|
||||
out['club'] = event.target.club?.value
|
||||
out['owner'] = event.target.owner?.value
|
||||
out['description'] = event.target.description?.value
|
||||
out['adresse'] = event.target.adresse?.value
|
||||
out['publicVisible'] = event.target.publicVisible?.checked
|
||||
|
||||
out['startRegister'] = event.target.startRegister?.value
|
||||
out['endRegister'] = event.target.endRegister?.value
|
||||
out['registerMode'] = registerMode
|
||||
|
||||
if (out['registerMode'] === "HELLOASSO") {
|
||||
out['data3'] = event.target.data3?.value
|
||||
|
||||
const url = event.target.helloassoUrl?.value
|
||||
|
||||
if (!url || !event.target.data3?.value) {
|
||||
toast.error("Veuillez renseigner l'URL de la billetterie HelloAsso et les tarifs associés.")
|
||||
err = true;
|
||||
} else {
|
||||
const regex = /\/associations\/([^/]+)\/evenements\/([^/]+)/;
|
||||
const match = url.match(regex);
|
||||
|
||||
if (match) {
|
||||
out['data1'] = match[1]
|
||||
out['data2'] = match[2]
|
||||
} else {
|
||||
toast.error("L'URL de la billetterie HelloAsso n'est pas valide. Veuillez vérifier le format de l'URL.")
|
||||
err = true;
|
||||
}
|
||||
}
|
||||
|
||||
out['data4'] = event.target.data4?.value
|
||||
if (!out['data4']) {
|
||||
toast.error("Veuillez renseigner l'email de réception des inscriptions échouées.")
|
||||
err = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (out['date'] > out['toDate']) {
|
||||
toast.error("La date de fin doit être postérieure à la date de début.")
|
||||
err = true;
|
||||
}
|
||||
|
||||
if ((out['registerMode'] === "FREE" || out['registerMode'] === "CLUB_ADMIN") && (!out['startRegister'] || !out['endRegister'])) {
|
||||
toast.error("Veuillez renseigner les dates de début et de fin d'inscription.")
|
||||
err = true;
|
||||
}
|
||||
if ((out['registerMode'] === "FREE" || out['registerMode'] === "CLUB_ADMIN") && out['startRegister'] > out['endRegister']) {
|
||||
toast.error("La date de fin d'inscription doit être postérieure à la date de début d'inscription.")
|
||||
err = true;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
toast.promise(
|
||||
apiAxios.post(`/competition`, out),
|
||||
@@ -246,24 +303,135 @@ function Content({data}) {
|
||||
<input name="id" value={data.id || ""} readOnly hidden/>
|
||||
<div className="card-header">{data.id ? "Edition competition" : "Création competition"}</div>
|
||||
<div className="card-body text-center">
|
||||
<TextField name="uuid" text="UUID" value={data.uuid} disabled={true}/>
|
||||
<TextField name="name" text="Nom" value={data.name}/>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text" id="birth_date">Date</span>
|
||||
<input type="date" className="form-control" placeholder="jj/mm/aaaa" aria-label="date"
|
||||
name="date" aria-describedby="date" defaultValue={data.date ? data.date.split('T')[0] : ''} required/>
|
||||
</div>
|
||||
|
||||
{data.id !== null && <TextField name="owner" text="Propriétaire" value={data.owner}/>}
|
||||
|
||||
<OptionField name="system" text="System" value={data.system} values={{SAFCA: 'SAFCA'}} disabled={data.id !== null}/>
|
||||
|
||||
{data.id !== null &&
|
||||
<div className="row">
|
||||
<ClubSelect defaultValue={data.club} name="club" na={false} disabled={true}/>
|
||||
<div className="accordion" id="accordionExample">
|
||||
<div className="accordion-item">
|
||||
<h2 className="accordion-header">
|
||||
<button className="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne"
|
||||
aria-expanded="false" aria-controls="collapseOne">
|
||||
Informations techniques
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseOne" className="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div className="accordion-body">
|
||||
<TextField name="uuid" text="UUID" value={data.uuid} disabled={true}/>
|
||||
<OptionField name="system" text="System" value={data.system} values={{SAFCA: 'SAFCA', NONE: "intranet"}}
|
||||
disabled={data.id !== null}/>
|
||||
{data.id !== null &&
|
||||
<div className="row">
|
||||
<ClubSelect defaultValue={data.club} name="club" na={false} disabled={true}/>
|
||||
</div>
|
||||
}
|
||||
{data.id !== null && <TextField name="owner" text="Propriétaire" value={data.owner}/>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div className="accordion-item">
|
||||
<h2 className="accordion-header">
|
||||
<button className="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo"
|
||||
aria-expanded="true" aria-controls="collapseTwo">
|
||||
Informations générales sur la competition
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseTwo" className="accordion-collapse collapse show" data-bs-parent="#accordionExample">
|
||||
<div className="accordion-body">
|
||||
<TextField name="name" text="Nom*" value={data.name}/>
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text" id="date">Date*</span>
|
||||
<span className="input-group-text" id="date">Du</span>
|
||||
<input type="date" className="form-control" placeholder="jj/mm/aaaa" aria-label="date"
|
||||
name="date" aria-describedby="date" defaultValue={data.date ? data.date.split('T')[0] : ''} required/>
|
||||
<span className="input-group-text" id="date">Au</span>
|
||||
<input type="date" className="form-control" placeholder="jj/mm/aaaa" aria-label="toDate"
|
||||
name="toDate" aria-describedby="toDate" defaultValue={data.toDate ? data.toDate.split('T')[0] : ''}
|
||||
required/>
|
||||
</div>
|
||||
<TextField name="description" text="Description" value={data.description} required={false}/>
|
||||
<TextField name="adresse" text="Adresse" value={data.adresse} required={false}/>
|
||||
<CheckField name="publicVisible" text="Visible par le public (Apparaît dans la liste des compétitions)"
|
||||
value={data.publicVisible} row={true}/>
|
||||
<small>Si non coché, la compétition ne sera visible que par les personnes pouvant y inscrire des participants.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="accordion-item">
|
||||
<h2 className="accordion-header">
|
||||
<button className="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree"
|
||||
aria-expanded="false" aria-controls="collapseThree">
|
||||
Informations sur le mode d'inscription
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseThree" className="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div className="accordion-body">
|
||||
|
||||
<div className="row">
|
||||
<div className="input-group mb-3">
|
||||
<label className="input-group-text">Qui peut inscrire</label>
|
||||
<select className="form-select" value={registerMode} onChange={event => setRegisterMode(event.target.value)}>
|
||||
<option value="FREE">Tous les membres de la FFSAF</option>
|
||||
<option value="CLUB_ADMIN">Responsables et bureaux des associations</option>
|
||||
<option value="ADMIN">Uniquement les administrateurs de la compétition</option>
|
||||
<option value="HELLOASSO">Billetterie HelloAsso</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3"
|
||||
style={{display: registerMode === "FREE" || registerMode === "CLUB_ADMIN" ? "flex" : "none"}}>
|
||||
<span className="input-group-text" id="startRegister">Date d'inscription</span>
|
||||
<span className="input-group-text" id="startRegister">Du</span>
|
||||
<input type="datetime-local" className="form-control" placeholder="jj/mm/aaaa" aria-label="date"
|
||||
name="startRegister" aria-describedby="startRegister"
|
||||
defaultValue={data.startRegister ? data.startRegister.split('+')[0] : ''}/>
|
||||
<span className="input-group-text" id="endRegister">Au</span>
|
||||
<input type="datetime-local" className="form-control" placeholder="jj/mm/aaaa" aria-label="endRegister"
|
||||
name="endRegister" aria-describedby="endRegister"
|
||||
defaultValue={data.endRegister ? data.endRegister.split('+')[0] : ''}/>
|
||||
</div>
|
||||
|
||||
<div style={{display: registerMode === "HELLOASSO" ? "initial" : "none"}}>
|
||||
<span style={{textAlign: "left"}}>
|
||||
<div>Afin de permettre une bonne interconnexion avec HelloAsso, merci de suivre les instructions suivantes :</div>
|
||||
<ul>
|
||||
<li><strong>Configurer l'url de notification : </strong>afin que nous puissions recevoir une notification à
|
||||
chaque inscription, il est nécessaire de configurer l'url de notification de votre compte HelloAsso pour
|
||||
qu'il redirige vers "https://intra.ffsaf.fr/api/webhook/ha". Pour ce faire, depuis la page d'accueil de
|
||||
votre association sur HelloAsso, allez dans <strong>Mon compte</strong> > <strong>Paramètres</strong> >
|
||||
<strong> Intégrations et API</strong> section Notification et copier-coller <strong>https://intra.ffsaf.fr/api/webhook/ha </strong>
|
||||
dans le champ <strong>Mon URL de callback</strong> et enregister.
|
||||
<img src="/img/HA-help-4.png" alt="" className="img-fluid" style={{objectFit: "contain"}}/></li>
|
||||
<li><strong>Copier-coller le nom exacte des tarifs</strong> <em>-sépare par des point-virgules-</em> qui donneront
|
||||
lieux à une inscription automatique. Tous ces tarifs doivent impérativement demander le numéro de licence
|
||||
en champs obligatoire. Pour ce faire, lors de la configuration de votre billetterie à l'étape n°3,
|
||||
cliquer sur <strong>+ Ajouter une information</strong>, saisissez l'intituler exact suivant
|
||||
<strong> Numéro de licence</strong>, dans <strong>Type de réponse souhaitée</strong> rentrer Nombre,
|
||||
sélectionner les tarifs entrés plus précédemment et rendre l'information obligatoire.
|
||||
<img src="/img/HA-help-3.png" className="img-fluid" alt="..." style={{object_fit: 'contain'}}/>
|
||||
<img src="/img/HA-help-2.png" className="img-fluid" alt="..." style={{object_fit: 'contain'}}/></li>
|
||||
<li><strong>Copier-coller l'url de votre billetterie</strong> dans le champs si dessous. Il devrais avoir la forme suivante:
|
||||
<em> https://www.helloasso.com/associations/<nom-asso-sur-helloasso>/evenements/<nom-billetterie></em></li>
|
||||
</ul>
|
||||
</span>
|
||||
|
||||
<TextField name="helloassoUrl" text="Url de la billetterie HelloAsso" required={false}
|
||||
value={data.data1 && data.data2 && `https://www.helloasso.com/associations/${data.data1}/evenements/${data.data2}` || ""}
|
||||
placeholder="https://www.helloasso.com/associations/nom-asso-sur-helloasso/evenements/nom-billetterie"/>
|
||||
|
||||
<TextField name="data3" text="Tarifs HelloAsso"
|
||||
value={data.data3 || ""} required={false}
|
||||
placeholder="Tarif1;Tarif2;Tarif3"/>
|
||||
|
||||
<TextField name="data4" text="Email de réception des inscriptions échoué"
|
||||
value={data.data4 || ""} required={false}/>
|
||||
<small>Si pour une raison quelconque l'inscription automatique échoue, un email sera envoyé à cette adresse pour
|
||||
vous en informer</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row mb-3">
|
||||
@@ -273,4 +441,4 @@ function Content({data}) {
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,32 @@ function MakeCentralPanel({data, navigate}) {
|
||||
</>
|
||||
}
|
||||
|
||||
const inscText = (type) => {
|
||||
if (type === "FREE") {
|
||||
return "Inscriptions libres"
|
||||
} else if (type === "CLUB_ADMIN") {
|
||||
return "Inscriptions par les responsables de club"
|
||||
} else if (type === "ADMIN") {
|
||||
return "Inscriptions par les administrateurs de la compétition"
|
||||
} else if (type === "HELLOASSO") {
|
||||
return "Inscriptions sur la billetterie HelloAsso"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
function MakeRow({data, navigate}) {
|
||||
return <div className="list-group-item d-flex justify-content-between align-items-start list-group-item-action"
|
||||
onClick={() => navigate("" + data.id)}>
|
||||
<div className="ms-2 col-auto">
|
||||
<div className="fw-bold">{data.name}</div>
|
||||
<small>{data.date.split('T')[0]}</small>
|
||||
return <div className="list-group-item list-group-item-action"
|
||||
onClick={() => data.canEdit ? navigate("" + data.id) : navigate("view/" + data.id)}>
|
||||
<div className="row justify-content-between align-items-start ">
|
||||
<div className="ms-2 col-auto">
|
||||
<div><strong>{data.name}</strong> <small>par {data.clubName}</small></div>
|
||||
<small>Du {new Date(data.date.split('T')[0]).toLocaleDateString()} au {new Date(data.toDate.split('T')[0]).toLocaleDateString()}</small>
|
||||
</div>
|
||||
<div className="ms-2 col-auto">
|
||||
<small style={{textAlign: 'right'}}>{inscText(data.registerMode)}</small>
|
||||
</div>
|
||||
</div>
|
||||
<small style={{textAlign: 'right'}}>{data.clubName}<br/>{data.system}</small>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import {Outlet} from "react-router-dom";
|
||||
import {CompetitionList} from "./CompetitionList.jsx";
|
||||
import {CompetitionEdit} from "./CompetitionEdit.jsx";
|
||||
import {CompetitionRegisterAdmin} from "./CompetitionRegisterAdmin.jsx";
|
||||
import {CompetitionView} from "./CompetitionView.jsx";
|
||||
|
||||
export function CompetitionRoot() {
|
||||
return <>
|
||||
@@ -23,9 +24,13 @@ export function getCompetitionChildren() {
|
||||
path: ':id',
|
||||
element: <CompetitionEdit/>
|
||||
},
|
||||
{
|
||||
path: 'view/:id',
|
||||
element: <CompetitionView/>
|
||||
},
|
||||
{
|
||||
path: ':id/register',
|
||||
element: <CompetitionRegisterAdmin/>
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
72
src/main/webapp/src/pages/competition/CompetitionView.jsx
Normal file
72
src/main/webapp/src/pages/competition/CompetitionView.jsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import {useNavigate, useParams} from "react-router-dom";
|
||||
import {useLoadingSwitcher} from "../../hooks/useLoading.jsx";
|
||||
import {useFetch} from "../../hooks/useFetch.js";
|
||||
import {AxiosError} from "../../components/AxiosError.jsx";
|
||||
import {useAuth} from "../../hooks/useAuth.jsx";
|
||||
import {isClubAdmin} from "../../utils/Tools.js";
|
||||
|
||||
export function CompetitionView() {
|
||||
|
||||
const {id} = useParams()
|
||||
const navigate = useNavigate();
|
||||
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error} = useFetch(`/competition/${id}?light=true`, setLoading, 1)
|
||||
|
||||
return <>
|
||||
<button type="button" className="btn btn-link" onClick={() => navigate("/competition")}>
|
||||
« retour
|
||||
</button>
|
||||
<div>
|
||||
{data ? <>
|
||||
<MakeContent data={data}/>
|
||||
</>
|
||||
: error && <AxiosError error={error}/>
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
const inscText = (type) => {
|
||||
if (type === "FREE") {
|
||||
return "Libres"
|
||||
} else if (type === "CLUB_ADMIN") {
|
||||
return "Par les responsables de club"
|
||||
} else if (type === "ADMIN") {
|
||||
return "Par les administrateurs de la compétition"
|
||||
} else if (type === "HELLOASSO") {
|
||||
return "Sur la billetterie HelloAsso"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
function MakeContent({data}) {
|
||||
const {userinfo} = useAuth()
|
||||
|
||||
return <div className="card mb-4">
|
||||
<div className="card-header">
|
||||
<h2 className="card-title" style={{textAlign: "center"}}>{data.name}</h2>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<p>{data.description}</p>
|
||||
<p><strong>Date
|
||||
:</strong> Du {new Date(data.date.split('T')[0]).toLocaleDateString()} au {new Date(data.toDate.split('T')[0]).toLocaleDateString()}
|
||||
</p>
|
||||
<p><strong>Lieu :</strong> {data.adresse}</p>
|
||||
<p><strong>Organisateur :</strong> {data.clubName}</p>
|
||||
<p><strong>Type d'inscription :</strong> {inscText(data.registerMode)}</p>
|
||||
{(data.registerMode === "FREE" || data.registerMode === "CLUB_ADMIN") &&
|
||||
<p><strong>Date d'inscription :</strong> Du {new Date(data.startRegister.split('+')[0]).toLocaleString()} au {new Date(data.endRegister.split('+')[0]).toLocaleString()}</p>
|
||||
}
|
||||
{(data.registerMode === "CLUB_ADMIN" && isClubAdmin(userinfo)) || data.registerMode === "FREE" &&
|
||||
<button type="button" className="btn btn-primary" disabled={new Date() < new Date(data.startRegister.split('+')[0]) || new Date() > new Date(data.endRegister.split('+')[0])}>Inscription</button>
|
||||
}
|
||||
{data.registerMode === "HELLOASSO" &&
|
||||
<p><strong>Billetterie :</strong> <a
|
||||
href={`https://www.helloasso.com/associations/${data.data1}/evenements/${data.data2}`} target="_blank"
|
||||
rel="noopener noreferrer">{`https://www.helloasso.com/associations/${data.data1}/evenements/${data.data2}`}</a></p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -7,6 +7,15 @@ export const apiAxios = axios.create({
|
||||
});
|
||||
apiAxios.defaults.headers.post['Accept'] = 'application/json; charset=UTF-8';
|
||||
|
||||
export function isInClub(userinfo, clubId) {
|
||||
return userinfo?.groups.filter((g => g.startsWith("/club/"))).map(g => g.split("/")[2]).map(g => Number(g.split("-")[0])).includes(clubId)
|
||||
}
|
||||
|
||||
export function isClubAdmin(userinfo) {
|
||||
return userinfo?.roles?.includes("club_president") || userinfo?.roles?.includes("club_respo_intra")
|
||||
|| userinfo?.roles?.includes("club_secretaire") || userinfo?.roles?.includes("club_tresorier");
|
||||
}
|
||||
|
||||
|
||||
export const errFormater = (data, msg) => {
|
||||
if (typeof data.response.data === 'string' || data.response.data instanceof String)
|
||||
|
||||
Reference in New Issue
Block a user