From 6a21bd47357e36b7e4b8de8aaa77548b2ef72c0e Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Sun, 14 Jul 2024 13:24:02 +0200 Subject: [PATCH] feat: club end main --- .../ffsaf/data/model/ClubModel.java | 8 +- .../ffsaf/domain/entity/ClubEntity.java | 4 +- .../ffsaf/domain/service/ClubService.java | 46 ++++- .../ffsaf/net2/data/SimpleClubModel.java | 4 +- .../ffsaf/rest/data/SimpleClub.java | 4 +- .../ffsaf/rest/from/FullClubForm.java | 26 +++ .../java/fr/titionfire/ffsaf/utils/Utils.java | 2 +- .../src/components/Club/ContactEditor.jsx | 79 ++++++++ .../src/components/Club/HoraireEditor.jsx | 95 ++++++++++ .../Club}/LocationEditor.jsx | 172 ++++++++--------- .../webapp/src/pages/admin/club/ClubPage.jsx | 173 +++--------------- src/main/webapp/src/utils/SimpleReducer.jsx | 2 + 12 files changed, 365 insertions(+), 250 deletions(-) create mode 100644 src/main/webapp/src/components/Club/ContactEditor.jsx create mode 100644 src/main/webapp/src/components/Club/HoraireEditor.jsx rename src/main/webapp/src/{pages/admin/club => components/Club}/LocationEditor.jsx (53%) diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java index 791c31a..9f26806 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java @@ -28,8 +28,6 @@ public class ClubModel { String country; - String shieldURL; - //@Enumerated(EnumType.STRING) @ElementCollection @CollectionTable(name = "club_contact_mapping", @@ -37,15 +35,19 @@ public class ClubModel { @MapKeyColumn(name = "contact_type") Map contact; + @Lob + @Column(length=4096) String training_location; + @Lob + @Column(length=4096) String training_day_time; String contact_intern; String RNA; - String SIRET; + Long SIRET; String no_affiliation; diff --git a/src/main/java/fr/titionfire/ffsaf/domain/entity/ClubEntity.java b/src/main/java/fr/titionfire/ffsaf/domain/entity/ClubEntity.java index ab59f8b..bf4939a 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/entity/ClubEntity.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/entity/ClubEntity.java @@ -18,13 +18,12 @@ public class ClubEntity { private String name; private String clubId; private String country; - private String shieldURL; private Map contact; private String training_location; private String training_day_time; private String contact_intern; private String RNA; - private String SIRET; + private Long SIRET; private String no_affiliation; private boolean international; @@ -38,7 +37,6 @@ public class ClubEntity { .name(model.getName()) .clubId(model.getClubId()) .country(model.getCountry()) - .shieldURL(model.getShieldURL()) .contact(model.getContact()) .training_location(model.getTraining_location()) .training_day_time(model.getTraining_day_time()) diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java index 6bb1081..1e3d4e4 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java @@ -1,9 +1,14 @@ package fr.titionfire.ffsaf.domain.service; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import fr.titionfire.ffsaf.data.model.ClubModel; import fr.titionfire.ffsaf.data.repository.ClubRepository; +import fr.titionfire.ffsaf.net2.ServerCustom; import fr.titionfire.ffsaf.net2.data.SimpleClubModel; +import fr.titionfire.ffsaf.net2.request.SReqClub; import fr.titionfire.ffsaf.rest.from.FullClubForm; +import fr.titionfire.ffsaf.utils.Contact; import fr.titionfire.ffsaf.utils.PageResult; import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.PanacheQuery; @@ -19,8 +24,11 @@ import jakarta.ws.rs.BadRequestException; import org.hibernate.reactive.mutiny.Mutiny; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER; + @WithSession @ApplicationScoped public class ClubService { @@ -28,13 +36,18 @@ public class ClubService { @Inject ClubRepository repository; + @Inject + ServerCustom serverCustom; + public SimpleClubModel findByIdOptionalClub(long id) throws Throwable { - return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel))); + return VertxContextSupport.subscribeAndAwait( + () -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel))); } public Collection findAllClub() throws Throwable { return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction( - () -> repository.findAll().list().map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList()))); + () -> repository.findAll().list() + .map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList()))); } public Uni> getAll() { @@ -88,15 +101,32 @@ public class ClubService { } public Uni update(long id, FullClubForm input) { - /*return repository.findById(id) - .onItem().transformToUni(m -> { + return repository.findById(id).call(m -> Mutiny.fetch(m.getContact())) + .onItem().transformToUni(Unchecked.function(m -> { + TypeReference> typeRef = new TypeReference<>() { + }; + m.setName(input.getName()); m.setCountry(input.getCountry()); - m.setNo_affiliation(input.getNo_affiliation()); - m.setShieldURL(input.getShieldURL()); + + if (!input.isInternational()) { + m.setTraining_location(input.getTraining_location()); + m.setTraining_day_time(input.getTraining_day_time()); + m.setContact_intern(input.getContact_intern()); + m.setRNA(input.getRna()); + m.setSIRET(input.getSiret()); + + try { + m.setContact(MAPPER.readValue(input.getContact(), typeRef)); + } catch (JsonProcessingException e) { + throw new BadRequestException(); + } + } return Panache.withTransaction(() -> repository.persist(m)); - });*/ - return Uni.createFrom().nullItem(); + })) + .invoke(membreModel -> SReqClub.sendIfNeed(serverCustom.clients, + SimpleClubModel.fromModel(membreModel))) + .map(__ -> "OK"); } public Uni add(FullClubForm input) { diff --git a/src/main/java/fr/titionfire/ffsaf/net2/data/SimpleClubModel.java b/src/main/java/fr/titionfire/ffsaf/net2/data/SimpleClubModel.java index 17d406c..815a464 100644 --- a/src/main/java/fr/titionfire/ffsaf/net2/data/SimpleClubModel.java +++ b/src/main/java/fr/titionfire/ffsaf/net2/data/SimpleClubModel.java @@ -23,7 +23,7 @@ public class SimpleClubModel { if (model == null) return null; - return new SimpleClubModel(model.getId(), model.getName(), model.getCountry(), model.getShieldURL(), - model.getNo_affiliation()); + return new SimpleClubModel(model.getId(), model.getName(), model.getCountry(), + "/api/club/" + model.getClubId() + "/logo", model.getNo_affiliation()); } } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleClub.java b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleClub.java index 9e8d8ff..8972e07 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleClub.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleClub.java @@ -21,13 +21,12 @@ public class SimpleClub { private String clubId; private String name; private String country; - private String shieldURL; private Map contact; private String training_location; private String training_day_time; private String contact_intern; private String RNA; - private String SIRET; + private Long SIRET; private String no_affiliation; private boolean international; private HashMap contactMap = null; @@ -41,7 +40,6 @@ public class SimpleClub { .clubId(model.getClubId()) .name(model.getName()) .country(model.getCountry()) - .shieldURL(model.getShieldURL()) .contact(model.getContact()) .training_location(model.getTraining_location()) .training_day_time(model.getTraining_day_time()) diff --git a/src/main/java/fr/titionfire/ffsaf/rest/from/FullClubForm.java b/src/main/java/fr/titionfire/ffsaf/rest/from/FullClubForm.java index fcd9108..2ad0d6f 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/from/FullClubForm.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/from/FullClubForm.java @@ -3,8 +3,10 @@ package fr.titionfire.ffsaf.rest.from; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.core.MediaType; import lombok.Getter; +import lombok.ToString; import org.jboss.resteasy.reactive.PartType; +@ToString @Getter public class FullClubForm { @FormParam("id") @@ -13,6 +15,30 @@ public class FullClubForm { @FormParam("name") private String name = null; + @FormParam("country") + private String country = null; + + @FormParam("contact") + private String contact = null; + + @FormParam("training_location") + private String training_location = null; + + @FormParam("training_day_time") + private String training_day_time = null; + + @FormParam("contact_intern") + private String contact_intern = null; + + @FormParam("rna") + private String rna = null; + + @FormParam("siret") + private Long siret = null; + + @FormParam("international") + private boolean international = false; + @FormParam("status") @PartType(MediaType.APPLICATION_OCTET_STREAM) private byte[] status = new byte[0]; diff --git a/src/main/java/fr/titionfire/ffsaf/utils/Utils.java b/src/main/java/fr/titionfire/ffsaf/utils/Utils.java index e6de816..7dda235 100644 --- a/src/main/java/fr/titionfire/ffsaf/utils/Utils.java +++ b/src/main/java/fr/titionfire/ffsaf/utils/Utils.java @@ -54,7 +54,7 @@ public class Utils { File dirFile = new File(media, dir); if (!dirFile.exists()) - if (dirFile.mkdirs()) + if (!dirFile.mkdirs()) throw new IOException("Fail to create directory " + dir); FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id)); diff --git a/src/main/webapp/src/components/Club/ContactEditor.jsx b/src/main/webapp/src/components/Club/ContactEditor.jsx new file mode 100644 index 0000000..33582fe --- /dev/null +++ b/src/main/webapp/src/components/Club/ContactEditor.jsx @@ -0,0 +1,79 @@ +import {useEffect, useReducer, useState} from "react"; +import {SimpleReducer} from "../../utils/SimpleReducer.jsx"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {faAdd, faTrashCan} from "@fortawesome/free-solid-svg-icons"; + +export function ContactEditor({data}) { + const [state, dispatch] = useReducer(SimpleReducer, []) + const [out_data, setOutData] = useState({}) + + useEffect(() => { + for (const key in data.contact) { + dispatch({type: 'UPDATE_OR_ADD', payload: {id: key, data: data.contact[key]}}) + } + }, [data.contact]); + + useEffect(() => { + let out_data2 = {} + state.forEach(d => { + if (d.data !== undefined) + out_data2[d.id] = d.data + }) + setOutData(out_data2) + }, [state]); + + return
+ + Contacts +
    + {state.map((d, index) => { + if (d.data === undefined) + return; + + return
    + + { + dispatch({type: 'UPDATE_OR_ADD', payload: {id: d.id, data: e.target.value}}) + }}/> + +
    + })} +
    + +
    +
