wip: club
This commit is contained in:
@@ -14,6 +14,10 @@
|
||||
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
||||
crossorigin=""/>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
30
src/main/webapp/package-lock.json
generated
30
src/main/webapp/package-lock.json
generated
@@ -15,8 +15,10 @@
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"axios": "^1.6.5",
|
||||
"browser-image-compression": "^2.0.2",
|
||||
"leaflet": "^1.9.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-leaflet": "^4.2.1",
|
||||
"react-loader-spinner": "^6.1.6",
|
||||
"react-router-dom": "^6.21.2",
|
||||
"react-toastify": "^10.0.4"
|
||||
@@ -1023,6 +1025,16 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-leaflet/core": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
|
||||
"integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
|
||||
"peerDependencies": {
|
||||
"leaflet": "^1.9.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.2.tgz",
|
||||
@@ -3113,6 +3125,11 @@
|
||||
"json-buffer": "3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/leaflet": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
|
||||
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="
|
||||
},
|
||||
"node_modules/levn": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||
@@ -3558,6 +3575,19 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"node_modules/react-leaflet": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
|
||||
"integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
|
||||
"dependencies": {
|
||||
"@react-leaflet/core": "^2.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"leaflet": "^1.9.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-loader-spinner": {
|
||||
"version": "6.1.6",
|
||||
"resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-6.1.6.tgz",
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"axios": "^1.6.5",
|
||||
"browser-image-compression": "^2.0.2",
|
||||
"leaflet": "^1.9.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-leaflet": "^4.2.1",
|
||||
"react-loader-spinner": "^6.1.6",
|
||||
"react-router-dom": "^6.21.2",
|
||||
"react-toastify": "^10.0.4"
|
||||
|
||||
@@ -36,7 +36,7 @@ export function BirthDayField({inti_date, inti_category}) {
|
||||
</>
|
||||
}
|
||||
|
||||
export function OptionField({name, text, values, value, disabled=false}) {
|
||||
export function OptionField({name, text, values, value, disabled = false}) {
|
||||
return <div className="row">
|
||||
<div className="input-group mb-3">
|
||||
<label className="input-group-text" id={name}>{text}</label>
|
||||
@@ -49,12 +49,20 @@ export function OptionField({name, text, values, value, disabled=false}) {
|
||||
</div>
|
||||
}
|
||||
|
||||
export function TextField({name, text, value, placeholder, type = "text"}) {
|
||||
export function CountryList({name, text, value, values = undefined, disabled = false}) {
|
||||
if (values === undefined){
|
||||
values = {NA: 'Sélectionner...', fr: 'FR', es: 'ES', be: 'BE'}
|
||||
}
|
||||
|
||||
return <OptionField name={name} text={text} value={value} values={values} disabled={disabled}/>
|
||||
}
|
||||
|
||||
export function TextField({name, text, value, placeholder, type = "text", disabled = false}) {
|
||||
return <div className="row">
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text" id={name}>{text}</span>
|
||||
<input type={type} className="form-control" placeholder={placeholder ? placeholder : text} aria-label={name}
|
||||
name={name} aria-describedby={name} defaultValue={value} required/>
|
||||
name={name} aria-describedby={name} defaultValue={value} disabled={disabled} required/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -79,7 +87,7 @@ export function CheckField({name, text, value, row = false}) {
|
||||
</>
|
||||
}
|
||||
|
||||
export const Checkbox = ({ label, value, onChange }) => {
|
||||
export const Checkbox = ({label, value, onChange}) => {
|
||||
const handleChange = () => {
|
||||
onChange(!value);
|
||||
};
|
||||
|
||||
@@ -72,7 +72,7 @@ function AdminMenu() {
|
||||
</div>
|
||||
<ul className="dropdown-menu">
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/admin/member">Member</NavLink></li>
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/admin/b">B</NavLink></li>
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/admin/club">Club</NavLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
|
||||
43
src/main/webapp/src/components/SearchBar.jsx
Normal file
43
src/main/webapp/src/components/SearchBar.jsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import {useEffect, useState} from "react";
|
||||
|
||||
const removeDiacritics = str => {
|
||||
return str
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
}
|
||||
|
||||
|
||||
export function SearchBar({search}) {
|
||||
const [searchInput, setSearchInput] = useState("");
|
||||
|
||||
const handelChange = (e) => {
|
||||
setSearchInput(e.target.value);
|
||||
}
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
searchMember();
|
||||
}
|
||||
}
|
||||
|
||||
const searchMember = () => {
|
||||
search(removeDiacritics(searchInput));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const delayDebounceFn = setTimeout(() => {
|
||||
searchMember();
|
||||
}, 750)
|
||||
return () => clearTimeout(delayDebounceFn)
|
||||
}, [searchInput])
|
||||
|
||||
return <div className="mb-3">
|
||||
<div className="input-group mb-3">
|
||||
<input type="text" className="form-control" placeholder="Rechercher..." aria-label="Rechercher..."
|
||||
aria-describedby="button-addon2" value={searchInput} onChange={handelChange} onKeyDown={handleKeyDown}/>
|
||||
<button className="btn btn-outline-secondary" type="button" id="button-addon2"
|
||||
onClick={searchMember}>Rechercher
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -30,15 +30,16 @@ export function DemandeAff() {
|
||||
const submit = (event) => {
|
||||
event.preventDefault()
|
||||
const formData = new FormData(event.target)
|
||||
formData.append("m1_role", event.target.m1_role?.value)
|
||||
toast.promise(
|
||||
apiAxios.post(`asso/affiliation`, formData, { headers: {'Accept': '*/*'}}),
|
||||
apiAxios.post(`/affiliation`, 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")
|
||||
navigate("/affiliation/ok")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -67,14 +68,14 @@ export function DemandeAff() {
|
||||
<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"/>
|
||||
<h4>Membre n°1</h4>
|
||||
<MembreInfo role="m1"/>
|
||||
<h4 style={{marginTop: '1em'}}>Membre n°2</h4>
|
||||
<MembreInfo role="m2"/>
|
||||
<h4 style={{marginTop: '1em'}}>Membre n°3</h4>
|
||||
<MembreInfo role="m3"/>
|
||||
|
||||
<div className="mb-3">
|
||||
<div className="mb-3" style={{marginTop: '1em'}}>
|
||||
<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 :
|
||||
@@ -126,13 +127,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/>
|
||||
aria-describedby="basic-addon1" required defaultValue="Mesnie"/>
|
||||
</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)}/>
|
||||
onChange={e => setSiren(e.target.value)} defaultValue={500213731}/>
|
||||
<button className="btn btn-outline-secondary" type="button" id="button-addon2"
|
||||
onClick={fetchSiren}>Rechercher
|
||||
</button>
|
||||
@@ -173,36 +174,65 @@ function AssoInfo() {
|
||||
}
|
||||
|
||||
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>
|
||||
const [switchOn, setSwitchOn] = useState(false);
|
||||
|
||||
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>
|
||||
<option>Sélectionner...</option>
|
||||
<option value="PRESIDENT">Président</option>
|
||||
<option value="TRESORIER">Trésorier</option>
|
||||
<option value="SECRETAIRE">Secrétaire</option>
|
||||
<option value="VPRESIDENT">Vise-Président</option>
|
||||
<option value="VTRESORIER">Vise-Trésorier</option>
|
||||
<option value="VSECRETAIRE">Vise-Secrétaire</option>
|
||||
<option value="MEMBREBUREAU">Membre du bureau</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<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/>
|
||||
<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/>
|
||||
<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/>
|
||||
<label htmlFor="floatingInput">Email</label>
|
||||
</div>
|
||||
</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 className="input-group mb-3">
|
||||
<label className="input-group-text" htmlFor="inputGroupSelect01">Dispose déjà d'une licence</label>
|
||||
<div className="input-group-text">
|
||||
<input type="checkbox" id="inputGroupSelect01" className="form-check-input mt-0"
|
||||
checked={switchOn} onChange={() => setSwitchOn(!switchOn)}/>
|
||||
</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>
|
||||
{switchOn &&
|
||||
<div className="col-sm-3">
|
||||
<div className="form-floating">
|
||||
<input type="number" className="form-control" id="floatingInput" placeholder="N° Licence"
|
||||
name={role + "_licence"}/>
|
||||
<label htmlFor="floatingInput">N° Licence</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-sm-3">
|
||||
<div>OU</div>
|
||||
<div className="form-floating">
|
||||
<input type="number" className="form-control" id="floatingInput" placeholder="N° Licence"
|
||||
name={role + "-licence"}/>
|
||||
<label htmlFor="floatingInput">N° Licence</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</>
|
||||
}
|
||||
|
||||
export function DemandeAffOk() {
|
||||
|
||||
@@ -9,6 +9,7 @@ import {Checkbox} from "../components/MemberCustomFiels.jsx";
|
||||
import axios from "axios";
|
||||
import {apiAxios} from "../utils/Tools.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {SearchBar} from "../components/SearchBar.jsx";
|
||||
|
||||
const removeDiacritics = str => {
|
||||
return str
|
||||
@@ -106,41 +107,6 @@ export function MemberList({source}) {
|
||||
</>
|
||||
}
|
||||
|
||||
function SearchBar({search}) {
|
||||
const [searchInput, setSearchInput] = useState("");
|
||||
|
||||
const handelChange = (e) => {
|
||||
setSearchInput(e.target.value);
|
||||
}
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
searchMember();
|
||||
}
|
||||
}
|
||||
|
||||
const searchMember = () => {
|
||||
search(removeDiacritics(searchInput));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const delayDebounceFn = setTimeout(() => {
|
||||
searchMember();
|
||||
}, 750)
|
||||
return () => clearTimeout(delayDebounceFn)
|
||||
}, [searchInput])
|
||||
|
||||
return <div className="mb-3">
|
||||
<div className="input-group mb-3">
|
||||
<input type="text" className="form-control" placeholder="Rechercher..." aria-label="Rechercher..."
|
||||
aria-describedby="button-addon2" value={searchInput} onChange={handelChange} onKeyDown={handleKeyDown}/>
|
||||
<button className="btn btn-outline-secondary" type="button" id="button-addon2"
|
||||
onClick={searchMember}>Rechercher
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
function MakeCentralPanel({data, visibleMember, navigate, showLicenceState, page}) {
|
||||
const pages = []
|
||||
for (let i = 1; i <= data.page_count; i++) {
|
||||
|
||||
@@ -4,6 +4,10 @@ import {LoadingProvider} from "../../hooks/useLoading.jsx";
|
||||
import {MemberList} from "../MemberList.jsx";
|
||||
import {MemberPage} from "./member/MemberPage.jsx";
|
||||
import {NewMemberPage} from "./member/NewMemberPage.jsx";
|
||||
import {ClubList} from "./club/ClubList.jsx";
|
||||
import {AffiliationReqPage} from "./affiliation/AffiliationReqPage.jsx";
|
||||
import {NewClubPage} from "./club/NewClubPage.jsx";
|
||||
import {ClubPage} from "./club/ClubPage.jsx";
|
||||
|
||||
export function AdminRoot() {
|
||||
return <>
|
||||
@@ -28,6 +32,22 @@ export function getAdminChildren() {
|
||||
path: 'member/new',
|
||||
element: <NewMemberPage/>
|
||||
},
|
||||
{
|
||||
path: 'club',
|
||||
element: <ClubList/>
|
||||
},
|
||||
{
|
||||
path: 'club/:id',
|
||||
element: <ClubPage/>
|
||||
},
|
||||
{
|
||||
path: 'affiliation/request',
|
||||
element: <AffiliationReqPage/>
|
||||
},
|
||||
{
|
||||
path: 'club/new',
|
||||
element: <NewClubPage/>
|
||||
},
|
||||
{
|
||||
path: 'b',
|
||||
element: <div>Admin B</div>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
export function AffiliationReqPage() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return <>
|
||||
<h2>Page affiliation</h2>
|
||||
<button type="button" className="btn btn-link" onClick={() => navigate("/admin/affiliation")}>
|
||||
« retour
|
||||
</button>
|
||||
<div>
|
||||
<div className="row">
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
187
src/main/webapp/src/pages/admin/club/AffiliationCard.jsx
Normal file
187
src/main/webapp/src/pages/admin/club/AffiliationCard.jsx
Normal file
@@ -0,0 +1,187 @@
|
||||
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 {faPen} from "@fortawesome/free-solid-svg-icons";
|
||||
import {AxiosError} from "../../../components/AxiosError.jsx";
|
||||
import {CheckField, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
import {apiAxios, getSaison} from "../../../utils/Tools.js";
|
||||
import {Input} from "../../../components/Input.jsx";
|
||||
import {toast} from "react-toastify";
|
||||
|
||||
function affiliationReducer(affiliation, action) {
|
||||
switch (action.type) {
|
||||
case 'ADD':
|
||||
return [
|
||||
...affiliation,
|
||||
action.payload
|
||||
]
|
||||
case 'REMOVE':
|
||||
return affiliation.filter(affiliation => affiliation.id !== action.payload)
|
||||
case 'UPDATE_OR_ADD':
|
||||
const index = affiliation.findIndex(affiliation => affiliation.id === action.payload.id)
|
||||
if (index === -1) {
|
||||
return [
|
||||
...affiliation,
|
||||
action.payload
|
||||
]
|
||||
} else {
|
||||
affiliation[index] = action.payload
|
||||
return [...affiliation]
|
||||
}
|
||||
case 'SORT':
|
||||
return affiliation.sort((a, b) => b.saison - a.saison)
|
||||
default:
|
||||
throw new Error()
|
||||
}
|
||||
}
|
||||
|
||||
export function AffiliationCard({clubData}) {
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error} = useFetch(`/affiliation/${clubData.id}`, setLoading, 1)
|
||||
|
||||
const [modalAffiliation, setModal] = useState({id: -1, club: clubData.id})
|
||||
const [affiliations, dispatch] = useReducer(affiliationReducer, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!data) return
|
||||
for (const dataKey of data) {
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: dataKey})
|
||||
}
|
||||
dispatch({type: 'SORT'})
|
||||
}, [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" data-bs-toggle="modal" data-bs-target="#AffiliationModal"
|
||||
onClick={_ => setModal({id: -1, club: clubData.id})}>Ajouter
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<ul className="list-group">
|
||||
{affiliations.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={faPen}/></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} dispatch={dispatch}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function sendAffiliation(event, dispatch) {
|
||||
event.preventDefault();
|
||||
|
||||
const formData = new FormData(event.target);
|
||||
toast.promise(
|
||||
apiAxios.post(`/affiliation/${formData.get('membre')}`, formData), // TODO
|
||||
{
|
||||
pending: "Enregistrement de l'affiliation en cours",
|
||||
success: "Affiliation enregistrée avec succès 🎉",
|
||||
error: "Échec de l'enregistrement de l'affiliation 😕"
|
||||
}
|
||||
).then(data => {
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: data.data})
|
||||
dispatch({type: 'SORT'})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function removeAffiliation(id, dispatch) {
|
||||
toast.promise(
|
||||
apiAxios.delete(`/affiliation/${id}`),
|
||||
{
|
||||
pending: "Suppression de l'affiliation en cours",
|
||||
success: "Affiliation supprimée avec succès 🎉",
|
||||
error: "Échec de la suppression de l'affiliation 😕"
|
||||
}
|
||||
).then(_ => {
|
||||
dispatch({type: 'REMOVE', payload: id})
|
||||
})
|
||||
}
|
||||
|
||||
function ModalContent({affiliation, dispatch}) {
|
||||
const [saison, setSaison] = useState(0)
|
||||
const [validate, setValidate] = useState(false)
|
||||
const [isNew, setNew] = useState(true)
|
||||
const setSeason = (event) => {
|
||||
setSaison(Number(event.target.value))
|
||||
}
|
||||
const handleValidateChange = (event) => {
|
||||
setValidate(event.target.value === 'true');
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (affiliation.id !== -1) {
|
||||
setNew(false)
|
||||
setSaison(affiliation.saison)
|
||||
setValidate(affiliation.validate)
|
||||
} else {
|
||||
setNew(true)
|
||||
setSaison(getSaison())
|
||||
setValidate(false)
|
||||
}
|
||||
}, [affiliation]);
|
||||
|
||||
return <form onSubmit={e => sendAffiliation(e, dispatch)}>
|
||||
<input name="id" value={affiliation.id} readOnly hidden/>
|
||||
<input name="membre" value={affiliation.membre} readOnly hidden/>
|
||||
<div className="modal-header">
|
||||
<h1 className="modal-title fs-5" id="AffiliationModalLabel">Edition 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">
|
||||
{isNew
|
||||
? <input type="number" className="form-control" placeholder="Saison" name="saison"
|
||||
aria-label="Saison" aria-describedby="basic-addon2" value={saison} onChange={setSeason}/>
|
||||
: <><span className="input-group-text" id="basic-addon2">{saison}</span>
|
||||
<input name="saison" value={saison} readOnly hidden/></>}
|
||||
<span className="input-group-text" id="basic-addon2">-</span>
|
||||
<span className="input-group-text" id="basic-addon2">{saison + 1}</span>
|
||||
</div>
|
||||
<RadioGroupeOnOff name="validate" text="Validation de l'affiliation" value={validate}
|
||||
onChange={handleValidateChange}/>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal">Enregistrer</button>
|
||||
<button type="reset" className="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
|
||||
{isNew || <button type="button" className="btn btn-danger" data-bs-dismiss="modal"
|
||||
onClick={() => removeAffiliation(affiliation.id, dispatch)}>Supprimer</button>}
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
function RadioGroupeOnOff({value, onChange, name, text}) {
|
||||
return <div className="btn-group input-group mb-3 justify-content-md-center" role="group"
|
||||
aria-label="Basic radio toggle button group">
|
||||
<span className="input-group-text">{text}</span>
|
||||
<input type="radio" className="btn-check" id={"btnradio1" + name} autoComplete="off"
|
||||
value="false" checked={value === false} onChange={onChange}/>
|
||||
<label className="btn btn-outline-primary" htmlFor={"btnradio1" + name}>Non</label>
|
||||
<input type="radio" className="btn-check" name={name} id={"btnradio2" + name} autoComplete="off"
|
||||
value="true" checked={value === true} onChange={onChange}/>
|
||||
<label className="btn btn-outline-primary" htmlFor={"btnradio2" + name}>Oui</label>
|
||||
</div>;
|
||||
}
|
||||
192
src/main/webapp/src/pages/admin/club/ClubList.jsx
Normal file
192
src/main/webapp/src/pages/admin/club/ClubList.jsx
Normal file
@@ -0,0 +1,192 @@
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import {useEffect, useState} from "react";
|
||||
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
||||
import {useFetch} from "../../../hooks/useFetch.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {apiAxios} from "../../../utils/Tools.js";
|
||||
import {AxiosError} from "../../../components/AxiosError.jsx";
|
||||
import {Checkbox} from "../../../components/MemberCustomFiels.jsx";
|
||||
import {ThreeDots} from "react-loader-spinner";
|
||||
import {SearchBar} from "../../../components/SearchBar.jsx";
|
||||
|
||||
export function ClubList() {
|
||||
const {hash} = useLocation();
|
||||
const navigate = useNavigate();
|
||||
let page = Number(hash.substring(1));
|
||||
page = (page > 0) ? page : 1;
|
||||
|
||||
const [clubData, setClubData] = useState([]);
|
||||
const [affiliationData, setAffiliationData] = useState([]);
|
||||
const [showAffiliationState, setShowAffiliationState] = useState(false);
|
||||
const [countryFilter, setCountryFilter] = useState("");
|
||||
const [lastSearch, setLastSearch] = useState("");
|
||||
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error, refresh} = useFetch(`/club/find?page=${page}`, setLoading, 1)
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
refresh(`/club/find?page=${page}&search=${lastSearch}&country=${countryFilter}`);
|
||||
}, [hash, countryFilter]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!data)
|
||||
return;
|
||||
const data2 = [];
|
||||
for (const e of data.result) {
|
||||
data2.push({
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
country: e.country,
|
||||
shieldURL: e.shieldURL,
|
||||
no_affiliation: e.no_affiliation,
|
||||
affiliation: showAffiliationState ? affiliationData.find(licence => licence.club === e.id) : null
|
||||
})
|
||||
}
|
||||
setClubData(data2);
|
||||
}, [data, affiliationData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showAffiliationState)
|
||||
return;
|
||||
|
||||
toast.promise(
|
||||
apiAxios.get(`/affiliation/current`),
|
||||
{
|
||||
pending: "Chargement des affiliation...",
|
||||
success: "Affiliation chargées",
|
||||
error: "Impossible de charger les affiliations"
|
||||
})
|
||||
.then(data => {
|
||||
setAffiliationData(data.data);
|
||||
});
|
||||
}, [showAffiliationState]);
|
||||
|
||||
const search = (search) => {
|
||||
if (search === lastSearch)
|
||||
return;
|
||||
setLastSearch(search);
|
||||
refresh(`/club/find?page=${page}&search=${search}&country=${countryFilter}`);
|
||||
}
|
||||
|
||||
return <>
|
||||
<h2>Club</h2>
|
||||
<div>
|
||||
<div className="row">
|
||||
<div className="col-lg-9">
|
||||
<SearchBar search={search}/>
|
||||
{data
|
||||
? <MakeCentralPanel data={data} visibleclub={clubData} navigate={navigate} showLicenceState={showAffiliationState}
|
||||
page={page}/>
|
||||
: error
|
||||
? <AxiosError error={error}/>
|
||||
: <Def/>
|
||||
}
|
||||
</div>
|
||||
<div className="col-lg-3">
|
||||
<div className="mb-4">
|
||||
<button className="btn btn-primary" onClick={() => navigate("../affiliation/request")}>Demande en cours</button>
|
||||
<button className="btn btn-primary" onClick={() => navigate("new")}>Ajouter une affiliation</button>
|
||||
</div>
|
||||
<div className="card mb-4">
|
||||
<div className="card-header">Filtre</div>
|
||||
<div className="card-body">
|
||||
<FiltreBar showAffiliationState={showAffiliationState} setShowLAffiliationState={setShowAffiliationState} data={data}
|
||||
countryFilter={countryFilter} setCountryFilter={setCountryFilter}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
function MakeCentralPanel({data, visibleclub, navigate, showAffiliationState, page}) {
|
||||
const pages = []
|
||||
for (let i = 1; i <= data.page_count; i++) {
|
||||
pages.push(<li key={i} className={"page-item " + ((page === i) ? "active" : "")}>
|
||||
<span className="page-link" onClick={() => navigate("#" + i)}>{i}</span>
|
||||
</li>);
|
||||
}
|
||||
|
||||
return <>
|
||||
<div className="mb-4">
|
||||
<small>Ligne {((page - 1) * data.page_size) + 1} à {
|
||||
(page * data.page_size > data.result_count) ? data.result_count : (page * data.page_size)} (page {page} sur {data.page_count})</small>
|
||||
<div className="list-group">
|
||||
{visibleclub.map(club => (<MakeRow key={club.id} club={club} navigate={navigate} showAffiliationState={showAffiliationState}/>))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<nav aria-label="Page navigation">
|
||||
<ul className="pagination justify-content-center">
|
||||
<li className={"page-item" + ((page <= 1) ? " disabled" : "")}>
|
||||
<span className="page-link" onClick={() => navigate("#" + (page - 1))}>«</span></li>
|
||||
{pages}
|
||||
<li className={"page-item" + ((page >= data.page_count) ? " disabled" : "")}>
|
||||
<span className="page-link" onClick={() => navigate("#" + (page + 1))}>»</span></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
function MakeRow({club, showAffiliationState, navigate}) {
|
||||
const rowContent = <>
|
||||
<div className="row">
|
||||
<span className="col-auto">{String(club.no_affiliation).padStart(5, '0')}</span>
|
||||
<div className="ms-2 col-auto">
|
||||
<div className="fw-bold">{club.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
<small>{club.country}</small>
|
||||
</>
|
||||
|
||||
if (showAffiliationState && club.affiliation != null) {
|
||||
return <div
|
||||
className={"list-group-item d-flex justify-content-between align-items-start list-group-item-action list-group-item-"
|
||||
+ (club.affiliation.validate ? "success" : "warning")}
|
||||
onClick={() => navigate("" + club.id)}>{rowContent}</div>
|
||||
} else {
|
||||
return <div className="list-group-item d-flex justify-content-between align-items-start list-group-item-action"
|
||||
onClick={() => navigate("" + club.id)}>
|
||||
{rowContent}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
let allCountry = []
|
||||
|
||||
function FiltreBar({showAffiliationState, setShowAffiliationState, data, countryFilter, setCountryFilter}) {
|
||||
useEffect(() => {
|
||||
if (!data)
|
||||
return;
|
||||
allCountry.push(...data.result.map((e) => e.club?.name))
|
||||
allCountry = allCountry.filter((value, index, self) => self.indexOf(value) === index).filter(value => value != null).sort()
|
||||
}, [data]);
|
||||
|
||||
return <div>
|
||||
<div className="mb-3">
|
||||
<Checkbox value={showAffiliationState} onChange={setShowAffiliationState} label="Afficher l'état des affiliation"/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<select className="form-select" value={countryFilter} onChange={event => setCountryFilter(event.target.value)}>
|
||||
<option value="">--- tout les pays ---</option>
|
||||
{allCountry && allCountry.map((value, index) => {
|
||||
return <option key={index} value={value}>{value}</option>
|
||||
})
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
function Def() {
|
||||
return <div className="list-group">
|
||||
<li className="list-group-item"><ThreeDots/></li>
|
||||
<li className="list-group-item"><ThreeDots/></li>
|
||||
<li className="list-group-item"><ThreeDots/></li>
|
||||
<li className="list-group-item"><ThreeDots/></li>
|
||||
<li className="list-group-item"><ThreeDots/></li>
|
||||
</div>
|
||||
}
|
||||
128
src/main/webapp/src/pages/admin/club/ClubPage.jsx
Normal file
128
src/main/webapp/src/pages/admin/club/ClubPage.jsx
Normal file
@@ -0,0 +1,128 @@
|
||||
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 {CheckField, CountryList, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
|
||||
import {MapContainer, Marker, Popup, TileLayer, useMap} from 'react-leaflet'
|
||||
|
||||
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 membre</h2>
|
||||
<button type="button" className="btn btn-link" onClick={() => navigate("/admin/club")}>
|
||||
« retour
|
||||
</button>
|
||||
{data
|
||||
? <div>
|
||||
<div className="row">
|
||||
<div className="col-lg-8">
|
||||
<LoadingProvider><InformationForm data={data}/></LoadingProvider>
|
||||
</div>
|
||||
<div className="col-lg-4">
|
||||
<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 compte
|
||||
</button>
|
||||
</div>
|
||||
<ConfirmDialog title="Supprimer le compte" message="Êtes-vous sûr de vouloir supprimer ce compte ?"
|
||||
onConfirm={handleRm}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
: error && <AxiosError error={error}/>
|
||||
}
|
||||
</>
|
||||
}
|
||||
|
||||
function InformationForm({data}) {
|
||||
return <div className="card mb-4">
|
||||
<div className="card-header">Licence 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}/>
|
||||
<TextField name="siret" text="SIRET" value={data.siret} type="number"/>
|
||||
<TextField name="rna" text="RNA" value={data.rna}/>
|
||||
<CountryList name="country" text="Pays" value={data.country}/>
|
||||
<img
|
||||
src={`${vite_url}/api/club/${data.id}/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="url_photo">Blason</label>
|
||||
<input type="file" className="form-control" id="url_photo" name="url_photo"
|
||||
accept=".jpg,.jpeg,.gif,.png,.svg"/>
|
||||
</div>
|
||||
<div className="form-text" id="url_photo">Laissez vide pour ne rien changer.</div>
|
||||
</div>
|
||||
|
||||
<TextField name="contact" text="Contact" value={data.contact}/>
|
||||
<TextField name="training_location" text="Lieux d'entrainement" value={data.training_location}/>
|
||||
<TextField name="training_day_time" text="Horaire d'entrainement" value={data.training_day_time}/>
|
||||
<TextField name="contact_intern" text="Contact" value={"contact_intern"}/>
|
||||
<CheckField name="international" text="Club international" value={data.international}/>
|
||||
|
||||
<MainMap/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
// https://annuaire-entreprises.data.gouv.fr/entreprise/la-mesnie-des-chevaliers-de-st-georges-et-de-st-michel-500213731
|
||||
const position = [51.505, -0.09]
|
||||
function MainMap() {
|
||||
function handleReturnCurrentPosition() {
|
||||
console.log("I have clicked return button!!");
|
||||
//const newCurrentPositionId = uuidv4();
|
||||
//setReturnCurrentPosition(newCurrentPositionId);
|
||||
//console.log(newCurrentPositionId);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<MapContainer center={position} zoom={13} scrollWheelZoom={false} style={{height: "30em", width: "50em"}}>
|
||||
<TileLayer
|
||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
/>
|
||||
<Marker position={position}>
|
||||
<Popup>
|
||||
A pretty CSS3 popup. <br/> Easily customizable.
|
||||
</Popup>
|
||||
</Marker>
|
||||
</MapContainer>
|
||||
<button className="btn btn-primary" onClick={handleReturnCurrentPosition}>Return current position</button>
|
||||
<SearchBarMap/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function SearchBarMap() {
|
||||
return <>
|
||||
</>
|
||||
}
|
||||
16
src/main/webapp/src/pages/admin/club/NewClubPage.jsx
Normal file
16
src/main/webapp/src/pages/admin/club/NewClubPage.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
export function NewClubPage() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return <>
|
||||
<h2>Page affiliation</h2>
|
||||
<button type="button" className="btn btn-link" onClick={() => navigate("/admin/affiliation")}>
|
||||
« retour
|
||||
</button>
|
||||
<div>
|
||||
<div className="row">
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
||||
import {apiAxios} from "../../../utils/Tools.js";
|
||||
import {toast} from "react-toastify";
|
||||
import imageCompression from "browser-image-compression";
|
||||
import {BirthDayField, OptionField, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
import {BirthDayField, CountryList, OptionField, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
import {ClubSelect} from "../../../components/ClubSelect.jsx";
|
||||
|
||||
export function addPhoto(event, formData, send) {
|
||||
@@ -74,8 +74,7 @@ export function InformationForm({data}) {
|
||||
type="email"/>
|
||||
<OptionField name="genre" text="Genre" value={data.genre}
|
||||
values={{NA: 'N/A', H: 'H', F: 'F'}}/>
|
||||
<OptionField name="country" text="Pays" value={data.country}
|
||||
values={{NA: 'Sélectionner...', fr: 'FR', es: 'ES', be: 'BE'}}/>
|
||||
<CountryList name="country" text="Pays" value={data.country}/>
|
||||
<BirthDayField inti_date={data.birth_date ? data.birth_date.split('T')[0] : ''}
|
||||
inti_category={data.categorie}/>
|
||||
<div className="row">
|
||||
@@ -86,7 +85,11 @@ export function InformationForm({data}) {
|
||||
MEMBRE: 'Membre',
|
||||
PRESIDENT: 'Président',
|
||||
TRESORIER: 'Trésorier',
|
||||
SECRETAIRE: 'Secrétaire'
|
||||
SECRETAIRE: 'Secrétaire',
|
||||
VPRESIDENT: 'Vise-Président',
|
||||
VTRESORIER: 'Vise-Trésorier',
|
||||
VSECRETAIRE: 'Vise-Secrétaire',
|
||||
MEMBREBUREAU: 'Membre bureau'
|
||||
}}/>
|
||||
<OptionField name="grade_arbitrage" text="Grade d'arbitrage" value={data.grade_arbitrage}
|
||||
values={{NA: 'N/A', ASSESSEUR: 'Assesseur', ARBITRE: 'Arbitre'}}/>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
||||
import {apiAxios} from "../../../utils/Tools.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {BirthDayField, OptionField, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
import {BirthDayField, CountryList, OptionField, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
import {addPhoto} from "../../admin/member/InformationForm.jsx";
|
||||
|
||||
export function InformationForm({data}) {
|
||||
@@ -52,8 +52,7 @@ export function InformationForm({data}) {
|
||||
type="email"/>
|
||||
<OptionField name="genre" text="Genre" value={data.genre}
|
||||
values={{NA: 'N/A', H: 'H', F: 'F'}}/>
|
||||
<OptionField name="country" text="Pays" value={data.country}
|
||||
values={{NA: 'Sélectionner...', fr: 'FR', es: 'ES', be: 'BE'}}/>
|
||||
<CountryList name="country" text="Pays" value={data.country}/>
|
||||
<BirthDayField inti_date={data.birth_date ? data.birth_date.split('T')[0] : ''}
|
||||
inti_category={data.categorie}/>
|
||||
<OptionField name="role" text="Rôle" value={data.role}
|
||||
@@ -61,7 +60,11 @@ export function InformationForm({data}) {
|
||||
MEMBRE: 'Membre',
|
||||
PRESIDENT: 'Président',
|
||||
TRESORIER: 'Trésorier',
|
||||
SECRETAIRE: 'Secrétaire'
|
||||
SECRETAIRE: 'Secrétaire',
|
||||
VPRESIDENT: 'Vise-Président',
|
||||
VTRESORIER: 'Vise-Trésorier',
|
||||
VSECRETAIRE: 'Vise-Secrétaire',
|
||||
MEMBREBUREAU: 'Membre bureau'
|
||||
}} disabled={true}/>
|
||||
<OptionField name="grade_arbitrage" text="Grade d'arbitrage" value={data.grade_arbitrage}
|
||||
values={{NA: 'N/A', ASSESSEUR: 'Assesseur', ARBITRE: 'Arbitre'}} disabled={true}/>
|
||||
|
||||
Reference in New Issue
Block a user