feat: start affiliation system

This commit is contained in:
2024-02-05 21:42:50 +01:00
parent 2a59c22db6
commit 40427b8cfb
9 changed files with 403 additions and 0 deletions

View File

@@ -11,6 +11,7 @@ import {ToastContainer} from "react-toastify";
import './App.css'
import 'react-toastify/dist/ReactToastify.css';
import {ClubRoot, getClubChildren} from "./pages/club/ClubRoot.jsx";
import {DemandeAff, DemandeAffOk} from "./pages/DemandeAff.jsx";
const router = createBrowserRouter([
{
@@ -31,6 +32,19 @@ const router = createBrowserRouter([
path: 'club',
element: <ClubRoot/>,
children: getClubChildren()
},
{
path: 'affiliation',
children: [
{
path: '',
element: <DemandeAff/>
},
{
path: 'ok',
element: <DemandeAffOk/>
}
]
}
]
},

View File

@@ -24,6 +24,7 @@ export function Nav() {
<li className="nav-item"><NavLink className="nav-link" to="/">Accueil</NavLink></li>
<ClubMenu/>
<AdminMenu/>
<AffiliationMenu/>
<LoginMenu/>
</ul>
</div>
@@ -32,6 +33,15 @@ export function Nav() {
</nav>
}
function AffiliationMenu() {
const {is_authenticated} = useAuth()
if (is_authenticated)
return <></>
return <li className="nav-item"><NavLink className="nav-link" to="/affiliation">Demande d'affiliation</NavLink></li>
}
function ClubMenu() {
const {is_authenticated, userinfo} = useAuth()

View File

@@ -0,0 +1,187 @@
import {useState} from "react";
import {apiAxios} from "../utils/Tools.js";
import {toast} from "react-toastify";
import {useNavigate} from "react-router-dom";
function reconstruireAdresse(infos) {
let adresseReconstruite = "";
adresseReconstruite += infos.numero_voie + ' ' + infos.type_voie + ' ';
adresseReconstruite += infos.libelle_voie + ', ';
adresseReconstruite += infos.code_postal + ' ' + infos.libelle_commune + ', ';
if (infos.complement_adresse) {
adresseReconstruite += infos.complement_adresse + ', ';
}
if (infos.code_cedex && infos.libelle_cedex) {
adresseReconstruite += 'Cedex ' + infos.code_cedex + ' - ' + infos.libelle_cedex;
}
if (adresseReconstruite.endsWith(', ')) {
adresseReconstruite = adresseReconstruite.slice(0, -2);
}
return adresseReconstruite;
}
export function DemandeAff() {
const navigate = useNavigate();
const submit = (event) => {
event.preventDefault()
const formData = new FormData(event.target)
toast.promise(
apiAxios.post(`asso/affiliation`, formData),
{
pending: "Enregistrement de la demande d'affiliation en cours",
success: "Demande d'affiliation enregistrée avec succès 🎉",
error: "Échec de la demande d'affiliation 😕"
}
).then(_ => {
navigate("/affiliation/ok")
})
}
return <div>
<h1>Demande d'affiliation</h1>
<p>L'affiliation est annuelle et valable pour une saison sportive : du 1er septembre au 31 août de lannée suivante.</p>
Pour saffilier, une association sportive doit réunir les conditions suivantes :
<ul>
<li>Avoir son siège social en France ou Principauté de Monaco</li>
<li>Être constituée conformément au chapitre 1er du titre II du livre 1er du Code du Sport</li>
<li>Poursuivre un objet social entrant dans la définition de larticle 1 des statuts de la Fédération</li>
<li>Disposer de statuts compatibles avec les principes dorganisation et de fonctionnement de la Fédération</li>
<li>Assurer en son sein la liberté dopinion et le respect des droits de la défense, et sinterdire toute discrimination</li>
<li>Respecter les règles dencadrement, dhygiène et de sécurité établies par les règlements de la Fédération</li>
</ul>
<div className="card mb-4">
<form onSubmit={submit}>
<div className="card-body">
<h4>L'association</h4>
<AssoInfo/>
<h4>Le président</h4>
<MembreInfo role="president"/>
<h4>Le trésorier</h4>
<MembreInfo role="tresorier"/>
<h4>Le secrétaire</h4>
<MembreInfo role="secretaire"/>
<div className="mb-3">
<p>Après validation de votre demande, vous recevrez un login et mot de passe provisoire pour accéder à votre espace FFSAF</p>
Notez que pour finaliser votre affiliation, il vous faudra :
<ul>
<li>Disposer dau moins trois membres licenciés, dont le président, le trésorier et le secrétaire</li>
<li>S'être acquitté des cotisations prévues par les règlements fédéraux</li>
</ul>
</div>
<div className="row">
<div className="d-grid gap-2 d-md-flex justify-content-md-center">
<button type="submit" className="btn btn-primary">Confirmer ma demande d'affiliation</button>
</div>
</div>
</div>
</form>
</div>
</div>
}
function AssoInfo() {
const [denomination, setDenomination] = useState("")
const [siren, setSiren] = useState("")
const [rna, setRna] = useState("")
const [rnaEnable, setRnaEnable] = useState(false)
const [adresse, setAdresse] = useState("")
const fetchSiren = () => {
toast.promise(
apiAxios.get(`asso/siren/${siren}`),
{
pending: "Recherche de l'association en cours",
success: "Association trouvée avec succès 🎉",
error: "Échec de la recherche de l'association 😕"
}
).then(data => {
const data2 = data.data.unite_legale
setDenomination(data2.denomination)
setRnaEnable(data2.identifiant_association === null)
setRna(data2.identifiant_association ? data2.identifiant_association : "")
setAdresse(reconstruireAdresse(data2.etablissement_siege))
})
}
return <>
<div className="input-group mb-3">
<span className="input-group-text" id="basic-addon1">Nom de l'association*</span>
<input type="text" className="form-control" placeholder="Nom de l'association" name="name" aria-label="Nom de l'association"
aria-describedby="basic-addon1" required/>
</div>
<div className="input-group mb-3">
<span className="input-group-text">N° SIREN*</span>
<input type="number" className="form-control" placeholder="siren" name="siren" required value={siren}
onChange={e => setSiren(e.target.value)}/>
<button className="btn btn-outline-secondary" type="button" id="button-addon2" onClick={fetchSiren}>Rechercher</button>
</div>
<div className="input-group mb-3">
<span className="input-group-text" id="basic-addon1">Dénomination</span>
<input type="text" className="form-control" placeholder="Appuyer sur rechercher pour compléter" aria-label="Dénomination"
aria-describedby="basic-addon1" disabled value={denomination} readOnly/>
</div>
<div className="input-group mb-3">
<span className="input-group-text" id="basic-addon1">RNA</span>
<input type="text" className="form-control" placeholder="RNA" aria-label="RNA" aria-describedby="basic-addon1"
disabled={!rnaEnable} name="rna" value={rna} onChange={e => setRna(e.target.value)}/>
</div>
<div className="input-group mb-3">
<span className="input-group-text" id="basic-addon1">Adresse*</span>
<input type="text" className="form-control" placeholder="Adresse" aria-label="Adresse" aria-describedby="basic-addon1"
required value={adresse} name="adresse" onChange={e => setAdresse(e.target.value)}/>
</div>
<div className="input-group mb-3">
<label className="input-group-text" htmlFor="status">Status*</label>
<input type="file" className="form-control" id="status" name="status" accept=".pdf,.txt" required/>
</div>
<div className="input-group mb-3">
<label className="input-group-text" htmlFor="logo">Logo*</label>
<input type="file" className="form-control" id="logo" name="logo" accept=".jpg,.jpeg,.gif,.png,.svg" required/>
</div>
</>;
}
function MembreInfo({role}) {
return <div className="row g-3 mb-3">
<div className="col-sm-3">
<div className="form-floating">
<input type="text" className="form-control" id="floatingInput" placeholder="Nom" name={role + "-nom"}/>
<label htmlFor="floatingInput">Nom*</label>
</div>
</div>
<div className="col-sm-3">
<div className="form-floating">
<input type="text" className="form-control" id="floatingInput" placeholder="Prénom" name={role + "-prenom"}/>
<label htmlFor="floatingInput">Prénom*</label>
</div>
</div>
<div className="col-sm-5">
<div className="form-floating">
<input type="email" className="form-control" id="floatingInput" placeholder="name@example.com" name={role + "-mail"}/>
<label htmlFor="floatingInput">Email*</label>
</div>
</div>
</div>
}
export function DemandeAffOk() {
return (
<div>
<h1 className="text-green-800 text-4xl">Demande d'affiliation envoyée avec succès</h1>
<p>Une fois votre demande validée, vous recevrez un login et mot de passe provisoire pour accéder à votre espace FFSAF</p>
</div>
);
}