+
+} \ No newline at end of file diff --git a/src/main/webapp/src/components/Club/HoraireEditor.jsx b/src/main/webapp/src/components/Club/HoraireEditor.jsx new file mode 100644 index 0000000..3b6dd25 --- /dev/null +++ b/src/main/webapp/src/components/Club/HoraireEditor.jsx @@ -0,0 +1,95 @@ +import {useEffect, useReducer, useState} from "react"; +import {SimpleReducer} from "../../utils/SimpleReducer.jsx"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {faAdd, faTrashCan} from "@fortawesome/free-solid-svg-icons"; + +function timeNumberToSting(nbMin) { + return String(Math.floor(nbMin / 60)).padStart(2, '0') + ":" + String(nbMin % 60).padStart(2, '0') +} + +function timeStringToNumber(time) { + let times = time.split(':'); + return parseInt(times[0]) * 60 + parseInt(times[1]); +} + +export function HoraireEditor({data}) { + const [state, dispatch] = useReducer(SimpleReducer, []) + const [out_data, setOutData] = useState({}) + + useEffect(() => { + if (data.training_day_time === null) + return + JSON.parse(data.training_day_time).forEach((d, index) => { + dispatch({type: 'UPDATE_OR_ADD', payload: {id: index, data: d}}) + }) + }, [data.training_day_time]); + + useEffect(() => { + setOutData(state.map(d => { + return {day: d.data.day, time_start: d.data.time_start, time_end: d.data.time_end} + })) + }, [state]); + + const sortHoraire = (a, b) => { + if (a.data.day === b.data.day) + return a.data.time_start - b.data.time_start; + return a.data.day - b.data.day; + } + + return
+ + Horaires d'entrainements +
    + {state.map((d, index) => { + return
    + + de + { + d.data.time_start = timeStringToNumber(e.target.value) + dispatch({type: 'UPDATE_OR_ADD', payload: {id: d.id, data: d.data}}) + dispatch({type: 'SORT', payload: sortHoraire}) + }}/> + à + { + d.data.time_end = timeStringToNumber(e.target.value) + dispatch({type: 'UPDATE_OR_ADD', payload: {id: d.id, data: d.data}}) + dispatch({type: 'SORT', payload: sortHoraire}) + }}/> + +
    + })} +
    + +
    +
