diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/MembreModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/MembreModel.java index 819a1c0..334c3fc 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/MembreModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/MembreModel.java @@ -53,6 +53,6 @@ public class MembreModel { String url_photo; - @OneToMany(mappedBy = "membre", fetch = FetchType.EAGER) + @OneToMany(mappedBy = "membre", fetch = FetchType.LAZY) List licences; } diff --git a/src/main/java/fr/titionfire/ffsaf/data/repository/LicenceRepository.java b/src/main/java/fr/titionfire/ffsaf/data/repository/LicenceRepository.java new file mode 100644 index 0000000..77d9f34 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/data/repository/LicenceRepository.java @@ -0,0 +1,9 @@ +package fr.titionfire.ffsaf.data.repository; + +import fr.titionfire.ffsaf.data.model.LicenceModel; +import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class LicenceRepository implements PanacheRepositoryBase { +} diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/LicenceService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/LicenceService.java new file mode 100644 index 0000000..b37aa03 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/LicenceService.java @@ -0,0 +1,52 @@ +package fr.titionfire.ffsaf.domain.service; + +import fr.titionfire.ffsaf.data.model.LicenceModel; +import fr.titionfire.ffsaf.data.repository.CombRepository; +import fr.titionfire.ffsaf.data.repository.LicenceRepository; +import fr.titionfire.ffsaf.rest.from.LicenceForm; +import io.quarkus.hibernate.reactive.panache.Panache; +import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.hibernate.reactive.mutiny.Mutiny; + +import java.util.List; + +@WithSession +@ApplicationScoped +public class LicenceService { + + @Inject + LicenceRepository repository; + + @Inject + CombRepository combRepository; + + public Uni> getLicence(long id) { + return combRepository.findById(id).chain(combRepository -> Mutiny.fetch(combRepository.getLicences())); + } + + public Uni setLicence(long id, LicenceForm form) { + if (form.getId() == -1) { + return combRepository.findById(id).chain(combRepository -> { + LicenceModel model = new LicenceModel(); + model.setMembre(combRepository); + model.setSaison(form.getSaison()); + model.setCertificate(form.isCertificate()); + model.setValidate(form.isValidate()); + return Panache.withTransaction(() -> repository.persist(model)); + }); + } else { + return repository.findById(form.getId()).chain(model -> { + model.setCertificate(form.isCertificate()); + model.setValidate(form.isValidate()); + return Panache.withTransaction(() -> repository.persist(model)); + }); + } + } + + public Uni deleteLicence(long id) { + return Panache.withTransaction(() -> repository.deleteById(id)); + } +} 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 cde7a34..15dfb48 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java @@ -79,4 +79,5 @@ public class MembreService { return Panache.withTransaction(() -> repository.persist(membreModel)); }); } + } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java index 3030d1f..f1be7f8 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java @@ -49,6 +49,7 @@ public class CombEndpoints { return membreService.getById(id).map(SimpleMembre::fromModel); } + @POST @Path("{id}") @RolesAllowed("federation_admin") diff --git a/src/main/java/fr/titionfire/ffsaf/rest/LicenceEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/LicenceEndpoints.java new file mode 100644 index 0000000..5bfff5c --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/LicenceEndpoints.java @@ -0,0 +1,44 @@ +package fr.titionfire.ffsaf.rest; + +import fr.titionfire.ffsaf.domain.service.LicenceService; +import fr.titionfire.ffsaf.rest.data.SimpleLicence; +import fr.titionfire.ffsaf.rest.from.LicenceForm; +import io.smallrye.mutiny.Uni; +import jakarta.annotation.security.RolesAllowed; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; + +import java.util.List; + +@Path("api/licence") +public class LicenceEndpoints { + + @Inject + LicenceService licenceService; + + @GET + @Path("{id}") + @RolesAllowed("federation_admin") + @Produces(MediaType.APPLICATION_JSON) + public Uni> getLicence(@PathParam("id") long id) { + return licenceService.getLicence(id).map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList()); + } + + @POST + @Path("{id}") + @RolesAllowed("federation_admin") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.MULTIPART_FORM_DATA) + public Uni setLicence(@PathParam("id") long id, LicenceForm form) { + return licenceService.setLicence(id, form).map(SimpleLicence::fromModel); + } + + @DELETE + @Path("{id}") + @RolesAllowed("federation_admin") + @Produces(MediaType.TEXT_PLAIN) + public Uni deleteLicence(@PathParam("id") long id) { + return licenceService.deleteLicence(id); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleLicence.java b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleLicence.java new file mode 100644 index 0000000..c9fedce --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleLicence.java @@ -0,0 +1,32 @@ +package fr.titionfire.ffsaf.rest.data; + +import fr.titionfire.ffsaf.data.model.LicenceModel; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +@AllArgsConstructor +@RegisterForReflection +public class SimpleLicence { + Long id; + Long membre; + int saison; + boolean certificate; + boolean validate; + + public static SimpleLicence fromModel(LicenceModel model) { + if (model == null) + return null; + + return new SimpleLicenceBuilder() + .id(model.getId()) + .membre(model.getMembre().getId()) + .saison(model.getSaison()) + .certificate(model.isCertificate()) + .validate(model.isValidate()) + .build(); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/from/LicenceForm.java b/src/main/java/fr/titionfire/ffsaf/rest/from/LicenceForm.java new file mode 100644 index 0000000..678acc7 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/from/LicenceForm.java @@ -0,0 +1,24 @@ +package fr.titionfire.ffsaf.rest.from; + +import jakarta.ws.rs.FormParam; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public class LicenceForm { + @FormParam("id") + private long id; + + @FormParam("membre") + private long membre; + + @FormParam("saison") + private int saison; + + @FormParam("certificate") + private boolean certificate; + + @FormParam("validate") + private boolean validate; +} diff --git a/src/main/webapp/src/pages/admin/AdminRoot.jsx b/src/main/webapp/src/pages/admin/AdminRoot.jsx index 804f6fa..623032d 100644 --- a/src/main/webapp/src/pages/admin/AdminRoot.jsx +++ b/src/main/webapp/src/pages/admin/AdminRoot.jsx @@ -2,7 +2,7 @@ import {Outlet} from "react-router-dom"; import './AdminRoot.css' import {LoadingProvider} from "../../hooks/useLoading.jsx"; import {MemberList} from "./MemberList.jsx"; -import {MemberPage} from "./MemberPage.jsx"; +import {MemberPage} from "./member/MemberPage.jsx"; export function AdminRoot() { return <> diff --git a/src/main/webapp/src/pages/admin/MemberPage.jsx b/src/main/webapp/src/pages/admin/MemberPage.jsx deleted file mode 100644 index db5faec..0000000 --- a/src/main/webapp/src/pages/admin/MemberPage.jsx +++ /dev/null @@ -1,455 +0,0 @@ -import {useNavigate, useParams} from "react-router-dom"; -import {LoadingProvider, useLoadingSwitcher} from "../../hooks/useLoading.jsx"; -import {useFetch} from "../../hooks/useFetch.js"; -import {AxiosError} from "../../components/AxiosError.jsx"; -import {ClubSelect} from "../../components/ClubSelect.jsx"; -import {useEffect, useState} from "react"; -import {apiAxios, getCategoryFormBirthDate} from "../../utils/Tools.js"; -import imageCompression from "browser-image-compression"; -import {ColoredCircle} from "../../components/ColoredCircle.jsx"; -import {toast} from "react-toastify"; - -const vite_url = import.meta.env.VITE_URL; - -export function MemberPage() { - const {id} = useParams() - const navigate = useNavigate(); - - const setLoading = useLoadingSwitcher() - const {data, error} = useFetch(`/member/${id}`, setLoading, 1) - - return <> -

Page membre

- - {data - ?
-
-
- - -
-
- - -
- - -
-
-
-
- : error && - } - -} - -function PhotoCard({data}) { - return
-
Licence n°{data.licence}
-
-
- avatar -
-
-
; -} - -function InformationForm({data}) { - const setLoading = useLoadingSwitcher() - const handleSubmit = (event) => { - event.preventDefault(); - setLoading(1) - - const formData = new FormData(); - formData.append("id", data.id); - formData.append("lname", event.target.lname?.value); - formData.append("fname", event.target.fname?.value); - formData.append("categorie", event.target.category?.value); - formData.append("club", event.target.club?.value); - formData.append("genre", event.target.genre?.value); - formData.append("country", event.target.country?.value); - formData.append("birth_date", new Date(event.target.birth_date?.value).toUTCString()); - formData.append("email", event.target.email?.value); - formData.append("role", event.target.role?.value); - formData.append("grade_arbitrage", event.target.grade_arbitrage?.value); - - const send = (formData_) => { - apiAxios.post(`/member/${data.id}`, formData_, { - headers: { - 'Accept': '*/*', - 'Content-Type': 'multipart/form-data', - } - }).then(_ => { - toast.success('Profile mis à jours avec succès 🎉'); - }).catch(e => { - console.log(e.response) - toast.error('Échec de la mise à jours du profile 😕 (code: ' + e.response.status + ')'); - }).finally(() => { - if (setLoading) - setLoading(0) - }) - } - - const imageFile = event.target.url_photo.files[0]; - if (imageFile) { - console.log(`originalFile size ${imageFile.size / 1024 / 1024} MB`); - - const options = { - maxSizeMB: 1, - maxWidthOrHeight: 1920, - useWebWorker: true, - } - - imageCompression(imageFile, options).then(compressedFile => { - console.log(`compressedFile size ${compressedFile.size / 1024 / 1024} MB`); // smaller than maxSizeMB - formData.append("photo_data", compressedFile) - send(formData) - }); - } else { - send(formData) - } - } - - return
-
-
Information
-
- - - - - - -
- -
- - -
-
- - -
-
-
-
- -
-
-
-
-
; -} - -function PremForm({userData}) { - const setLoading = useLoadingSwitcher() - const handleSubmitPerm = (event) => { - event.preventDefault(); - setLoading(1) - - const formData = new FormData(); - formData.append("federation_admin", event.target.federation_admin?.checked); - formData.append("safca_user", event.target.safca_user?.checked); - formData.append("safca_create_compet", event.target.safca_create_compet?.checked); - formData.append("safca_super_admin", event.target.safca_super_admin?.checked); - - apiAxios.put(`/compte/${userData.userId}/roles`, formData, { - headers: { - 'Accept': '*/*', - 'Content-Type': 'form-data', - } - }).then(_ => { - toast.success('Permission mise à jours avec succès 🎉'); - }).catch(e => { - console.log(e.response) - toast.error('Échec de la mise à jours des permissions 😕 (code: ' + e.response.status + ')'); - }).finally(() => { - if (setLoading) - setLoading(0) - }) - } - - return
-
-
Permission
-
-
- {userData.userId - ? - :
-
-
Ce membre ne dispose pas de compte...
-
-
- } -
-
-
- {userData.userId && } -
-
-
-
-
-} - -function PremFormContent({userData}) { - const setLoading = useLoadingSwitcher() - const {data, error} = useFetch(`/compte/${userData.userId}/roles`, setLoading, 1) - - return <> -
-
FFSAF intra
- {data - ? <> - - - : error && } -
-
-
SAFCA
- {data - ? <> - - - - - : error && } -
- -} - -function LicenceCard() { - return
-
-
Licence
-
-

Web Design

-
-
-
; -} - -function SelectCard() { - return
-
-
Sélection en équipe de France
-
-

Web Design

- -
-
-
; -} - -function CompteInfo({userData}) { - - const creatAccount = () => { - let err = {}; - toast.promise( - apiAxios.put(`/compte/${userData.id}/init`).catch(e => { - err = e - }), - { - pending: 'Création du compte en cours', - success: 'Compte créé avec succès 🎉', - error: 'Échec de la création du compte 😕 (code: ' + err.response.status + ')' - } - ) - } - const sendId = (event) => { - event.preventDefault(); - - toast.promise( - apiAxios.put(`/compte/${userData.id}/setUUID/${event.target.uuid?.value}`), - { - pending: "Définition de l'identifient en cours", - success: "Identifient défini avec succès 🎉", - error: "Échec de la définition de l'identifient 😕 " - } - ) - } - - return
-
-
- -
    -
  • -
-
-
-
- {userData.userId - ? - : - <> -
-
-
Ce membre ne dispose pas de compte...
-
-
-
-
- -
-
- - } -
- -
- -} - -function CompteInfoContent({ - userData - }) { - const setLoading = useLoadingSwitcher() - const {data, error} = useFetch(`/compte/${userData.userId}`, setLoading, 1) - - return <> - {data - ? <> -
-
-
Identifiant: {data.login}
-
-
-
-
-
Activer:
-
-
-
-
-
Email vérifié:
-
-
- - : error && - } -} - -function BirthDayField({inti_date, inti_category}) { - const [date, setDate] = useState(inti_date) - const [category, setCategory] = useState(inti_category) - const [canUpdate, setCanUpdate] = useState(false) - useEffect(() => { - const b = category !== getCategoryFormBirthDate(new Date(date), new Date('2023-09-01')) - if (b !== canUpdate) - setCanUpdate(b) - }, [date, category]) - - const updateCat = _ => { - setCategory(getCategoryFormBirthDate(new Date(date), new Date('2023-09-01'))) - } - - - return <> -
- Date de naissance - setDate(e.target.value)}/> -
-
-
- Catégorie - - {canUpdate && } -
-
- -} - -function OptionField({name, text, values, value}) { - return
-
- - -
-
-} - -function TextField({name, text, value, placeholder, type = "text"}) { - return
-
- {text} - -
-
-} - -function CheckField({name, text, value, row = false}) { - return <>{ - row ? -
-
-
- - -
-
-
- :
- - -
- } - -} \ No newline at end of file diff --git a/src/main/webapp/src/pages/admin/member/CompteInfo.jsx b/src/main/webapp/src/pages/admin/member/CompteInfo.jsx new file mode 100644 index 0000000..7d825ce --- /dev/null +++ b/src/main/webapp/src/pages/admin/member/CompteInfo.jsx @@ -0,0 +1,120 @@ +import {toast} from "react-toastify"; +import {apiAxios} from "../../../utils/Tools.js"; +import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx"; +import {useFetch} from "../../../hooks/useFetch.js"; +import {ColoredCircle} from "../../../components/ColoredCircle.jsx"; +import {AxiosError} from "../../../components/AxiosError.jsx"; + +export function CompteInfo({userData}) { + + const creatAccount = () => { + let err = {}; + toast.promise( + apiAxios.put(`/compte/${userData.id}/init`).catch(e => { + err = e + }), + { + pending: 'Création du compte en cours', + success: 'Compte créé avec succès 🎉', + error: 'Échec de la création du compte 😕 (code: ' + err.response.status + ')' + } + ) + } + const sendId = (event) => { + event.preventDefault(); + + toast.promise( + apiAxios.put(`/compte/${userData.id}/setUUID/${event.target.uuid?.value}`), + { + pending: "Définition de l'identifient en cours", + success: "Identifient défini avec succès 🎉", + error: "Échec de la définition de l'identifient 😕 " + } + ) + } + + return
+
+
+ +
    +
  • + +
  • +
+
+
+
+ {userData.userId + ? + : + <> +
+
+
Ce membre ne dispose pas de compte...
+
+
+
+
+ +
+
+ + } +
+ +
+} + +function CompteInfoContent({userData}) { + const setLoading = useLoadingSwitcher() + const {data, error} = useFetch(`/compte/${userData.userId}`, setLoading, 1) + + return <> + {data + ? <> +
+
+
Identifiant: {data.login}
+
+
+
+
+
Activer:
+
+
+
+
+
Email vérifié:
+
+
+ + : error && + } +} diff --git a/src/main/webapp/src/pages/admin/member/InformationForm.jsx b/src/main/webapp/src/pages/admin/member/InformationForm.jsx new file mode 100644 index 0000000..e88630a --- /dev/null +++ b/src/main/webapp/src/pages/admin/member/InformationForm.jsx @@ -0,0 +1,106 @@ +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 "./MemberCustomFiels.jsx"; +import {ClubSelect} from "../../../components/ClubSelect.jsx"; + +export function InformationForm({data}) { + const setLoading = useLoadingSwitcher() + const handleSubmit = (event) => { + event.preventDefault(); + setLoading(1) + + const formData = new FormData(); + formData.append("id", data.id); + formData.append("lname", event.target.lname?.value); + formData.append("fname", event.target.fname?.value); + formData.append("categorie", event.target.category?.value); + formData.append("club", event.target.club?.value); + formData.append("genre", event.target.genre?.value); + formData.append("country", event.target.country?.value); + formData.append("birth_date", new Date(event.target.birth_date?.value).toUTCString()); + formData.append("email", event.target.email?.value); + formData.append("role", event.target.role?.value); + formData.append("grade_arbitrage", event.target.grade_arbitrage?.value); + + const send = (formData_) => { + apiAxios.post(`/member/${data.id}`, formData_, { + headers: { + 'Accept': '*/*', + 'Content-Type': 'multipart/form-data', + } + }).then(_ => { + toast.success('Profile mis à jours avec succès 🎉'); + }).catch(e => { + console.log(e.response) + toast.error('Échec de la mise à jours du profile 😕 (code: ' + e.response.status + ')'); + }).finally(() => { + if (setLoading) + setLoading(0) + }) + } + + const imageFile = event.target.url_photo.files[0]; + if (imageFile) { + console.log(`originalFile size ${imageFile.size / 1024 / 1024} MB`); + + const options = { + maxSizeMB: 1, + maxWidthOrHeight: 1920, + useWebWorker: true, + } + + imageCompression(imageFile, options).then(compressedFile => { + console.log(`compressedFile size ${compressedFile.size / 1024 / 1024} MB`); // smaller than maxSizeMB + formData.append("photo_data", compressedFile) + send(formData) + }); + } else { + send(formData) + } + } + + return
+
+
Information
+
+ + + + + + +
+ +
+ + +
+
+ + +
+
+
+
+ +
+
+
+
+
; +} \ No newline at end of file diff --git a/src/main/webapp/src/pages/admin/member/LicenceCard.jsx b/src/main/webapp/src/pages/admin/member/LicenceCard.jsx new file mode 100644 index 0000000..1346079 --- /dev/null +++ b/src/main/webapp/src/pages/admin/member/LicenceCard.jsx @@ -0,0 +1,196 @@ +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 "./MemberCustomFiels.jsx"; +import {apiAxios, getSaison} from "../../../utils/Tools.js"; +import {Input} from "../../../components/Input.jsx"; +import {toast} from "react-toastify"; + +function licenceReducer(licences, action) { + switch (action.type) { + case 'ADD': + return [ + ...licences, + action.payload + ] + case 'REMOVE': + return licences.filter(licence => licence.id !== action.payload) + case 'UPDATE_OR_ADD': + const index = licences.findIndex(licence => licence.id === action.payload.id) + if (index === -1) { + return [ + ...licences, + action.payload + ] + } else { + licences[index] = action.payload + return [...licences] + } + case 'SORT': + return licences.sort((a, b) => b.saison - a.saison) + default: + throw new Error() + } +} + +export function LicenceCard({userData}) { + const setLoading = useLoadingSwitcher() + const {data, error} = useFetch(`/licence/${userData.id}`, setLoading, 1) + + const [modalLicence, setModal] = useState({id: -1, membre: userData.id}) + const [licences, dispatch] = useReducer(licenceReducer, []) + + useEffect(() => { + if (!data) return + for (const dataKey of data) { + dispatch({type: 'UPDATE_OR_ADD', payload: dataKey}) + } + dispatch({type: 'SORT'}) + }, [data]); + + return
+
+
+
Licence
+
+ +
+
+
+
+
    + {licences.map((licence, index) => { + return
    +
    {licence?.saison}-{licence?.saison + 1}
    + +
    + })} + {error && } +
+
+ + +
; +} + +function sendLicence(event, dispatch) { + event.preventDefault(); + + const formData = new FormData(event.target); + toast.promise( + apiAxios.post(`/licence/${formData.get('membre')}`, formData), + { + pending: "Enregistrement de la licence en cours", + success: "Licence enregistrée avec succès 🎉", + error: "Échec de l'enregistrement de la licence 😕" + } + ).then(data => { + dispatch({type: 'UPDATE_OR_ADD', payload: data.data}) + dispatch({type: 'SORT'}) + }) + +} + +function removeLicence(id, dispatch) { + toast.promise( + apiAxios.delete(`/licence/${id}`), + { + pending: "Suppression de la licence en cours", + success: "Licence supprimée avec succès 🎉", + error: "Échec de la suppression de la licence 😕" + } + ).then(data => { + dispatch({type: 'REMOVE', payload: id}) + }) + console.log(id) +} + +function ModalContent({licence, dispatch}) { + const [saison, setSaison] = useState(0) + const [certificate, setCertificate] = useState(false) + const [validate, setValidate] = useState(false) + const [isNew, setNew] = useState(true) + const setSeason = (event) => { + setSaison(Number(event.target.value)) + } + const handleCertificateChange = (event) => { + setCertificate(event.target.value === 'true'); + } + const handleValidateChange = (event) => { + setValidate(event.target.value === 'true'); + } + + useEffect(() => { + if (licence.id !== -1) { + setNew(false) + setSaison(licence.saison) + setCertificate(licence.certificate) + setValidate(licence.validate) + } else { + setNew(true) + setSaison(getSaison()) + setCertificate(false) + setValidate(false) + } + }, [licence]); + + return
sendLicence(e, dispatch)}> + + +
+

Edition de la licence

+ +
+
+
+ {isNew + ? + : <>{saison} + } + - + {saison + 1} +
+ + +
+
+ + + {isNew || } +
+
+} + +function RadioGroupeOnOff({value, onChange, name, text}) { + return
+ {text} + + + + +
; +} \ No newline at end of file diff --git a/src/main/webapp/src/pages/admin/member/MemberCustomFiels.jsx b/src/main/webapp/src/pages/admin/member/MemberCustomFiels.jsx new file mode 100644 index 0000000..05490bf --- /dev/null +++ b/src/main/webapp/src/pages/admin/member/MemberCustomFiels.jsx @@ -0,0 +1,80 @@ +import {useEffect, useState} from "react"; +import {getCategoryFormBirthDate} from "../../../utils/Tools.js"; + +export function BirthDayField({inti_date, inti_category}) { + const [date, setDate] = useState(inti_date) + const [category, setCategory] = useState(inti_category) + const [canUpdate, setCanUpdate] = useState(false) + useEffect(() => { + const b = category !== getCategoryFormBirthDate(new Date(date), new Date('2023-09-01')) + if (b !== canUpdate) + setCanUpdate(b) + }, [date, category]) + + const updateCat = _ => { + setCategory(getCategoryFormBirthDate(new Date(date), new Date('2023-09-01'))) + } + + + return <> +
+ Date de naissance + setDate(e.target.value)}/> +
+
+
+ Catégorie + + {canUpdate && } +
+
+ +} + +export function OptionField({name, text, values, value}) { + return
+
+ + +
+
+} + +export function TextField({name, text, value, placeholder, type = "text"}) { + return
+
+ {text} + +
+
+} + +export function CheckField({name, text, value, row = false}) { + return <>{ + row ? +
+
+
+ + +
+
+
+ :
+ + +
+ } + +} \ No newline at end of file diff --git a/src/main/webapp/src/pages/admin/member/MemberPage.jsx b/src/main/webapp/src/pages/admin/member/MemberPage.jsx new file mode 100644 index 0000000..77b85d3 --- /dev/null +++ b/src/main/webapp/src/pages/admin/member/MemberPage.jsx @@ -0,0 +1,72 @@ +import {useNavigate, useParams} from "react-router-dom"; +import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx"; +import {useFetch} from "../../../hooks/useFetch.js"; +import {AxiosError} from "../../../components/AxiosError.jsx"; +import {CompteInfo} from "./CompteInfo.jsx"; +import {PremForm} from "./PremForm.jsx"; +import {InformationForm} from "./InformationForm.jsx"; +import {LicenceCard} from "./LicenceCard.jsx"; + +const vite_url = import.meta.env.VITE_URL; + +export function MemberPage() { + const {id} = useParams() + const navigate = useNavigate(); + + const setLoading = useLoadingSwitcher() + const {data, error} = useFetch(`/member/${id}`, setLoading, 1) + + return <> +

Page membre

+ + {data + ?
+
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+
+
+
+ : error && + } + +} + +function PhotoCard({data}) { + return
+
Licence n°{data.licence}
+
+
+ avatar +
+
+
; +} + +function SelectCard() { + return
+
Sélection en équipe de France
+
+

Web Design

+ +
+
; +} diff --git a/src/main/webapp/src/pages/admin/member/PremForm.jsx b/src/main/webapp/src/pages/admin/member/PremForm.jsx new file mode 100644 index 0000000..8f5ab0d --- /dev/null +++ b/src/main/webapp/src/pages/admin/member/PremForm.jsx @@ -0,0 +1,87 @@ +import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx"; +import {apiAxios} from "../../../utils/Tools.js"; +import {toast} from "react-toastify"; +import {useFetch} from "../../../hooks/useFetch.js"; +import {CheckField} from "./MemberCustomFiels.jsx"; +import {AxiosError} from "../../../components/AxiosError.jsx"; + +export function PremForm({userData}) { + const setLoading = useLoadingSwitcher() + const handleSubmitPerm = (event) => { + event.preventDefault(); + setLoading(1) + + const formData = new FormData(); + formData.append("federation_admin", event.target.federation_admin?.checked); + formData.append("safca_user", event.target.safca_user?.checked); + formData.append("safca_create_compet", event.target.safca_create_compet?.checked); + formData.append("safca_super_admin", event.target.safca_super_admin?.checked); + + apiAxios.put(`/compte/${userData.userId}/roles`, formData, { + headers: { + 'Accept': '*/*', + 'Content-Type': 'form-data', + } + }).then(_ => { + toast.success('Permission mise à jours avec succès 🎉'); + }).catch(e => { + console.log(e.response) + toast.error('Échec de la mise à jours des permissions 😕 (code: ' + e.response.status + ')'); + }).finally(() => { + if (setLoading) + setLoading(0) + }) + } + + return
+
+
Permission
+
+
+ {userData.userId + ? + :
+
+
Ce membre ne dispose pas de compte...
+
+
+ } +
+
+
+ {userData.userId && } +
+
+
+
+
+} + +function PremFormContent({userData}) { + const setLoading = useLoadingSwitcher() + const {data, error} = useFetch(`/compte/${userData.userId}/roles`, setLoading, 1) + + return <> +
+
FFSAF intra
+ {data + ? <> + + + : error && } +
+
+
SAFCA
+ {data + ? <> + + + + + : error && } +
+ +} \ No newline at end of file