wip: club end main
This commit is contained in:
parent
1b74c0a3bd
commit
e1a8c90f3e
@ -57,12 +57,12 @@ export function CountryList({name, text, value, values = undefined, disabled = f
|
||||
return <OptionField name={name} text={text} value={value} values={values} disabled={disabled}/>
|
||||
}
|
||||
|
||||
export function TextField({name, text, value, placeholder, type = "text", disabled = false}) {
|
||||
export function TextField({name, text, value, placeholder, type = "text", disabled = false, required = true}) {
|
||||
return <div className="row">
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text" id={name}>{text}</span>
|
||||
<input type={type} className="form-control" placeholder={placeholder ? placeholder : text} aria-label={name}
|
||||
name={name} aria-describedby={name} defaultValue={value} disabled={disabled} required/>
|
||||
name={name} aria-describedby={name} defaultValue={value} disabled={disabled} required={required}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -8,14 +8,11 @@ import {AxiosError} from "../../../components/AxiosError.jsx";
|
||||
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, 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";
|
||||
import {LocationEditor, LocationEditorModal} from "./LocationEditor.jsx";
|
||||
|
||||
const vite_url = import.meta.env.VITE_URL;
|
||||
|
||||
@ -71,43 +68,82 @@ export function ClubPage() {
|
||||
}
|
||||
|
||||
function InformationForm({data}) {
|
||||
return <div className="card mb-4">
|
||||
<div className="card-header">Licence n°{data.no_affiliation}</div>
|
||||
<div className="card-body text-center">
|
||||
const [switchOn, setSwitchOn] = useState(data.international);
|
||||
const [modal, setModal] = useState({id: -1})
|
||||
const locationModalCallback = useRef(null)
|
||||
|
||||
<TextField name="clubId" text="ClubID" value={data.clubId} disabled={true}/>
|
||||
<TextField name="name" text="Nom" value={data.name}/>
|
||||
<TextField name="siret" text="SIRET" value={data.siret} type="number"/>
|
||||
<TextField name="rna" text="RNA" value={data.rna}/>
|
||||
<CountryList name="country" text="Pays" value={data.country}/>
|
||||
<img
|
||||
src={`${vite_url}/api/club/${data.id}/logo`}
|
||||
alt="avatar"
|
||||
className="img-fluid" style={{object_fit: 'contain', maxHeight: '15em'}}/>
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
<div className="mb-3">
|
||||
<div className="input-group">
|
||||
<label className="input-group-text" htmlFor="url_photo">Blason</label>
|
||||
<input type="file" className="form-control" id="url_photo" name="url_photo"
|
||||
accept=".jpg,.jpeg,.gif,.png,.svg"/>
|
||||
const formData = new FormData(event.target);
|
||||
|
||||
toast.promise(
|
||||
apiAxios.post(`/club/${data.id}`, formData),
|
||||
{
|
||||
pending: "Enregistrement du club en cours",
|
||||
success: "Club enregistrée avec succès 🎉",
|
||||
error: "Échec de l'enregistrement du club 😕"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return <>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="card mb-4">
|
||||
<div className="card-header">Licence n°{data.no_affiliation}</div>
|
||||
<div className="card-body text-center">
|
||||
|
||||
<TextField name="clubId" text="ClubID" value={data.clubId} disabled={true}/>
|
||||
<TextField name="name" text="Nom" value={data.name}/>
|
||||
<CountryList name="country" text="Pays" value={data.country}/>
|
||||
<img
|
||||
src={`${vite_url}/api/club/${data.id}/logo`}
|
||||
alt="avatar"
|
||||
className="img-fluid" style={{object_fit: 'contain', maxHeight: '15em'}}/>
|
||||
|
||||
<div className="mb-3">
|
||||
<div className="input-group">
|
||||
<label className="input-group-text" htmlFor="url_photo">Blason</label>
|
||||
<input type="file" className="form-control" id="url_photo" name="url_photo"
|
||||
accept=".jpg,.jpeg,.gif,.png,.svg"/>
|
||||
</div>
|
||||
<div className="form-text" id="url_photo">Laissez vide pour ne rien changer.</div>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<div className="input-group-text">
|
||||
<input type="checkbox" id="inputGroupSelect01" className="form-check-input mt-0" name="international"
|
||||
checked={switchOn} onChange={() => setSwitchOn(!switchOn)}/>
|
||||
</div>
|
||||
<label className="input-group-text" htmlFor="inputGroupSelect01">Club externe</label>
|
||||
</div>
|
||||
{!switchOn && <>
|
||||
<TextField name="siret" text="SIRET" value={data.siret} type="number"/>
|
||||
<TextField name="rna" text="RNA" value={data.rna} required={false}/>
|
||||
|
||||
<ContactEditor data={data}/>
|
||||
<LocationEditor data={data} setModal={setModal} sendData={locationModalCallback}/>
|
||||
<HoraireEditor data={data}/>
|
||||
|
||||
<TextField name="contact_intern" text="Contact interne" value={data.contact_intern} required={false}/>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<button type="submit" className="btn btn-primary">Enregistrer</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-text" id="url_photo">Laissez vide pour ne rien changer.</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<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}/>
|
||||
|
||||
</div>
|
||||
</div>;
|
||||
<LocationEditorModal modal={modal} sendData={locationModalCallback}/>
|
||||
</>
|
||||
}
|
||||
|
||||
export function ContactEditor({data}) {
|
||||
const [state, dispatch] = useReducer(SimpleReducer, [])
|
||||
const [out_data, setOutData] = useState({})
|
||||
|
||||
useEffect(() => {
|
||||
for (const key in data.contact) {
|
||||
@ -115,9 +151,19 @@ export function ContactEditor({data}) {
|
||||
}
|
||||
}, [data.contact]);
|
||||
|
||||
useEffect(() => {
|
||||
let out_data2 = {}
|
||||
state.forEach(d => {
|
||||
if (d.data !== undefined)
|
||||
out_data2[d.id] = d.data
|
||||
})
|
||||
setOutData(out_data2)
|
||||
}, [state]);
|
||||
|
||||
return <div className="row">
|
||||
<input name="contact" value={JSON.stringify(out_data)} readOnly hidden/>
|
||||
<span className="input-group-text">Contacts</span>
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text">Contact</span>
|
||||
<ul className="list-group form-control">
|
||||
{state.map((d, index) => {
|
||||
if (d.data === undefined)
|
||||
@ -152,4 +198,76 @@ export function ContactEditor({data}) {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
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]);
|
||||
|
||||
return <div className="row">
|
||||
<input name="training_day_time" value={JSON.stringify(out_data)} readOnly hidden/>
|
||||
<span className="input-group-text">Horaires d'entrainements</span>
|
||||
<div className="input-group mb-3">
|
||||
<ul className="list-group form-control">
|
||||
{state.map((d, index) => {
|
||||
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.data.day}
|
||||
onChange={(e) => {
|
||||
d.data.day = e.target.value
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: {id: d.id, data: d.data}})
|
||||
}}>
|
||||
<option value="0">Lundi</option>
|
||||
<option value="1">Mardi</option>
|
||||
<option value="2">Mercredi</option>
|
||||
<option value="3">Jeudi</option>
|
||||
<option value="4">Vendredi</option>
|
||||
<option value="5">Samedi</option>
|
||||
<option value="6">Dimanche</option>
|
||||
</select>
|
||||
<span className="input-group-text">de</span>
|
||||
<input type="time" className="form-control" defaultValue={timeNumberToSting(d.data.time_start)} required
|
||||
onChange={(e) => {
|
||||
d.data.time_start = timeStringToNumber(e.target.value)
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: {id: d.id, data: d.data}})
|
||||
}}/>
|
||||
<span className="input-group-text">à</span>
|
||||
<input type="time" className="form-control" defaultValue={timeNumberToSting(d.data.time_end)} required
|
||||
onChange={(e) => {
|
||||
d.data.time_end = timeStringToNumber(e.target.value)
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: {id: d.id, data: d.data}})
|
||||
}}/>
|
||||
<button className="btn btn-danger" type="button"
|
||||
onClick={() => dispatch({type: 'REMOVE', payload: d.id})}><FontAwesomeIcon
|
||||
icon={faTrashCan}/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@ -6,9 +6,9 @@ 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})
|
||||
export function LocationEditor({data, setModal, sendData}) {
|
||||
const [state, dispatch] = useReducer(SimpleReducer, [])
|
||||
const [out_data, setOutData] = useState({})
|
||||
|
||||
useEffect(() => {
|
||||
if (data.training_location === null)
|
||||
@ -18,7 +18,7 @@ export function LocationEditor({data}) {
|
||||
})
|
||||
}, [data.training_location]);
|
||||
|
||||
const sendAffiliation = (e) => {
|
||||
sendData.current = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(e.target);
|
||||
@ -34,15 +34,25 @@ export function LocationEditor({data}) {
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setOutData(state.map(d => {
|
||||
return {text: d.data.text, lat: d.data.lat, lng: d.data.lng}
|
||||
}))
|
||||
}, [state]);
|
||||
|
||||
return <div className="row">
|
||||
<input name="training_location" value={JSON.stringify(out_data)} readOnly hidden/>
|
||||
<span className="input-group-text">Lieux d'entrainements</span>
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text">Lieux d'entrainement</span>
|
||||
<ul className="list-group form-control">
|
||||
{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)}>
|
||||
data-bs-target="#EditModal" onClick={e => {
|
||||
e.preventDefault();
|
||||
setModal(d);
|
||||
}}>
|
||||
<FontAwesomeIcon icon={faPen}/></button>
|
||||
<button className="badge btn btn-danger rounded-pill"
|
||||
onClick={() => dispatch({type: 'REMOVE', payload: d.id})}>
|
||||
@ -50,27 +60,31 @@ export function LocationEditor({data}) {
|
||||
</div>
|
||||
})}
|
||||
</ul>
|
||||
<div className="modal fade" id="EditModal" tabIndex="-1" aria-labelledby="EditModalLabel"
|
||||
aria-hidden="true">
|
||||
<div className="modal-dialog">
|
||||
<form onSubmit={e => sendAffiliation(e)}>
|
||||
<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">
|
||||
<LocationEditorModalBody modal={modal}/>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal">Enregistrer</button>
|
||||
<button type="reset" className="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
export function LocationEditorModal({modal, sendData}) {
|
||||
return <div className="modal fade" id="EditModal" tabIndex="-1" aria-labelledby="EditModalLabel"
|
||||
aria-hidden="true">
|
||||
<div className="modal-dialog">
|
||||
<form onSubmit={e => sendData.current(e)}>
|
||||
<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">
|
||||
<LocationEditorModalBody modal={modal}/>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal">Enregistrer</button>
|
||||
<button type="reset" className="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user