From a58dcdd08e16639410cdd72dcc9e32ab8335035b Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Thu, 18 Jul 2024 21:40:36 +0200 Subject: [PATCH] feat: mePage --- src/main/java/fr/titionfire/SomePage.java | 13 +- .../ffsaf/domain/service/MembreService.java | 13 ++ .../titionfire/ffsaf/rest/CombEndpoints.java | 15 ++- .../ffsaf/rest/CountriesEndpoints.java | 32 +++++ .../fr/titionfire/ffsaf/rest/data/MeData.java | 45 +++++++ .../fr/titionfire/ffsaf/utils/Categorie.java | 16 +++ .../java/fr/titionfire/ffsaf/utils/Genre.java | 15 ++- .../ffsaf/utils/GradeArbitrage.java | 6 +- .../fr/titionfire/ffsaf/utils/RoleAsso.java | 6 +- src/main/webapp/src/App.jsx | 7 +- .../src/components/MemberCustomFiels.jsx | 26 +++- src/main/webapp/src/components/Nav.jsx | 26 ++-- src/main/webapp/src/hooks/useCountries.jsx | 24 ++++ src/main/webapp/src/pages/MePage.jsx | 115 ++++++++++++++++++ .../src/pages/admin/club/NewClubPage.jsx | 2 +- .../src/pages/admin/member/NewMemberPage.jsx | 5 +- .../src/pages/club/member/NewMemberPage.jsx | 5 +- 17 files changed, 333 insertions(+), 38 deletions(-) create mode 100644 src/main/java/fr/titionfire/ffsaf/rest/CountriesEndpoints.java create mode 100644 src/main/java/fr/titionfire/ffsaf/rest/data/MeData.java create mode 100644 src/main/webapp/src/hooks/useCountries.jsx create mode 100644 src/main/webapp/src/pages/MePage.jsx diff --git a/src/main/java/fr/titionfire/SomePage.java b/src/main/java/fr/titionfire/SomePage.java index 08bd7d1..5bdb78f 100644 --- a/src/main/java/fr/titionfire/SomePage.java +++ b/src/main/java/fr/titionfire/SomePage.java @@ -1,17 +1,15 @@ package fr.titionfire; import io.quarkus.qute.Template; -import io.quarkus.qute.TemplateInstance; - +import io.smallrye.mutiny.Uni; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import static java.util.Objects.requireNonNull; -@Path("/some-page") +@Path("api/some-page") public class SomePage { private final Template page; @@ -22,8 +20,11 @@ public class SomePage { @GET @Produces(MediaType.TEXT_HTML) - public TemplateInstance get(@QueryParam("name") String name) { - return page.data("name", name); + public Uni get() { + return Uni.createFrom() + .completionStage(() -> page + .data("name", "test") + .renderAsync()); } } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java index 6dd6afe..3b8074a 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java @@ -8,6 +8,8 @@ import fr.titionfire.ffsaf.data.repository.LicenceRepository; import fr.titionfire.ffsaf.net2.ServerCustom; import fr.titionfire.ffsaf.net2.data.SimpleCombModel; 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.from.ClubMemberForm; import fr.titionfire.ffsaf.rest.from.FullMemberForm; @@ -27,6 +29,7 @@ import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.ForbiddenException; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.jwt.JsonWebToken; +import org.hibernate.reactive.mutiny.Mutiny; import java.util.List; @@ -265,4 +268,14 @@ public class MembreService { StringSimilarity.similarity(m.getLname(), lname) <= 3) .map(SimpleMembre::fromModel).toList()); } + + public Uni 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); + } } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java index 95654ec..7d3b8c2 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java @@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.rest; import fr.titionfire.ffsaf.data.model.MembreModel; 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.from.ClubMemberForm; import fr.titionfire.ffsaf.rest.from.FullMemberForm; @@ -18,17 +19,11 @@ import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; -import jodd.net.MimeTypes; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.jwt.JsonWebToken; -import java.io.*; import java.net.URISyntaxException; -import java.net.URLConnection; -import java.nio.file.Files; import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; import java.util.function.Consumer; @Authenticated @@ -198,6 +193,14 @@ public class CombEndpoints { return membreService.delete(id, idToken); } + @GET + @Path("me") + @Authenticated + @Produces(MediaType.APPLICATION_JSON) + public Uni getMe() { + return membreService.getMembre(idToken.getSubject()); + } + @GET @Path("{id}/photo") @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) diff --git a/src/main/java/fr/titionfire/ffsaf/rest/CountriesEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/CountriesEndpoints.java new file mode 100644 index 0000000..ff87b52 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/CountriesEndpoints.java @@ -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> getCountries(@PathParam("lang") String lang, @PathParam("code") String code) { + Locale locale = new Locale(lang, code); + return Uni.createFrom().item(new HashMap()) + .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)); + } + }); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/MeData.java b/src/main/java/fr/titionfire/ffsaf/rest/data/MeData.java new file mode 100644 index 0000000..d9a79ea --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/MeData.java @@ -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 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; + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/utils/Categorie.java b/src/main/java/fr/titionfire/ffsaf/utils/Categorie.java index eca6c0e..fdfc437 100644 --- a/src/main/java/fr/titionfire/ffsaf/utils/Categorie.java +++ b/src/main/java/fr/titionfire/ffsaf/utils/Categorie.java @@ -30,4 +30,20 @@ public enum Categorie { 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"; + }; + } } diff --git a/src/main/java/fr/titionfire/ffsaf/utils/Genre.java b/src/main/java/fr/titionfire/ffsaf/utils/Genre.java index b5815c3..3ee557c 100644 --- a/src/main/java/fr/titionfire/ffsaf/utils/Genre.java +++ b/src/main/java/fr/titionfire/ffsaf/utils/Genre.java @@ -1,5 +1,18 @@ package fr.titionfire.ffsaf.utils; 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; + } } diff --git a/src/main/java/fr/titionfire/ffsaf/utils/GradeArbitrage.java b/src/main/java/fr/titionfire/ffsaf/utils/GradeArbitrage.java index 800dca5..35af369 100644 --- a/src/main/java/fr/titionfire/ffsaf/utils/GradeArbitrage.java +++ b/src/main/java/fr/titionfire/ffsaf/utils/GradeArbitrage.java @@ -5,14 +5,14 @@ public enum GradeArbitrage { ASSESSEUR("Assesseur"), ARBITRE("Arbitre"); - public final String name; + public final String str; GradeArbitrage(String name) { - this.name = name; + this.str = name; } @Override public String toString() { - return name; + return str; } } diff --git a/src/main/java/fr/titionfire/ffsaf/utils/RoleAsso.java b/src/main/java/fr/titionfire/ffsaf/utils/RoleAsso.java index c591150..b498495 100644 --- a/src/main/java/fr/titionfire/ffsaf/utils/RoleAsso.java +++ b/src/main/java/fr/titionfire/ffsaf/utils/RoleAsso.java @@ -13,16 +13,16 @@ public enum RoleAsso { VTRESORIER("Vise-Trésorier", 2), MEMBREBUREAU("Membre bureau", 1); - public final String name; + public final String str; public final int level; RoleAsso(String name, int level) { - this.name = name; + this.str = name; this.level = level; } @Override public String toString() { - return name; + return str; } } diff --git a/src/main/webapp/src/App.jsx b/src/main/webapp/src/App.jsx index e5e6d24..a2fe33a 100644 --- a/src/main/webapp/src/App.jsx +++ b/src/main/webapp/src/App.jsx @@ -12,6 +12,7 @@ import './App.css' import 'react-toastify/dist/ReactToastify.css'; import {ClubRoot, getClubChildren} from "./pages/club/ClubRoot.jsx"; import {DemandeAff, DemandeAffOk} from "./pages/DemandeAff.jsx"; +import {MePage} from "./pages/MePage.jsx"; const router = createBrowserRouter([ { @@ -45,6 +46,10 @@ const router = createBrowserRouter([ element: } ] + }, + { + path: 'me', + element: } ] }, @@ -82,7 +87,7 @@ function Root() {
Catégorie {canUpdate && } @@ -63,11 +64,28 @@ export function RoleList({name, text, value, disabled = false}) { } export function CountryList({name, text, value, values = undefined, disabled = false}) { - if (values === undefined){ - values = {NA: 'Sélectionner...', fr: 'FR', es: 'ES', be: 'BE'} + const country = useCountries('fr') + const [value_, setValue] = useState(value) + + if (values === undefined) { + values = {...country} } - return + return
+
+ + +
+
} export function TextField({name, text, value, placeholder, type = "text", disabled = false, required = true}) { diff --git a/src/main/webapp/src/components/Nav.jsx b/src/main/webapp/src/components/Nav.jsx index 2b3a4f4..4e5a01d 100644 --- a/src/main/webapp/src/components/Nav.jsx +++ b/src/main/webapp/src/components/Nav.jsx @@ -80,11 +80,23 @@ function AdminMenu() { function LoginMenu() { const {is_authenticated} = useAuth() - return
  • - {!is_authenticated ? ( -
    login()}>Connexion
    - ) : ( -
    logout()}>Déconnexion
    - )} -
  • + return <> + {!is_authenticated ? +
  • +
    login()}>Connexion
    +
  • + : +
  • + +
      +
    • Mon espace
    • +
    • +
      logout()}>Déconnexion
      +
    • +
    +
  • + } + } \ No newline at end of file diff --git a/src/main/webapp/src/hooks/useCountries.jsx b/src/main/webapp/src/hooks/useCountries.jsx new file mode 100644 index 0000000..f4cb55b --- /dev/null +++ b/src/main/webapp/src/hooks/useCountries.jsx @@ -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; +} \ No newline at end of file diff --git a/src/main/webapp/src/pages/MePage.jsx b/src/main/webapp/src/pages/MePage.jsx new file mode 100644 index 0000000..edaa3fa --- /dev/null +++ b/src/main/webapp/src/pages/MePage.jsx @@ -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
    +

    Mon espace

    + + {data + ?
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + : error && + } +
    +} + +export function LicenceCard({userData}) { + return
    +
    +
    +
    Licence
    +
    +
    +
    +
      + {userData.licences.map((licence, index) => { + return
      +
      {licence?.saison}-{licence?.saison + 1}
      +
      + })} +
    +
    +
    ; +} + +function PhotoCard({data}) { + return
    +
    Licence n°{data.licence}
    +
    +
    + avatar +
    +
    +
    ; +} + +function SelectCard() { + return
    +
    Sélection en équipe de France
    +
    +
    +
    ; +} + + +export function InformationForm({data}) { + const style = {marginRight: '0.7em'} + + return
    +
    Information
    +
    +
    +

    + {data.lname} {data.fname}
    + {data.email}
    + {data.genre === 'Homme' && + || data.genre === 'Femme' && + || }{data.genre}
    + {data.birth_date ? data.birth_date.split('T')[0] : ''}
    + {data.categorie}
    + Nationalité :
    + Rôle au sien du club : {data.role}
    + Formation d'arbitrage : {data.grade_arbitrage} +

    +
    +
    +
    ; +} \ No newline at end of file diff --git a/src/main/webapp/src/pages/admin/club/NewClubPage.jsx b/src/main/webapp/src/pages/admin/club/NewClubPage.jsx index 74903a6..0e01377 100644 --- a/src/main/webapp/src/pages/admin/club/NewClubPage.jsx +++ b/src/main/webapp/src/pages/admin/club/NewClubPage.jsx @@ -62,7 +62,7 @@ function InformationForm() {
    - +
    diff --git a/src/main/webapp/src/pages/admin/member/NewMemberPage.jsx b/src/main/webapp/src/pages/admin/member/NewMemberPage.jsx index c8b0727..a171c8d 100644 --- a/src/main/webapp/src/pages/admin/member/NewMemberPage.jsx +++ b/src/main/webapp/src/pages/admin/member/NewMemberPage.jsx @@ -2,7 +2,7 @@ import {useNavigate} from "react-router-dom"; import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx"; import {apiAxios} from "../../../utils/Tools.js"; 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 {addPhoto} from "./InformationForm.jsx"; @@ -73,8 +73,7 @@ function Form() { - +
    diff --git a/src/main/webapp/src/pages/club/member/NewMemberPage.jsx b/src/main/webapp/src/pages/club/member/NewMemberPage.jsx index b3d588f..ee40289 100644 --- a/src/main/webapp/src/pages/club/member/NewMemberPage.jsx +++ b/src/main/webapp/src/pages/club/member/NewMemberPage.jsx @@ -2,7 +2,7 @@ import {useNavigate} from "react-router-dom"; 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 NewMemberPage() { @@ -69,8 +69,7 @@ function Form() { - +