feat: mePage
This commit is contained in:
parent
8ba3f45215
commit
a58dcdd08e
@ -1,17 +1,15 @@
|
|||||||
package fr.titionfire;
|
package fr.titionfire;
|
||||||
|
|
||||||
import io.quarkus.qute.Template;
|
import io.quarkus.qute.Template;
|
||||||
import io.quarkus.qute.TemplateInstance;
|
import io.smallrye.mutiny.Uni;
|
||||||
|
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
import jakarta.ws.rs.Path;
|
import jakarta.ws.rs.Path;
|
||||||
import jakarta.ws.rs.Produces;
|
import jakarta.ws.rs.Produces;
|
||||||
import jakarta.ws.rs.QueryParam;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
@Path("/some-page")
|
@Path("api/some-page")
|
||||||
public class SomePage {
|
public class SomePage {
|
||||||
|
|
||||||
private final Template page;
|
private final Template page;
|
||||||
@ -22,8 +20,11 @@ public class SomePage {
|
|||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.TEXT_HTML)
|
@Produces(MediaType.TEXT_HTML)
|
||||||
public TemplateInstance get(@QueryParam("name") String name) {
|
public Uni<String> get() {
|
||||||
return page.data("name", name);
|
return Uni.createFrom()
|
||||||
|
.completionStage(() -> page
|
||||||
|
.data("name", "test")
|
||||||
|
.renderAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import fr.titionfire.ffsaf.data.repository.LicenceRepository;
|
|||||||
import fr.titionfire.ffsaf.net2.ServerCustom;
|
import fr.titionfire.ffsaf.net2.ServerCustom;
|
||||||
import fr.titionfire.ffsaf.net2.data.SimpleCombModel;
|
import fr.titionfire.ffsaf.net2.data.SimpleCombModel;
|
||||||
import fr.titionfire.ffsaf.net2.request.SReqComb;
|
import fr.titionfire.ffsaf.net2.request.SReqComb;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.MeData;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.SimpleLicence;
|
||||||
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||||
import fr.titionfire.ffsaf.rest.from.ClubMemberForm;
|
import fr.titionfire.ffsaf.rest.from.ClubMemberForm;
|
||||||
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
||||||
@ -27,6 +29,7 @@ import jakarta.ws.rs.BadRequestException;
|
|||||||
import jakarta.ws.rs.ForbiddenException;
|
import jakarta.ws.rs.ForbiddenException;
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||||
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -265,4 +268,14 @@ public class MembreService {
|
|||||||
StringSimilarity.similarity(m.getLname(), lname) <= 3)
|
StringSimilarity.similarity(m.getLname(), lname) <= 3)
|
||||||
.map(SimpleMembre::fromModel).toList());
|
.map(SimpleMembre::fromModel).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uni<MeData> getMembre(String subject) {
|
||||||
|
MeData meData = new MeData();
|
||||||
|
return repository.find("userId = ?1", subject).firstResult()
|
||||||
|
.invoke(meData::setMembre)
|
||||||
|
.chain(membreModel -> Mutiny.fetch(membreModel.getLicences()))
|
||||||
|
.map(licences -> licences.stream().map(SimpleLicence::fromModel).toList())
|
||||||
|
.invoke(meData::setLicences)
|
||||||
|
.map(__ -> meData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.rest;
|
|||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
import fr.titionfire.ffsaf.domain.service.MembreService;
|
import fr.titionfire.ffsaf.domain.service.MembreService;
|
||||||
|
import fr.titionfire.ffsaf.rest.data.MeData;
|
||||||
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||||
import fr.titionfire.ffsaf.rest.from.ClubMemberForm;
|
import fr.titionfire.ffsaf.rest.from.ClubMemberForm;
|
||||||
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
||||||
@ -18,17 +19,11 @@ import jakarta.inject.Inject;
|
|||||||
import jakarta.ws.rs.*;
|
import jakarta.ws.rs.*;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import jodd.net.MimeTypes;
|
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@Authenticated
|
@Authenticated
|
||||||
@ -198,6 +193,14 @@ public class CombEndpoints {
|
|||||||
return membreService.delete(id, idToken);
|
return membreService.delete(id, idToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("me")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<MeData> getMe() {
|
||||||
|
return membreService.getMembre(idToken.getSubject());
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{id}/photo")
|
@Path("{id}/photo")
|
||||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@Path("api/countries")
|
||||||
|
public class CountriesEndpoints {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{lang}/{code}")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Uni<HashMap<String, String>> getCountries(@PathParam("lang") String lang, @PathParam("code") String code) {
|
||||||
|
Locale locale = new Locale(lang, code);
|
||||||
|
return Uni.createFrom().item(new HashMap<String, String>())
|
||||||
|
.invoke(map -> {
|
||||||
|
String[] locales = Locale.getISOCountries();
|
||||||
|
for (String countryCode : locales) {
|
||||||
|
if (countryCode.equals("AN"))
|
||||||
|
continue;
|
||||||
|
Locale obj = new Locale("", countryCode);
|
||||||
|
map.put(countryCode, obj.getDisplayName(locale));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/main/java/fr/titionfire/ffsaf/rest/data/MeData.java
Normal file
45
src/main/java/fr/titionfire/ffsaf/rest/data/MeData.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest.data;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@ToString
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public class MeData {
|
||||||
|
private long id;
|
||||||
|
private String lname = "";
|
||||||
|
private String fname = "";
|
||||||
|
private String categorie;
|
||||||
|
private String club;
|
||||||
|
private String genre;
|
||||||
|
private int licence;
|
||||||
|
private String country;
|
||||||
|
private Date birth_date;
|
||||||
|
private String email;
|
||||||
|
private String role;
|
||||||
|
private String grade_arbitrage;
|
||||||
|
private List<SimpleLicence> licences;
|
||||||
|
|
||||||
|
public void setMembre(MembreModel membreModel) {
|
||||||
|
this.id = membreModel.getId();
|
||||||
|
this.lname = membreModel.getLname();
|
||||||
|
this.fname = membreModel.getFname();
|
||||||
|
this.categorie = membreModel.getCategorie().getName();
|
||||||
|
this.club = membreModel.getClub().getName();
|
||||||
|
this.genre = membreModel.getGenre().str;
|
||||||
|
this.licence = membreModel.getLicence();
|
||||||
|
this.country = membreModel.getCountry();
|
||||||
|
this.birth_date = membreModel.getBirth_date();
|
||||||
|
this.email = membreModel.getEmail();
|
||||||
|
this.role = membreModel.getRole().str;
|
||||||
|
this.grade_arbitrage = membreModel.getGrade_arbitrage().str;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,4 +30,20 @@ public enum Categorie {
|
|||||||
case VETERAN2 -> BUNDLE.getString("Cat.VETERAN2");
|
case VETERAN2 -> BUNDLE.getString("Cat.VETERAN2");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return switch (this){
|
||||||
|
case SUPER_MINI -> "Super Mini";
|
||||||
|
case MINI_POUSSIN -> "Mini Poussin";
|
||||||
|
case POUSSIN -> "Poussin";
|
||||||
|
case BENJAMIN -> "Benjamin";
|
||||||
|
case MINIME -> "Minime";
|
||||||
|
case CADET -> "Cadet";
|
||||||
|
case JUNIOR -> "Junior";
|
||||||
|
case SENIOR1 -> "Senior 1";
|
||||||
|
case SENIOR2 -> "Senior 2";
|
||||||
|
case VETERAN1 -> "Vétéran 1";
|
||||||
|
case VETERAN2 -> "Vétéran 2";
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,18 @@
|
|||||||
package fr.titionfire.ffsaf.utils;
|
package fr.titionfire.ffsaf.utils;
|
||||||
|
|
||||||
public enum Genre {
|
public enum Genre {
|
||||||
H, F, NA
|
H("Homme"),
|
||||||
|
F("Femme"),
|
||||||
|
NA("Non définie");
|
||||||
|
|
||||||
|
public final String str;
|
||||||
|
|
||||||
|
Genre(String name) {
|
||||||
|
this.str = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,14 +5,14 @@ public enum GradeArbitrage {
|
|||||||
ASSESSEUR("Assesseur"),
|
ASSESSEUR("Assesseur"),
|
||||||
ARBITRE("Arbitre");
|
ARBITRE("Arbitre");
|
||||||
|
|
||||||
public final String name;
|
public final String str;
|
||||||
|
|
||||||
GradeArbitrage(String name) {
|
GradeArbitrage(String name) {
|
||||||
this.name = name;
|
this.str = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,16 +13,16 @@ public enum RoleAsso {
|
|||||||
VTRESORIER("Vise-Trésorier", 2),
|
VTRESORIER("Vise-Trésorier", 2),
|
||||||
MEMBREBUREAU("Membre bureau", 1);
|
MEMBREBUREAU("Membre bureau", 1);
|
||||||
|
|
||||||
public final String name;
|
public final String str;
|
||||||
public final int level;
|
public final int level;
|
||||||
|
|
||||||
RoleAsso(String name, int level) {
|
RoleAsso(String name, int level) {
|
||||||
this.name = name;
|
this.str = name;
|
||||||
this.level = level;
|
this.level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import './App.css'
|
|||||||
import 'react-toastify/dist/ReactToastify.css';
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
import {ClubRoot, getClubChildren} from "./pages/club/ClubRoot.jsx";
|
import {ClubRoot, getClubChildren} from "./pages/club/ClubRoot.jsx";
|
||||||
import {DemandeAff, DemandeAffOk} from "./pages/DemandeAff.jsx";
|
import {DemandeAff, DemandeAffOk} from "./pages/DemandeAff.jsx";
|
||||||
|
import {MePage} from "./pages/MePage.jsx";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@ -45,6 +46,10 @@ const router = createBrowserRouter([
|
|||||||
element: <DemandeAffOk/>
|
element: <DemandeAffOk/>
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'me',
|
||||||
|
element: <MePage/>
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -82,7 +87,7 @@ function Root() {
|
|||||||
<div className="container my-4">
|
<div className="container my-4">
|
||||||
<Outlet/>
|
<Outlet/>
|
||||||
<ToastContainer
|
<ToastContainer
|
||||||
position="top-right"
|
position="top-center"
|
||||||
autoClose={5000}
|
autoClose={5000}
|
||||||
hideProgressBar={false}
|
hideProgressBar={false}
|
||||||
newestOnTop={false}
|
newestOnTop={false}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {getCategoryFormBirthDate} from "../utils/Tools.js";
|
import {getCategoryFormBirthDate} from "../utils/Tools.js";
|
||||||
|
import {useCountries} from "../hooks/useCountries.jsx";
|
||||||
|
|
||||||
export function BirthDayField({inti_date, inti_category, required = true}) {
|
export function BirthDayField({inti_date, inti_category, required = true}) {
|
||||||
const [date, setDate] = useState(inti_date)
|
const [date, setDate] = useState(inti_date)
|
||||||
@ -26,7 +27,7 @@ export function BirthDayField({inti_date, inti_category, required = true}) {
|
|||||||
<div className="input-group mb-3">
|
<div className="input-group mb-3">
|
||||||
<span className="input-group-text" id="category">Catégorie</span>
|
<span className="input-group-text" id="category">Catégorie</span>
|
||||||
<input type="text" className="form-control" placeholder="" name="category"
|
<input type="text" className="form-control" placeholder="" name="category"
|
||||||
aria-label="category" value={category? category : ""} aria-describedby="category"
|
aria-label="category" value={category ? category : ""} aria-describedby="category"
|
||||||
disabled/>
|
disabled/>
|
||||||
{canUpdate && <button className="btn btn-outline-secondary" type="button" id="button-addon1"
|
{canUpdate && <button className="btn btn-outline-secondary" type="button" id="button-addon1"
|
||||||
onClick={updateCat}>Mettre à jours</button>}
|
onClick={updateCat}>Mettre à jours</button>}
|
||||||
@ -63,11 +64,28 @@ export function RoleList({name, text, value, disabled = false}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function CountryList({name, text, value, values = undefined, disabled = false}) {
|
export function CountryList({name, text, value, values = undefined, disabled = false}) {
|
||||||
if (values === undefined){
|
const country = useCountries('fr')
|
||||||
values = {NA: 'Sélectionner...', fr: 'FR', es: 'ES', be: 'BE'}
|
const [value_, setValue] = useState(value)
|
||||||
|
|
||||||
|
if (values === undefined) {
|
||||||
|
values = {...country}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <OptionField name={name} text={text} value={value} values={values} disabled={disabled}/>
|
return <div className="row">
|
||||||
|
<div className="input-group mb-3">
|
||||||
|
<label className="input-group-text" id={name}>{text}</label>
|
||||||
|
<select className="form-select" id={name} name={name} value={value_} required disabled={disabled}
|
||||||
|
onChange={e => setValue(e.target.value)}>
|
||||||
|
{Object.keys(values).sort((a, b) => {
|
||||||
|
if (a < b) return -1
|
||||||
|
if (a > b) return 1
|
||||||
|
return 0
|
||||||
|
}).map((key, _) => {
|
||||||
|
return (<option key={key} value={key}>{values[key]}</option>)
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TextField({name, text, value, placeholder, type = "text", disabled = false, required = true}) {
|
export function TextField({name, text, value, placeholder, type = "text", disabled = false, required = true}) {
|
||||||
|
|||||||
@ -80,11 +80,23 @@ function AdminMenu() {
|
|||||||
function LoginMenu() {
|
function LoginMenu() {
|
||||||
const {is_authenticated} = useAuth()
|
const {is_authenticated} = useAuth()
|
||||||
|
|
||||||
return <li className="nav-item">
|
return <>
|
||||||
{!is_authenticated ? (
|
{!is_authenticated ?
|
||||||
|
<li className="nav-item">
|
||||||
<div className="nav-link" onClick={() => login()}>Connexion</div>
|
<div className="nav-link" onClick={() => login()}>Connexion</div>
|
||||||
) : (
|
|
||||||
<div className="nav-link" onClick={() => logout()}>Déconnexion</div>
|
|
||||||
)}
|
|
||||||
</li>
|
</li>
|
||||||
|
:
|
||||||
|
<li className="nav-item dropdown">
|
||||||
|
<div className="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Mon compte
|
||||||
|
</div>
|
||||||
|
<ul className="dropdown-menu">
|
||||||
|
<li className="nav-item"><NavLink className="nav-link" to="/me">Mon espace</NavLink></li>
|
||||||
|
<li className="nav-item">
|
||||||
|
<div className="nav-link" onClick={() => logout()}>Déconnexion</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
24
src/main/webapp/src/hooks/useCountries.jsx
Normal file
24
src/main/webapp/src/hooks/useCountries.jsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {apiAxios} from "../utils/Tools.js";
|
||||||
|
|
||||||
|
const countries = {}
|
||||||
|
|
||||||
|
export function useCountries(country = 'fr') {
|
||||||
|
const [out, setOut] = useState(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (countries[country] === undefined) {
|
||||||
|
console.log('fetch')
|
||||||
|
apiAxios.get(`/countries/${country}/${country}`).then(data => {
|
||||||
|
console.log(data.data)
|
||||||
|
countries[country] = data.data
|
||||||
|
setOut(data.data)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setOut(countries[country])
|
||||||
|
}
|
||||||
|
}, [country]);
|
||||||
|
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
115
src/main/webapp/src/pages/MePage.jsx
Normal file
115
src/main/webapp/src/pages/MePage.jsx
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import {LoadingProvider, useLoadingSwitcher} from "../hooks/useLoading.jsx";
|
||||||
|
import {AxiosError} from "../components/AxiosError.jsx";
|
||||||
|
import {useFetch} from "../hooks/useFetch.js";
|
||||||
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
|
import {
|
||||||
|
faCalendarDay,
|
||||||
|
faEnvelope, faFlag,
|
||||||
|
faInfoCircle,
|
||||||
|
faMars,
|
||||||
|
faMarsAndVenus,
|
||||||
|
faUser,
|
||||||
|
faUserGroup,
|
||||||
|
faVenus
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
|
|
||||||
|
export function MePage() {
|
||||||
|
const setLoading = useLoadingSwitcher()
|
||||||
|
const {data, error} = useFetch(`/member/me`, setLoading, 1)
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<h1>Mon espace</h1>
|
||||||
|
|
||||||
|
{data
|
||||||
|
? <div>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-lg-4">
|
||||||
|
<PhotoCard data={data}/>
|
||||||
|
</div>
|
||||||
|
<div className="col-lg-8">
|
||||||
|
<InformationForm data={data}/>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-6">
|
||||||
|
<LoadingProvider><LicenceCard userData={data}/></LoadingProvider>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-6">
|
||||||
|
<LoadingProvider><SelectCard/></LoadingProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
: error && <AxiosError error={error}/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LicenceCard({userData}) {
|
||||||
|
return <div className="card mb-4 mb-md-0">
|
||||||
|
<div className="card-header container-fluid">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col">Licence</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="card-body">
|
||||||
|
<ul className="list-group">
|
||||||
|
{userData.licences.map((licence, index) => {
|
||||||
|
return <div key={index}
|
||||||
|
className={"list-group-item d-flex justify-content-between align-items-start list-group-item-" +
|
||||||
|
(licence.validate ? "success" : (licence.certificate ? "warning" : "danger"))}>
|
||||||
|
<div className="me-auto">{licence?.saison}-{licence?.saison + 1}</div>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PhotoCard({data}) {
|
||||||
|
return <div className="card mb-4">
|
||||||
|
<div className="card-header">Licence n°{data.licence}</div>
|
||||||
|
<div className="card-body text-center">
|
||||||
|
<div className="input-group mb-3">
|
||||||
|
<img
|
||||||
|
src={`${vite_url}/api/member/${data.id}/photo`}
|
||||||
|
alt="avatar"
|
||||||
|
className="rounded-circle img-fluid" style={{object_fit: 'contain'}}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectCard() {
|
||||||
|
return <div className="card mb-4 mb-md-0">
|
||||||
|
<div className="card-header">Sélection en équipe de France</div>
|
||||||
|
<div className="card-body">
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function InformationForm({data}) {
|
||||||
|
const style = {marginRight: '0.7em'}
|
||||||
|
|
||||||
|
return <div className="card mb-4">
|
||||||
|
<div className="card-header">Information</div>
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="row mb-2">
|
||||||
|
<p>
|
||||||
|
<FontAwesomeIcon icon={faUser} style={style}/>{data.lname} {data.fname}<br/>
|
||||||
|
<FontAwesomeIcon icon={faEnvelope} style={style}/>{data.email}<br/>
|
||||||
|
{data.genre === 'Homme' && <FontAwesomeIcon icon={faMars} style={style}/>
|
||||||
|
|| data.genre === 'Femme' && <FontAwesomeIcon icon={faVenus} style={style}/>
|
||||||
|
|| <FontAwesomeIcon icon={faMarsAndVenus} style={style}/>}{data.genre}<br/>
|
||||||
|
<FontAwesomeIcon icon={faCalendarDay} style={style}/>{data.birth_date ? data.birth_date.split('T')[0] : ''}<br/>
|
||||||
|
<FontAwesomeIcon icon={faUserGroup} style={style}/>{data.categorie}<br/>
|
||||||
|
<FontAwesomeIcon icon={faFlag} style={style}/>Nationalité : <img src={"/flags/flags_" + data.country.toLowerCase() + ".png"} alt=""/><br/>
|
||||||
|
<FontAwesomeIcon icon={faInfoCircle} style={style}/>Rôle au sien du club : {data.role}<br/>
|
||||||
|
<FontAwesomeIcon icon={faInfoCircle} style={style}/>Formation d'arbitrage : {data.grade_arbitrage}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
@ -62,7 +62,7 @@ function InformationForm() {
|
|||||||
<div className="card-body text-center">
|
<div className="card-body text-center">
|
||||||
|
|
||||||
<TextField name="name" text="Nom*"/>
|
<TextField name="name" text="Nom*"/>
|
||||||
<CountryList name="country" text="Pays*" value={"fr"}/>
|
<CountryList name="country" text="Pays*" value={"FR"}/>
|
||||||
|
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import {useNavigate} from "react-router-dom";
|
|||||||
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
||||||
import {apiAxios} from "../../../utils/Tools.js";
|
import {apiAxios} from "../../../utils/Tools.js";
|
||||||
import {toast} from "react-toastify";
|
import {toast} from "react-toastify";
|
||||||
import {BirthDayField, OptionField, RoleList, TextField} from "../../../components/MemberCustomFiels.jsx";
|
import {BirthDayField, CountryList, OptionField, RoleList, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||||
import {ClubSelect} from "../../../components/ClubSelect.jsx";
|
import {ClubSelect} from "../../../components/ClubSelect.jsx";
|
||||||
import {addPhoto} from "./InformationForm.jsx";
|
import {addPhoto} from "./InformationForm.jsx";
|
||||||
|
|
||||||
@ -73,8 +73,7 @@ function Form() {
|
|||||||
<TextField name="email" text="Email" placeholder="name@example.com"
|
<TextField name="email" text="Email" placeholder="name@example.com"
|
||||||
type="email" required={false}/>
|
type="email" required={false}/>
|
||||||
<OptionField name="genre" text="Genre" values={{NA: 'N/A', H: 'H', F: 'F'}}/>
|
<OptionField name="genre" text="Genre" values={{NA: 'N/A', H: 'H', F: 'F'}}/>
|
||||||
<OptionField name="country" text="Pays" value={'fr'}
|
<CountryList name="country" text="Pays" value={"FR"}/>
|
||||||
values={{NA: 'Sélectionner...', fr: 'FR', es: 'ES', be: 'BE'}}/>
|
|
||||||
<BirthDayField/>
|
<BirthDayField/>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<ClubSelect name="club" na={true}/>
|
<ClubSelect name="club" na={true}/>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import {useNavigate} from "react-router-dom";
|
|||||||
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
||||||
import {apiAxios} from "../../../utils/Tools.js";
|
import {apiAxios} from "../../../utils/Tools.js";
|
||||||
import {toast} from "react-toastify";
|
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";
|
import {addPhoto} from "../../admin/member/InformationForm.jsx";
|
||||||
|
|
||||||
export function NewMemberPage() {
|
export function NewMemberPage() {
|
||||||
@ -69,8 +69,7 @@ function Form() {
|
|||||||
<TextField name="email" text="Email" placeholder="name@example.com"
|
<TextField name="email" text="Email" placeholder="name@example.com"
|
||||||
type="email"/>
|
type="email"/>
|
||||||
<OptionField name="genre" text="Genre" values={{NA: 'N/A', H: 'H', F: 'F'}}/>
|
<OptionField name="genre" text="Genre" values={{NA: 'N/A', H: 'H', F: 'F'}}/>
|
||||||
<OptionField name="country" text="Pays" value={'fr'}
|
<CountryList name="country" text="Pays" value={"FR"}/>
|
||||||
values={{NA: 'Sélectionner...', fr: 'FR', es: 'ES', be: 'BE'}}/>
|
|
||||||
<BirthDayField/>
|
<BirthDayField/>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="input-group mb-3">
|
<div className="input-group mb-3">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user