+
+} \ No newline at end of file diff --git a/src/main/webapp/src/pages/admin/club/LocationEditor.jsx b/src/main/webapp/src/components/Club/LocationEditor.jsx similarity index 53% rename from src/main/webapp/src/pages/admin/club/LocationEditor.jsx rename to src/main/webapp/src/components/Club/LocationEditor.jsx index 948649d..e984cb4 100644 --- a/src/main/webapp/src/pages/admin/club/LocationEditor.jsx +++ b/src/main/webapp/src/components/Club/LocationEditor.jsx @@ -1,10 +1,10 @@ import {useEffect, useReducer, useRef, useState} from "react"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; -import {faPen, faTrashCan} from "@fortawesome/free-solid-svg-icons"; +import {faAdd, faPen, faTrashCan} from "@fortawesome/free-solid-svg-icons"; import proj4 from "proj4"; -import {useFetch} from "../../../hooks/useFetch.js"; +import {useFetch} from "../../hooks/useFetch.js"; import {MapContainer, Marker, TileLayer} from "react-leaflet"; -import {SimpleReducer} from "../../../utils/SimpleReducer.jsx"; +import {SimpleReducer} from "../../utils/SimpleReducer.jsx"; export function LocationEditor({data, setModal, sendData}) { const [state, dispatch] = useReducer(SimpleReducer, []) @@ -40,64 +40,46 @@ export function LocationEditor({data, setModal, sendData}) { })) }, [state]); - return
+ return
Lieux d'entrainements -
-
    - {state.map((d, index) => { - return
    -
    {d.data.text}
    - - -
    - })} -
+
    + {state.map((d, index) => { + return
    + { + }}/> -
    + + +
+ })} +
+ +
+
} export function LocationEditorModal({modal, sendData}) { - return -} - -proj4.defs("EPSG:9794", "+proj=lcc +lat_1=44 +lat_2=49 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"); - -function convertLambert93ToLatLng(x, y) { - const lambertPoint = proj4.toPoint([x, y]); - const wgs84Point = proj4("EPSG:9794", "EPSG:4326", lambertPoint); - return {lat: wgs84Point.y, lng: wgs84Point.x}; -} - -function LocationEditorModalBody({modal}) { const [location, setLocation] = useState("") const [locationObj, setLocationObj] = useState({text: "", lng: undefined, lat: undefined}) const [mapPosition, setMapPosition] = useState([46.652195, 2.430226]) @@ -107,6 +89,7 @@ function LocationEditorModalBody({modal}) { useEffect(() => { if (modal.data !== undefined) { setLocation(modal.data.text) + setLocationObj(modal.data) } }, [modal]) @@ -143,34 +126,59 @@ function LocationEditorModalBody({modal}) { return () => clearTimeout(delayDebounceFn) }, [locationObj, modal]) + return