diff --git a/src/main/java/fr/titionfire/BlackPage.java b/src/main/java/fr/titionfire/BlackPage.java new file mode 100644 index 0000000..a77d835 --- /dev/null +++ b/src/main/java/fr/titionfire/BlackPage.java @@ -0,0 +1,18 @@ +package fr.titionfire; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +@Path("/api") +public class BlackPage { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public Response get() { + return Response.noContent().build(); + } + +} diff --git a/src/main/webapp/package-lock.json b/src/main/webapp/package-lock.json index 7e63a4c..78674e5 100644 --- a/src/main/webapp/package-lock.json +++ b/src/main/webapp/package-lock.json @@ -16,6 +16,7 @@ "axios": "^1.6.5", "browser-image-compression": "^2.0.2", "leaflet": "^1.9.4", + "proj4": "^2.11.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-leaflet": "^4.2.1", @@ -3184,6 +3185,11 @@ "yallist": "^3.0.2" } }, + "node_modules/mgrs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", + "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==" + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -3503,6 +3509,15 @@ "node": ">= 0.8.0" } }, + "node_modules/proj4": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.11.0.tgz", + "integrity": "sha512-SasuTkAx8HnWQHfIyhkdUNJorSJqINHAN3EyMWYiQRVorftz9DHz650YraFgczwgtHOxqnfuDxSNv3C8MUnHeg==", + "dependencies": { + "mgrs": "1.0.0", + "wkt-parser": "^1.3.3" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4423,6 +4438,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wkt-parser": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.3.3.tgz", + "integrity": "sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw==" + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index 39798b9..32fbdf3 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -18,6 +18,7 @@ "axios": "^1.6.5", "browser-image-compression": "^2.0.2", "leaflet": "^1.9.4", + "proj4": "^2.11.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-leaflet": "^4.2.1", diff --git a/src/main/webapp/src/pages/admin/club/ClubPage.jsx b/src/main/webapp/src/pages/admin/club/ClubPage.jsx index 53a79dd..32c0a66 100644 --- a/src/main/webapp/src/pages/admin/club/ClubPage.jsx +++ b/src/main/webapp/src/pages/admin/club/ClubPage.jsx @@ -10,37 +10,15 @@ import {CheckField, CountryList, TextField} from "../../../components/MemberCust import {MapContainer, Marker, Popup, TileLayer, useMap} from 'react-leaflet' import {ListEditorTest} from "../../../components/ListEditor.jsx"; -import {useEffect, useReducer, useState} from "react"; +import {useEffect, useReducer, useRef, useState} from "react"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {faPen, faTrashCan} from "@fortawesome/free-solid-svg-icons"; +import proj4 from "proj4"; +import {SimpleReducer} from "../../../utils/SimpleReducer.jsx"; +import {LocationEditor} from "./LocationEditor.jsx"; const vite_url = import.meta.env.VITE_URL; -function SimpleReducer(datas, action) { - switch (action.type) { - case 'ADD': - return [ - ...datas, - action.payload - ] - case 'REMOVE': - return datas.filter(data => data.id !== action.payload) - case 'UPDATE_OR_ADD': - const index = datas.findIndex(data => data.id === action.payload.id) - if (index === -1) { - return [ - ...datas, - action.payload - ] - } else { - datas[index] = action.payload - return [...datas] - } - default: - throw new Error() - } -} - export function ClubPage() { const {id} = useParams() const navigate = useNavigate(); @@ -69,12 +47,12 @@ export function ClubPage() { {data ?
-
+
-
+
; } -export function LocationEditor({data}) { - const [modal, setModal] = useState({id: -1}) - const [state, dispatch] = useReducer(SimpleReducer, []) - - useEffect(() => { - JSON.parse(data.training_location).forEach((d, index) => { - dispatch({type: 'UPDATE_OR_ADD', payload: {id: index, data: d}}) - }) - }, [data.training_location]); - - const sendAffiliation = (e) => { - - dispatch({type: 'UPDATE_OR_ADD', payload: e}) - } - - return <> -
    - {state.map((d, index) => { - return
    -
    {d.data.text}
    - - -
    - })} -
- - -} - -function Autoc() { - const [location, setLocation] = useState("9 rue Gracchus") - const { - data, - error, - refresh - } = useFetch(`https://api-adresse.data.gouv.fr/search/?q=${encodeURI(location)}&type=housenumber&autocomplete=1`) - - useEffect(() => { - refresh(`https://api-adresse.data.gouv.fr/search/?q=${encodeURI(location)}&type=housenumber&autocomplete=1`) - }, [location]); - - return <> -
- - setLocation(e.target.value)}/> - - {data && data.features.map((d, index) => { - return - })} - -
- -} - -export function ContactEditor({ - data - }) { +export function ContactEditor({data}) { const [state, dispatch] = useReducer(SimpleReducer, []) useEffect(() => { @@ -221,73 +115,41 @@ export function ContactEditor({ } }, [data.contact]); - return <> -
    - {state.map((d, index) => { - if (d.data === undefined) - return; + return
    +
    + Contact +
      + {state.map((d, index) => { + if (d.data === undefined) + return; - return
      -
      - - { - dispatch({type: 'UPDATE_OR_ADD', payload: {id: d.id, data: e.target.value}}) - }}/> - + return
      +
      + + { + dispatch({type: 'UPDATE_OR_ADD', payload: {id: d.id, data: e.target.value}}) + }}/> + +
      -
      - })} -
    - -} - -// 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 ( - <> - - - - - A pretty CSS3 popup.
    Easily customizable. -
    -
    -
    - - - - ) -} - -function SearchBarMap() { - return <> - + })} +
