wip: addr field
This commit is contained in:
parent
76d7a28678
commit
6c4b01590d
@ -5,6 +5,7 @@ import fr.titionfire.ffsaf.domain.service.ClubService;
|
||||
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleClub;
|
||||
import fr.titionfire.ffsaf.rest.from.FullClubForm;
|
||||
import fr.titionfire.ffsaf.utils.Contact;
|
||||
import fr.titionfire.ffsaf.utils.GroupeUtils;
|
||||
import fr.titionfire.ffsaf.utils.PageResult;
|
||||
import fr.titionfire.ffsaf.utils.Utils;
|
||||
@ -76,7 +77,9 @@ public class ClubEndpoints {
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<SimpleClub> getById(@PathParam("id") long id) {
|
||||
return clubService.getById(id).onItem().invoke(checkPerm).map(SimpleClub::fromModel);
|
||||
return clubService.getById(id).onItem().invoke(checkPerm).map(SimpleClub::fromModel).invoke(m -> {
|
||||
m.setContactMap(Contact.toSite());
|
||||
});
|
||||
}
|
||||
|
||||
@PUT
|
||||
|
||||
@ -8,6 +8,7 @@ import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@ -29,6 +30,7 @@ public class SimpleClub {
|
||||
private String SIRET;
|
||||
private String no_affiliation;
|
||||
private boolean international;
|
||||
private HashMap<String, String> contactMap = null;
|
||||
|
||||
public static SimpleClub fromModel(ClubModel model) {
|
||||
if (model == null)
|
||||
|
||||
@ -2,6 +2,9 @@ package fr.titionfire.ffsaf.utils;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
|
||||
import javax.naming.ldap.HasControls;
|
||||
import java.util.HashMap;
|
||||
|
||||
@RegisterForReflection
|
||||
public enum Contact {
|
||||
COURRIEL("Courriel"),
|
||||
@ -20,8 +23,11 @@ public enum Contact {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
public static HashMap<String, String> toSite() {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
for (Contact contact : Contact.values()) {
|
||||
map.put(contact.toString(), contact.name);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
83
src/main/webapp/src/components/ListEditor.jsx
Normal file
83
src/main/webapp/src/components/ListEditor.jsx
Normal file
@ -0,0 +1,83 @@
|
||||
import {useEffect, useReducer, useState} from "react";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faPen, faTrashCan} from "@fortawesome/free-solid-svg-icons";
|
||||
import {AxiosError} from "./AxiosError.jsx";
|
||||
|
||||
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 ListEditorTest() {
|
||||
const [html, dispatch] = ListEditor(ListHTML)
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: {id: 1, content: "data in"}})
|
||||
}, []);
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
export function ListEditor(ListItem) {
|
||||
const [modal, setModal] = useState({id: -1})
|
||||
const [state, dispatch] = useReducer(SimpleReducer, [])
|
||||
|
||||
const sendAffiliation = (e) => {
|
||||
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: e})
|
||||
}
|
||||
|
||||
return [<>
|
||||
<ul className="list-group">
|
||||
{state.map((d, index) => {
|
||||
return <div key={index} className={"list-group-item d-flex justify-content-between align-items-start"}>
|
||||
<ListItem data={d}/>
|
||||
<button className="badge btn btn-primary rounded-pill" data-bs-toggle="modal"
|
||||
data-bs-target="#EditModal" onClick={_ => setModal(d)}>
|
||||
<FontAwesomeIcon icon={faPen}/></button>
|
||||
<button className="badge btn btn-danger rounded-pill"
|
||||
onClick={() => dispatch({type: 'REMOVE', payload: d.id})}>
|
||||
<FontAwesomeIcon icon={faTrashCan}/></button>
|
||||
</div>
|
||||
})}
|
||||
</ul>
|
||||
<div className="modal fade" id="EditModal" tabIndex="-1" aria-labelledby="EditModalLabel"
|
||||
aria-hidden="true">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<form onSubmit={e => sendAffiliation(e, dispatch)}>
|
||||
<input name="id" value={modal.id} readOnly hidden/>
|
||||
</form>
|
||||
<ModalContent affiliation={modalAffiliation} dispatch={dispatch}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
, dispatch]
|
||||
}
|
||||
|
||||
function ListHTML({
|
||||
data
|
||||
}) {
|
||||
return <div className="me-auto">{data.content}</div>
|
||||
}
|
||||
@ -4,9 +4,7 @@ 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 "../../../components/MemberCustomFiels.jsx";
|
||||
import {apiAxios, getSaison} from "../../../utils/Tools.js";
|
||||
import {Input} from "../../../components/Input.jsx";
|
||||
import {toast} from "react-toastify";
|
||||
|
||||
function affiliationReducer(affiliation, action) {
|
||||
|
||||
@ -9,9 +9,38 @@ import {AffiliationCard} from "./AffiliationCard.jsx";
|
||||
import {CheckField, CountryList, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
|
||||
import {MapContainer, Marker, Popup, TileLayer, useMap} from 'react-leaflet'
|
||||
import {ListEditorTest} from "../../../components/ListEditor.jsx";
|
||||
import {useEffect, useReducer, useState} from "react";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faPen, faTrashCan} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
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();
|
||||
@ -41,15 +70,19 @@ export function ClubPage() {
|
||||
? <div>
|
||||
<div className="row">
|
||||
<div className="col-lg-8">
|
||||
<LoadingProvider><InformationForm data={data}/></LoadingProvider>
|
||||
<LoadingProvider>
|
||||
<InformationForm data={data}/>
|
||||
</LoadingProvider>
|
||||
</div>
|
||||
<div className="col-lg-4">
|
||||
<LoadingProvider><AffiliationCard clubData={data}/></LoadingProvider>
|
||||
<div className="col" style={{textAlign: 'right', marginTop: '1em'}}>
|
||||
<button className="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirm-delete">Supprimer le compte
|
||||
<button className="btn btn-danger btn-sm" data-bs-toggle="modal"
|
||||
data-bs-target="#confirm-delete">Supprimer le compte
|
||||
</button>
|
||||
</div>
|
||||
<ConfirmDialog title="Supprimer le compte" message="Êtes-vous sûr de vouloir supprimer ce compte ?"
|
||||
<ConfirmDialog title="Supprimer le compte"
|
||||
message="Êtes-vous sûr de vouloir supprimer ce compte ?"
|
||||
onConfirm={handleRm}/>
|
||||
</div>
|
||||
</div>
|
||||
@ -83,18 +116,150 @@ function InformationForm({data}) {
|
||||
<div className="form-text" id="url_photo">Laissez vide pour ne rien changer.</div>
|
||||
</div>
|
||||
|
||||
<TextField name="contact" text="Contact" value={data.contact}/>
|
||||
<ContactEditor data={data}/>
|
||||
<LocationEditor data={data}/>
|
||||
|
||||
<TextField name="training_location" text="Lieux d'entrainement" value={data.training_location}/>
|
||||
<TextField name="training_day_time" text="Horaire d'entrainement" value={data.training_day_time}/>
|
||||
<TextField name="contact_intern" text="Contact" value={"contact_intern"}/>
|
||||
<CheckField name="international" text="Club international" value={data.international}/>
|
||||
|
||||
|
||||
<MainMap/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
// https://annuaire-entreprises.data.gouv.fr/entreprise/la-mesnie-des-chevaliers-de-st-georges-et-de-st-michel-500213731
|
||||
|
||||
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 <>
|
||||
<ul className="list-group">
|
||||
{state.map((d, index) => {
|
||||
return <div key={index} className={"list-group-item d-flex justify-content-between align-items-start"}>
|
||||
<div className="me-auto">{d.data.text}</div>
|
||||
<button className="badge btn btn-primary rounded-pill" data-bs-toggle="modal"
|
||||
data-bs-target="#EditModal" onClick={_ => setModal(d)}>
|
||||
<FontAwesomeIcon icon={faPen}/></button>
|
||||
<button className="badge btn btn-danger rounded-pill"
|
||||
onClick={() => dispatch({type: 'REMOVE', payload: d.id})}>
|
||||
<FontAwesomeIcon icon={faTrashCan}/></button>
|
||||
</div>
|
||||
})}
|
||||
</ul>
|
||||
<div className="modal fade" id="EditModal" tabIndex="-1" aria-labelledby="EditModalLabel"
|
||||
aria-hidden="true">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h1 className="modal-title fs-5" id="EditModalLabel">Edition de l'adresse</h1>
|
||||
<button type="button" className="btn-close" data-bs-dismiss="modal"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="input-group mb-3 justify-content-md-center">
|
||||
<form onSubmit={e => sendAffiliation(e, dispatch)}>
|
||||
<input name="id" value={modal.id} readOnly hidden/>
|
||||
</form>
|
||||
<Autoc/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
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 <>
|
||||
<div className="form-group">
|
||||
<label htmlFor="input-datalist">Timezone</label>
|
||||
<input role="combobox" aria-autocomplete="list" aria-expanded="false" autoComplete="off"
|
||||
placeholder="Chercher une adresse..." aria-label="Recherche"
|
||||
className="form-control" list="addr" value={location}
|
||||
onChange={e => setLocation(e.target.value)}/>
|
||||
<datalist id="addr">
|
||||
{data && data.features.map((d, index) => {
|
||||
return <option key={index}>{d.properties.label}</option>
|
||||
})}
|
||||
</datalist>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
export function ContactEditor({
|
||||
data
|
||||
}) {
|
||||
const [state, dispatch] = useReducer(SimpleReducer, [])
|
||||
|
||||
useEffect(() => {
|
||||
for (const key in data.contact) {
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: {id: key, data: data.contact[key]}})
|
||||
}
|
||||
}, [data.contact]);
|
||||
|
||||
return <>
|
||||
<ul className="list-group">
|
||||
{state.map((d, index) => {
|
||||
if (d.data === undefined)
|
||||
return;
|
||||
|
||||
return <div key={index} className={"list-group-item d-flex justify-content-between align-items-start"}>
|
||||
<div className="input-group">
|
||||
<select className="form-select" aria-label="type" defaultValue={d.id}
|
||||
onChange={(e) => {
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: {id: d.id, data: undefined}})
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: {id: e.target.value, data: d.data}})
|
||||
}}>
|
||||
{Object.keys(data.contactMap).map((key, _) => {
|
||||
let b = false;
|
||||
for (let s of state) {
|
||||
if (s.id === key && s.data !== undefined) b = true;
|
||||
}
|
||||
return (<option key={key} value={key} disabled={b}>{data.contactMap[key]}</option>)
|
||||
})}
|
||||
</select>
|
||||
<input type="text" className="form-control" defaultValue={d.data} required
|
||||
onChange={(e) => {
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: {id: d.id, data: e.target.value}})
|
||||
}}/>
|
||||
<button className="btn btn-danger" type="button"
|
||||
onClick={() => dispatch({type: 'REMOVE', payload: d.id})}><FontAwesomeIcon
|
||||
icon={faTrashCan}/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
})}
|
||||
</ul>
|
||||
</>
|
||||
}
|
||||
|
||||
// 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!!");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user