feat: club page
wip: edit af request
This commit is contained in:
@@ -54,8 +54,8 @@ function ClubMenu() {
|
||||
Club
|
||||
</div>
|
||||
<ul className="dropdown-menu">
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/club/me">Mon club</NavLink></li>
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/club/member">Member</NavLink></li>
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/club/b">B</NavLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import {useState} from "react";
|
||||
import {useEffect, useState} from "react";
|
||||
import {apiAxios, getSaison} from "../utils/Tools.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import {RoleList} from "../components/MemberCustomFiels.jsx";
|
||||
import {useAuth} from "../hooks/useAuth.jsx";
|
||||
|
||||
const notUpperCase = ["de", "la", "le", "les", "des", "du", "d'", "l'", "sur"];
|
||||
|
||||
@@ -40,13 +41,38 @@ function reconstruireAdresse(infos) {
|
||||
|
||||
|
||||
export function DemandeAff() {
|
||||
const {hash} = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [initData, setInitData] = useState(null)
|
||||
const [needFile, setNeedFile] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
if (hash.startsWith("#d")) {
|
||||
const data = JSON.parse(decodeURI(hash.substring(2)));
|
||||
setInitData(data)
|
||||
setNeedFile(false)
|
||||
} else if (hash.startsWith("#e")) {
|
||||
apiAxios.get(`/affiliation/request/${hash.substring(2)}`).then(data => {
|
||||
for (let i = 0; i < data.data.members.length; i++) {
|
||||
if (data.data.members[i].licence === -1)
|
||||
data.data.members[i].licence = ""
|
||||
}
|
||||
setInitData(data.data)
|
||||
setNeedFile(false)
|
||||
}).catch(_ => {
|
||||
setInitData({})
|
||||
})
|
||||
} else {
|
||||
setInitData({})
|
||||
}
|
||||
}, []);
|
||||
|
||||
const submit = (event) => {
|
||||
event.preventDefault()
|
||||
const formData = new FormData(event.target)
|
||||
formData.append("m1_role", event.target.m1_role?.value)
|
||||
formData.append("rna", event.target.rna?.value)
|
||||
formData.append("siret", event.target.siret?.value)
|
||||
|
||||
let error = false;
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
@@ -59,16 +85,44 @@ export function DemandeAff() {
|
||||
return;
|
||||
}
|
||||
|
||||
toast.promise(
|
||||
apiAxios.post(`/affiliation/request`, formData, {headers: {'Accept': '*/*'}}),
|
||||
{
|
||||
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")
|
||||
})
|
||||
if (event.nativeEvent.submitter.value === "undo") {
|
||||
toast.promise(
|
||||
apiAxios.delete(`/affiliation/request/${initData.id}`),
|
||||
{
|
||||
pending: "Annulation de la demande d'affiliation en cours",
|
||||
success: "Demande d'affiliation annulée avec succès 🎉",
|
||||
error: "Échec de l'annulation de la demande d'affiliation 😕"
|
||||
}
|
||||
).then(_ => {
|
||||
navigate("/club/me")
|
||||
})
|
||||
}else if (event.nativeEvent.submitter.value === "edit") {
|
||||
formData.append("id", initData.id)
|
||||
|
||||
toast.promise(
|
||||
apiAxios.put(`/affiliation/request/edit`, formData),
|
||||
{
|
||||
pending: "Enregistrement des modifications en cours",
|
||||
success: "Modifications enregistrées avec succès 🎉",
|
||||
error: "Échec de l'enregistrement des modifications 😕"
|
||||
}
|
||||
).then(_ => {
|
||||
navigate("/club/me")
|
||||
})
|
||||
}else {
|
||||
formData.append("id", -1)
|
||||
|
||||
toast.promise(
|
||||
apiAxios.post(`/affiliation/request`, formData, {headers: {'Accept': '*/*'}}),
|
||||
{
|
||||
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>
|
||||
@@ -91,46 +145,59 @@ export function DemandeAff() {
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="card mb-4">
|
||||
{initData && <div className="card mb-4">
|
||||
<form onSubmit={submit}>
|
||||
<div className="card-body">
|
||||
<h4>L'association</h4>
|
||||
<AssoInfo/>
|
||||
<AssoInfo initData={initData} needFile={needFile}/>
|
||||
<h4>Membre n°1</h4>
|
||||
<MembreInfo role="m1"/>
|
||||
<MembreInfo role="m1" initData={initData.members?.at(0) || {}}/>
|
||||
<h4 style={{marginTop: '1em'}}>Membre n°2</h4>
|
||||
<MembreInfo role="m2"/>
|
||||
<MembreInfo role="m2" initData={initData.members?.at(1) || {}}/>
|
||||
<h4 style={{marginTop: '1em'}}>Membre n°3</h4>
|
||||
<MembreInfo role="m3"/>
|
||||
<MembreInfo role="m3" initData={initData.members?.at(2) || {}}/>
|
||||
|
||||
<div className="mb-3" style={{marginTop: '1em'}}>
|
||||
<p>Après validation de votre demande, vous recevrez un identifiant et mot de passe provisoire pour
|
||||
accéder à votre espace FFSAF</p>
|
||||
{!initData.id && <p>Après validation de votre demande, vous recevrez un identifiant et mot de passe provisoire pour
|
||||
accéder à votre espace FFSAF</p>}
|
||||
Notez que pour finaliser votre affiliation, il vous faudra :
|
||||
<ul>
|
||||
<li>Disposer d’au moins trois membres licenciés, dont le président</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>
|
||||
|
||||
{!initData.id ?
|
||||
<div className="row">
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-md-center">
|
||||
<button type="submit" value="new" className="btn btn-primary">Confirmer ma demande d'affiliation
|
||||
</button>
|
||||
</div>
|
||||
</div> :
|
||||
<div className="row">
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-md-center col">
|
||||
<button type="submit" value="undo" className="btn btn-danger">Annuler ma demande
|
||||
</button>
|
||||
</div>
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-md-center col">
|
||||
<button type="submit" value="edit" className="btn btn-primary">Enregistrer les modifications
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
|
||||
function AssoInfo() {
|
||||
function AssoInfo({initData, needFile}) {
|
||||
const [denomination, setDenomination] = useState("")
|
||||
const [siret, setSiret] = useState("")
|
||||
const [rna, setRna] = useState("")
|
||||
const [siret, setSiret] = useState(initData.siret ? String(initData.siret) : "")
|
||||
const [rna, setRna] = useState(initData.rna ? initData.rna : "")
|
||||
const [rnaEnable, setRnaEnable] = useState(false)
|
||||
const [adresse, setAdresse] = useState("")
|
||||
const [saison, setSaison] = useState(getSaison())
|
||||
const [adresse, setAdresse] = useState(initData.address ? initData.address : "")
|
||||
const [saison, setSaison] = useState(initData.saison ? initData.saison : getSaison())
|
||||
|
||||
const fetchSiret = () => {
|
||||
if (siret.length < 14) {
|
||||
@@ -150,7 +217,8 @@ function AssoInfo() {
|
||||
setDenomination(data2.denomination)
|
||||
setRnaEnable(data2.identifiant_association === null)
|
||||
setRna(data2.identifiant_association ? data2.identifiant_association : "")
|
||||
setAdresse(reconstruireAdresse(data2.etablissement_siege))
|
||||
if (!initData.saison || adresse === "")
|
||||
setAdresse(reconstruireAdresse(data2.etablissement_siege))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -178,13 +246,13 @@ function AssoInfo() {
|
||||
<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 defaultValue="Mesnie"/>
|
||||
aria-describedby="basic-addon1" required defaultValue={initData.name ? initData.name : ""}/>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text">N° SIRET*</span>
|
||||
<input type="number" className="form-control" placeholder="siret" name="siret" required value={siret}
|
||||
onChange={e => setSiret(e.target.value)} defaultValue={500213731}/>
|
||||
<input type="number" className="form-control" placeholder="siret" name="siret" required value={siret} disabled={!needFile}
|
||||
onChange={e => setSiret(e.target.value)}/>
|
||||
<button className="btn btn-outline-secondary" type="button" id="button-addon2"
|
||||
onClick={fetchSiret}>Rechercher
|
||||
</button>
|
||||
@@ -218,25 +286,25 @@ function AssoInfo() {
|
||||
|
||||
<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/>
|
||||
<input type="file" className="form-control" id="status" name="status" accept=".pdf,.txt" required={needFile}/>
|
||||
</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/>
|
||||
required={needFile}/>
|
||||
</div>
|
||||
</>;
|
||||
}
|
||||
|
||||
function MembreInfo({role}) {
|
||||
const [switchOn, setSwitchOn] = useState(false);
|
||||
function MembreInfo({role, initData}) {
|
||||
const [switchOn, setSwitchOn] = useState(!!initData.licence);
|
||||
|
||||
return <>
|
||||
<div className="input-group mb-3">
|
||||
<label className="input-group-text" htmlFor="inputGroupSelect01">Rôles</label>
|
||||
<select className="form-select" id="inputGroupSelect01" defaultValue={role === "m1" ? "PRESIDENT" : 0}
|
||||
disabled={role === "m1"} name={role + "_role"} required>
|
||||
<select className="form-select" id="inputGroupSelect01" defaultValue={initData.role ? initData.role : (role === "m1" ? "PRESIDENT" : 0)}
|
||||
disabled={initData.role ? initData.role === "PRESIDENT" : role === "m1"} name={role + "_role"} required>
|
||||
<option value="0">Sélectionner...</option>
|
||||
<option value="PRESIDENT">Président</option>
|
||||
<option value="TRESORIER">Trésorier</option>
|
||||
@@ -251,22 +319,22 @@ function MembreInfo({role}) {
|
||||
<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"} defaultValue={role + "-nom"}
|
||||
required/>
|
||||
<input type="text" className="form-control" id="floatingInput" placeholder="Nom" name={role + "_nom"}
|
||||
defaultValue={initData.lname ? initData.lname : ""} required/>
|
||||
<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"} defaultValue={role + "_prenom"} required/>
|
||||
name={role + "_prenom"} defaultValue={initData.fname ? initData.fname : ""} required/>
|
||||
<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"} defaultValue={role + "-mail@test.com"} required/>
|
||||
name={role + "_mail"} defaultValue={initData.email ? initData.email : ""} required/>
|
||||
<label htmlFor="floatingInput">Email*</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -283,7 +351,7 @@ function MembreInfo({role}) {
|
||||
<div className="col-sm-3">
|
||||
<div className="form-floating">
|
||||
<input type="number" className="form-control" id="floatingInput" placeholder="N° Licence"
|
||||
name={role + "_licence"} required/>
|
||||
name={role + "_licence"} defaultValue={initData.licence ? Number(initData.licence) : ""} required/>
|
||||
<label htmlFor="floatingInput">N° Licence</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -129,7 +129,7 @@ function InformationForm({data}) {
|
||||
|
||||
<div className="mb-3">
|
||||
<div className="input-group">
|
||||
<label className="input-group-text" htmlFor="status">Status</label>
|
||||
<label className="input-group-text" htmlFor="status">Statue</label>
|
||||
<a href={`${vite_url}/api/club/${data.id}/status`} target='_blank'>
|
||||
<button className="btn btn-outline-secondary" type="button" id="button-addon1"
|
||||
onClick={e => null}><FontAwesomeIcon icon={faFilePdf}></FontAwesomeIcon>
|
||||
|
||||
@@ -4,6 +4,7 @@ import {MemberPage} from "./member/MemberPage.jsx";
|
||||
import {useAuth} from "../../hooks/useAuth.jsx";
|
||||
import {MemberList} from "../MemberList.jsx";
|
||||
import {NewMemberPage} from "./member/NewMemberPage.jsx";
|
||||
import {MyClubPage} from "./club/MyClubPage.jsx";
|
||||
|
||||
export function ClubRoot() {
|
||||
const {userinfo} = useAuth()
|
||||
@@ -41,8 +42,8 @@ export function getClubChildren() {
|
||||
element: <NewMemberPage/>
|
||||
},
|
||||
{
|
||||
path: 'b',
|
||||
element: <div>Club B</div>
|
||||
path: 'me',
|
||||
element: <MyClubPage/>
|
||||
}
|
||||
]
|
||||
}
|
||||
95
src/main/webapp/src/pages/club/club/AffiliationCard.jsx
Normal file
95
src/main/webapp/src/pages/club/club/AffiliationCard.jsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
||||
import {useFetch} from "../../../hooks/useFetch.js";
|
||||
import {useEffect, useReducer, useState} from "react";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faEye, faPen} from "@fortawesome/free-solid-svg-icons";
|
||||
import {AxiosError} from "../../../components/AxiosError.jsx";
|
||||
import {apiAxios, getSaison} from "../../../utils/Tools.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {SimpleReducer} from "../../../utils/SimpleReducer.jsx";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
export function AffiliationCard({clubData}) {
|
||||
const navigate = useNavigate();
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error} = useFetch(`/affiliation/${clubData.id}`, setLoading, 1)
|
||||
const [modalAffiliation, setModal] = useState({id: 0, club: clubData.id})
|
||||
|
||||
const sendAffiliationRequest = () => {
|
||||
let createData = {}
|
||||
apiAxios.get(`/club/renew/${clubData.id}`).then(data => {
|
||||
navigate('/affiliation#d' + encodeURI(JSON.stringify(data.data)))
|
||||
})
|
||||
}
|
||||
|
||||
return <div className="card mb-4 mb-md-0">
|
||||
<div className="card-header container-fluid">
|
||||
<div className="row">
|
||||
<div className="col">Affiliation</div>
|
||||
<div className="col" style={{textAlign: 'right'}}>
|
||||
<button className="btn btn-primary btn-sm" onClick={e => sendAffiliationRequest()}>Renouveler
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<ul className="list-group">
|
||||
{data && data.sort((a, b) => b.saison - a.saison).map((affiliation, index) => {
|
||||
return <div key={index}
|
||||
className={"list-group-item d-flex justify-content-between align-items-start list-group-item-" +
|
||||
(affiliation.validate ? "success" : "warning")}>
|
||||
<div className="me-auto">{affiliation?.saison}-{affiliation?.saison + 1}</div>
|
||||
<button className="badge btn btn-primary rounded-pill" data-bs-toggle="modal"
|
||||
data-bs-target="#AffiliationModal" onClick={_ => setModal(affiliation)}>
|
||||
<FontAwesomeIcon icon={faEye}/></button>
|
||||
</div>
|
||||
})}
|
||||
{error && <AxiosError error={error}/>}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="modal fade" id="AffiliationModal" tabIndex="-1" aria-labelledby="AffiliationModalLabel"
|
||||
aria-hidden="true">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<ModalContent affiliation={modalAffiliation}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function ModalContent({affiliation}) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return <>
|
||||
{affiliation && <div>
|
||||
<div className="modal-header">
|
||||
<h1 className="modal-title fs-5" id="AffiliationModalLabel">Etat de l'affiliation</h1>
|
||||
<button type="button" className="btn-close" data-bs-dismiss="modal"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="input-group mb-3 justify-content-md-center">
|
||||
<span className="input-group-text" id="basic-addon2">{affiliation.saison || 0}</span>
|
||||
<span className="input-group-text" id="basic-addon2">-</span>
|
||||
<span className="input-group-text" id="basic-addon2">{(affiliation.saison || 0) + 1}</span>
|
||||
</div>
|
||||
<div className="input-group mb-3 justify-content-md-center">
|
||||
<span className="input-group-text" id="basic-addon2">État de la demande</span>
|
||||
{affiliation.validate ? <span className="input-group-text" id="basic-addon2">Validée</span> :
|
||||
<>
|
||||
<span className="input-group-text" id="basic-addon2">En attente</span>
|
||||
<button type="button" className="btn btn-primary"
|
||||
onClick={() => navigate('/affiliation#e' + (affiliation.id * -1))}
|
||||
data-bs-dismiss="modal"><FontAwesomeIcon icon={faEye}/></button>
|
||||
</>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="reset" className="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
import {useNavigate, useParams} from "react-router-dom";
|
||||
import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
||||
import {useFetch} from "../../../hooks/useFetch.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {apiAxios} from "../../../utils/Tools.js";
|
||||
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
|
||||
import {AxiosError} from "../../../components/AxiosError.jsx";
|
||||
import {AffiliationCard} from "./AffiliationCard.jsx";
|
||||
import {CountryList, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
|
||||
import {useRef, useState} from "react";
|
||||
import {LocationEditor, LocationEditorModal} from "../../../components/Club/LocationEditor.jsx";
|
||||
import {ContactEditor} from "../../../components/Club/ContactEditor.jsx";
|
||||
import {HoraireEditor} from "../../../components/Club/HoraireEditor.jsx";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faFilePdf} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
const vite_url = import.meta.env.VITE_URL;
|
||||
|
||||
export function ClubPage() {
|
||||
const {id} = useParams()
|
||||
const navigate = useNavigate();
|
||||
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error} = useFetch(`/club/${id}`, setLoading, 1)
|
||||
|
||||
const handleRm = () => {
|
||||
toast.promise(
|
||||
apiAxios.delete(`/club/${id}`),
|
||||
{
|
||||
pending: "Suppression du club en cours...",
|
||||
success: "Club supprimé avec succès 🎉",
|
||||
error: "Échec de la suppression du club 😕"
|
||||
}
|
||||
).then(_ => {
|
||||
navigate("/admin/club")
|
||||
})
|
||||
}
|
||||
|
||||
return <>
|
||||
<h2>Page club</h2>
|
||||
<button type="button" className="btn btn-link" onClick={() => navigate("/admin/club")}>
|
||||
« retour
|
||||
</button>
|
||||
{data
|
||||
? <div>
|
||||
<div className="row">
|
||||
<div className="col-lg-9">
|
||||
<LoadingProvider>
|
||||
<InformationForm data={data}/>
|
||||
</LoadingProvider>
|
||||
</div>
|
||||
<div className="col-lg-3">
|
||||
<LoadingProvider><AffiliationCard clubData={data}/></LoadingProvider>
|
||||
<div className="col" style={{textAlign: 'right', marginTop: '1em'}}>
|
||||
<button className="btn btn-danger btn-sm" data-bs-toggle="modal"
|
||||
data-bs-target="#confirm-delete">Supprimer le club
|
||||
</button>
|
||||
</div>
|
||||
<ConfirmDialog title="Supprimer le club"
|
||||
message="Êtes-vous sûr de vouloir supprimer ce club ?"
|
||||
onConfirm={handleRm}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
: error && <AxiosError error={error}/>
|
||||
}
|
||||
</>
|
||||
}
|
||||
|
||||
function InformationForm({data}) {
|
||||
const [switchOn, setSwitchOn] = useState(data.international);
|
||||
const [modal, setModal] = useState({id: -1})
|
||||
const locationModalCallback = useRef(null)
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const formData = new FormData(event.target);
|
||||
|
||||
toast.promise(
|
||||
apiAxios.put(`/club/${data.id}`, formData),
|
||||
{
|
||||
pending: "Enregistrement du club en cours",
|
||||
success: "Club enregistrée avec succès 🎉",
|
||||
error: "Échec de l'enregistrement du club 😕"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return <>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="card mb-4">
|
||||
<input name="id" value={data.id} readOnly hidden/>
|
||||
<div className="card-header">Affiliation n°{data.no_affiliation}</div>
|
||||
<div className="card-body text-center">
|
||||
|
||||
<TextField name="clubId" text="ClubID" value={data.clubId} disabled={true}/>
|
||||
<TextField name="name" text="Nom" value={data.name}/>
|
||||
<CountryList name="country" text="Pays" value={data.country}/>
|
||||
|
||||
<img
|
||||
src={`${vite_url}/api/club/${data.clubId}/logo`}
|
||||
alt="avatar"
|
||||
className="img-fluid" style={{object_fit: 'contain', maxHeight: '15em'}}/>
|
||||
<div className="mb-3">
|
||||
<div className="input-group">
|
||||
<label className="input-group-text" htmlFor="logo">Blason</label>
|
||||
<input type="file" className="form-control" id="logo" name="logo"
|
||||
accept=".jpg,.jpeg,.gif,.png,.svg"/>
|
||||
</div>
|
||||
<div className="form-text" id="logo">Laissez vide pour ne rien changer.</div>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<div className="input-group-text">
|
||||
<input type="checkbox" className="form-check-input mt-0" name="international" id="international"
|
||||
checked={switchOn} onChange={() => setSwitchOn(!switchOn)}/>
|
||||
<label className="input-group-text" htmlFor="international">Club externe</label>
|
||||
</div>
|
||||
</div>
|
||||
{!switchOn && <>
|
||||
<TextField name="siret" text="SIRET" value={data.siret} type="number"/>
|
||||
<TextField name="rna" text="RNA" value={data.rna} required={false}/>
|
||||
<TextField name="contact_intern" text="Contact interne" value={data.contact_intern} required={false}
|
||||
placeholder="example@test.com"/>
|
||||
<TextField name="address" text="Adresse administrative" value={data.address} placeholder="Adresse administrative"/>
|
||||
|
||||
<div className="mb-3">
|
||||
<div className="input-group">
|
||||
<label className="input-group-text" htmlFor="status">Status</label>
|
||||
<a href={`${vite_url}/api/club/${data.id}/status`} target='_blank'>
|
||||
<button className="btn btn-outline-secondary" type="button" id="button-addon1"
|
||||
onClick={e => null}><FontAwesomeIcon icon={faFilePdf}></FontAwesomeIcon>
|
||||
</button>
|
||||
</a>
|
||||
<input type="file" className="form-control" id="status" name="status" accept=".pdf,.txt"/>
|
||||
</div>
|
||||
<div className="form-text" id="status">Laissez vide pour ne rien changer.</div>
|
||||
</div>
|
||||
|
||||
<ContactEditor data={data}/>
|
||||
<LocationEditor data={data} setModal={setModal} sendData={locationModalCallback}/>
|
||||
<HoraireEditor data={data}/>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<button type="submit" className="btn btn-primary">Enregistrer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<LocationEditorModal modal={modal} sendData={locationModalCallback}/>
|
||||
</>
|
||||
}
|
||||
117
src/main/webapp/src/pages/club/club/MyClubPage.jsx
Normal file
117
src/main/webapp/src/pages/club/club/MyClubPage.jsx
Normal file
@@ -0,0 +1,117 @@
|
||||
import {useNavigate, useParams} from "react-router-dom";
|
||||
import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
||||
import {useFetch} from "../../../hooks/useFetch.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {apiAxios} from "../../../utils/Tools.js";
|
||||
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
|
||||
import {AxiosError} from "../../../components/AxiosError.jsx";
|
||||
import {AffiliationCard} from "./AffiliationCard.jsx";
|
||||
import {CountryList, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
|
||||
import {useRef, useState} from "react";
|
||||
import {LocationEditor, LocationEditorModal} from "../../../components/Club/LocationEditor.jsx";
|
||||
import {ContactEditor} from "../../../components/Club/ContactEditor.jsx";
|
||||
import {HoraireEditor} from "../../../components/Club/HoraireEditor.jsx";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faFilePdf} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
const vite_url = import.meta.env.VITE_URL;
|
||||
|
||||
export function MyClubPage() {
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error} = useFetch(`/club/me`, setLoading, 1)
|
||||
|
||||
return <>
|
||||
<h2>Mon club</h2>
|
||||
{data
|
||||
? <div>
|
||||
<div className="row">
|
||||
<div className="col-lg-9">
|
||||
<LoadingProvider>
|
||||
<InformationForm data={data}/>
|
||||
</LoadingProvider>
|
||||
</div>
|
||||
<div className="col-lg-3">
|
||||
<LoadingProvider><AffiliationCard clubData={data}/></LoadingProvider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
: error && <AxiosError error={error}/>
|
||||
}
|
||||
</>
|
||||
}
|
||||
|
||||
function InformationForm({data}) {
|
||||
const [modal, setModal] = useState({id: -1})
|
||||
const locationModalCallback = useRef(null)
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const formData = new FormData(event.target);
|
||||
|
||||
toast.promise(
|
||||
apiAxios.put(`/club/me`, formData),
|
||||
{
|
||||
pending: "Enregistrement des modifications en cours",
|
||||
success: "Modifications enregistrées avec succès 🎉",
|
||||
error: "Échec de l'enregistrement des modifications 😕"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return <>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="card mb-4">
|
||||
<input name="id" value={data.id} readOnly hidden/>
|
||||
<div className="card-header">Affiliation n°{data.no_affiliation}</div>
|
||||
<div className="card-body text-center">
|
||||
<TextField name="name" text="Nom" value={data.name} disabled={true}/>
|
||||
<CountryList name="country" text="Pays" value={data.country} disabled={true}/>
|
||||
|
||||
{!data.international && <>
|
||||
<TextField name="siret" text="SIRET" value={data.siret} type="number" disabled={true}/>
|
||||
<TextField name="rna" text="RNA" value={data.rna} required={false} disabled={true}/>
|
||||
</>}
|
||||
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6">
|
||||
<img
|
||||
src={`${vite_url}/api/club/${data.clubId}/logo`}
|
||||
alt="avatar"
|
||||
className="img-fluid" style={{object_fit: 'contain', maxHeight: '15em'}}/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<a href={`${vite_url}/api/club/${data.id}/status`} target='_blank'>
|
||||
<button className="btn btn-outline-secondary" type="button" id="button-addon1"
|
||||
onClick={e => null}>
|
||||
<FontAwesomeIcon icon={faFilePdf} size="5x"></FontAwesomeIcon><br/>
|
||||
Voir les statues
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div className="form-text" id="status">Pour modifier les informations ci-dessus, merci de contacter la FFSAF par mail.</div>
|
||||
</div>
|
||||
|
||||
{!data.international && <>
|
||||
<TextField name="contact_intern" text="Contact interne" value={data.contact_intern} required={false}
|
||||
placeholder="example@test.com"/>
|
||||
<TextField name="address" text="Adresse administrative" value={data.address} placeholder="Adresse administrative"/>
|
||||
|
||||
<ContactEditor data={data}/>
|
||||
<LocationEditor data={data} setModal={setModal} sendData={locationModalCallback}/>
|
||||
<HoraireEditor data={data}/>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<button type="submit" className="btn btn-primary">Enregistrer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<LocationEditorModal modal={modal} sendData={locationModalCallback}/>
|
||||
</>
|
||||
}
|
||||
Reference in New Issue
Block a user