+
+
} \ No newline at end of file diff --git a/src/main/webapp/src/pages/admin/club/LocationEditor.jsx b/src/main/webapp/src/pages/admin/club/LocationEditor.jsx new file mode 100644 index 0000000..4ccb5af --- /dev/null +++ b/src/main/webapp/src/pages/admin/club/LocationEditor.jsx @@ -0,0 +1,162 @@ +import {useEffect, useReducer, useRef, useState} from "react"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {faPen, faTrashCan} from "@fortawesome/free-solid-svg-icons"; +import proj4 from "proj4"; +import {useFetch} from "../../../hooks/useFetch.js"; +import {MapContainer, Marker, TileLayer} from "react-leaflet"; +import {SimpleReducer} from "../../../utils/SimpleReducer.jsx"; + +export function LocationEditor({data}) { + const [modal, setModal] = useState({id: -1}) + const [state, dispatch] = useReducer(SimpleReducer, []) + + useEffect(() => { + if (data.training_location === null) + return + JSON.parse(data.training_location).forEach((d, index) => { + dispatch({type: 'UPDATE_OR_ADD', payload: {id: index, data: d}}) + }) + }, [data.training_location]); + + const sendAffiliation = (e) => { + e.preventDefault(); + + const formData = new FormData(e.target); + dispatch({ + type: 'UPDATE_OR_ADD', payload: { + id: Number(formData.get('id')), + data: { + text: formData.get('loc_text'), + lat: Number(formData.get('loc_lat')), + lng: Number(formData.get('loc_lng')) + } + } + }) + } + + return
+
+ Lieux d'entrainement +
    + {state.map((d, index) => { + return
    +
    {d.data.text}
    + + +
    + })} +
+ +
+
+} + +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]) + const {data, error, refresh} = useFetch(``) + const map = useRef(null) + + useEffect(() => { + if (modal.data !== undefined) { + setLocation(modal.data.text) + } + }, [modal]) + + useEffect(() => { + if (location.length < 3) + return + + const delayDebounceFn = setTimeout(() => { + refresh(`https://api-adresse.data.gouv.fr/search/?q=${encodeURI(location)}&type=housenumber&autocomplete=1`) + }, 500) + return () => clearTimeout(delayDebounceFn) + }, [location]); + + useEffect(() => { + if (data?.features?.length === 1) { + const {lat, lng} = convertLambert93ToLatLng(data.features[0].properties.x, data.features[0].properties.y) + setLocationObj({text: data.features[0].properties.label, lng: lng, lat: lat}) + } else { + setLocationObj({text: "", lng: undefined, lat: undefined}) + } + }, [data]); + + useEffect(() => { + if (locationObj.lat !== undefined) { + setMapPosition([locationObj.lat, locationObj.lng]) + map.current?.setView([locationObj.lat, locationObj.lng], 19) + } else { + map.current?.setView([46.652195, 2.430226], 5) + } + + const delayDebounceFn = setTimeout(() => { + map.current.invalidateSize(false); + }, 300) + return () => clearTimeout(delayDebounceFn) + }, [locationObj, modal]) + + + return <> + + + + +
+
+ + setLocation(e.target.value)}/> + + {data?.features && data.features.map((d, index) => { + return + })} + +
+
+ +
+ + + {locationObj.lat !== undefined && } + +
+ +} diff --git a/src/main/webapp/src/utils/SimpleReducer.jsx b/src/main/webapp/src/utils/SimpleReducer.jsx new file mode 100644 index 0000000..8b395c8 --- /dev/null +++ b/src/main/webapp/src/utils/SimpleReducer.jsx @@ -0,0 +1,24 @@ +export function SimpleReducer(datas, action) { + switch (action.type) { + case 'ADD': + return [ + ...datas, + action.payload + ] + case 'REMOVE': + return datas.filter(data => data.id !== action.payload) + case 'UPDATE_OR_ADD': + const index = datas.findIndex(data => data.id === action.payload.id) + if (index === -1) { + return [ + ...datas, + action.payload + ] + } else { + datas[index] = action.payload + return [...datas] + } + default: + throw new Error() + } +} \ No newline at end of file