feat: club

This commit is contained in:
2024-07-17 10:44:04 +02:00
parent edcda185db
commit d268461bfd
8 changed files with 326 additions and 19 deletions

View File

@@ -70,7 +70,7 @@ export function ClubList() {
}
return <>
<h2>Club</h2>
<h2>Club </h2>
<div>
<div className="row">
<div className="col-lg-9">
@@ -85,8 +85,11 @@ export function ClubList() {
</div>
<div className="col-lg-3">
<div className="mb-4">
<button className="btn btn-primary" onClick={() => navigate("../affiliation/request")}>Demande en cours</button>
<button className="btn btn-primary" onClick={() => navigate("new")}>Ajouter une affiliation</button>
<div className="mb-2">
<button className="btn btn-primary" onClick={() => navigate("../affiliation/request")}>Demande d'affiliation en cours
</button>
</div>
<button className="btn btn-primary" onClick={() => navigate("new")}>Ajouter un club</button>
</div>
<div className="card mb-4">
<div className="card-header">Filtre</div>

View File

@@ -22,7 +22,7 @@ export function ClubPage() {
const navigate = useNavigate();
const setLoading = useLoadingSwitcher()
const {data, error} = useFetch(`/club/${id}`, setLoading, 1)
const {data, refresh, error} = useFetch(`/club/${id}`, setLoading, 1)
const handleRm = () => {
toast.promise(
@@ -38,7 +38,7 @@ export function ClubPage() {
}
return <>
<h2>Page membre</h2>
<h2>Page club</h2>
<button type="button" className="btn btn-link" onClick={() => navigate("/admin/club")}>
&laquo; retour
</button>
@@ -92,7 +92,7 @@ function InformationForm({data}) {
<form onSubmit={handleSubmit}>
<div className="card mb-4">
<input name="id" value={data.id} readOnly hidden/>
<div className="card-header">Licence n°{data.no_affiliation}</div>
<div className="card-header">Affiliation n°{data.no_affiliation}</div>
<div className="card-body text-center">
<TextField name="clubId" text="ClubID" value={data.clubId} disabled={true}/>
@@ -120,11 +120,12 @@ function InformationForm({data}) {
</div>
</div>
{!switchOn && <>
<TextField name="siret" text="SIRET" value={data.siret} type="number"/>
<TextField name="siret" text="SIRET" value={data.siret} required={false} type="number"/>
<TextField name="rna" text="RNA" value={data.rna} required={false}/>
<TextField name="contact_intern" text="Contact interne" value={data.contact_intern} required={false}
placeholder="example@test.com"/>
<TextField name="address" text="Adresse administrative" value={data.address} placeholder="Adresse administrative"/>
<TextField name="address" text="Adresse administrative" value={data.address} required={false}
placeholder="Adresse administrative"/>
<div className="mb-3">
<div className="input-group">

View File

@@ -1,16 +1,111 @@
import {useNavigate} from "react-router-dom";
import {LoadingProvider} from "../../../hooks/useLoading.jsx";
import {useFetch} from "../../../hooks/useFetch.js";
import {toast} from "react-toastify";
import {apiAxios} from "../../../utils/Tools.js";
import {CountryList, TextField} from "../../../components/MemberCustomFiels.jsx";
import {useRef, useState} from "react";
import {LocationEditor, LocationEditorModal} from "../../../components/Club/LocationEditor.jsx";
import {ContactEditor} from "../../../components/Club/ContactEditor.jsx";
import {HoraireEditor} from "../../../components/Club/HoraireEditor.jsx";
export function NewClubPage() {
const navigate = useNavigate();
const navigate = useNavigate()
return <>
<h2>Page affiliation</h2>
<button type="button" className="btn btn-link" onClick={() => navigate("/admin/affiliation")}>
<h2>Page nouveau club</h2>
<button type="button" className="btn btn-link" onClick={() => navigate("/admin/club")}>
&laquo; retour
</button>
<div>
<div className="row">
<div className="col-lg-9">
<LoadingProvider>
<InformationForm/>
</LoadingProvider>
</div>
</div>
</div>
</>
}
}
function InformationForm() {
const [switchOn, setSwitchOn] = useState(false);
const [modal, setModal] = useState({id: -1})
const locationModalCallback = useRef(null)
const navigate = useNavigate()
const {data} = useFetch(`/club/contact_type`)
const handleSubmit = (event) => {
event.preventDefault();
const formData = new FormData(event.target);
toast.promise(
apiAxios.put(`/club`, formData),
{
pending: "Création du club en cours",
success: "Club créé avec succès 🎉",
error: "Échec de la création du club 😕"
}
).then(data => {
navigate(`/admin/club/${data.data}`);
})
}
return <>
<form onSubmit={handleSubmit}>
<div className="card mb-4">
<div className="card-header">Nouveau club</div>
<div className="card-body text-center">
<TextField name="name" text="Nom*"/>
<CountryList name="country" text="Pays*" value={"fr"}/>
<div className="mb-3">
<div className="input-group">
<label className="input-group-text" htmlFor="logo">Blason</label>
<input type="file" className="form-control" id="logo" name="logo"
accept=".jpg,.jpeg,.gif,.png,.svg"/>
</div>
</div>
<div className="input-group mb-3">
<div className="input-group-text">
<input type="checkbox" className="form-check-input mt-0" name="international" id="international"
checked={switchOn} onChange={() => setSwitchOn(!switchOn)}/>
<label className="input-group-text" htmlFor="international">Club externe</label>
</div>
</div>
{!switchOn && <>
<TextField name="siret" text="SIRET" required={false} type="number"/>
<TextField name="rna" text="RNA" required={false}/>
<TextField name="contact_intern" text="Contact interne" required={false} placeholder="example@test.com"/>
<TextField name="address" text="Adresse administrative" required={false} placeholder="Adresse administrative"/>
<div className="mb-3">
<div className="input-group">
<label className="input-group-text" htmlFor="status">Status</label>
<input type="file" className="form-control" id="status" name="status" accept=".pdf,.txt"/>
</div>
</div>
<ContactEditor data={{contact: {}, contactMap: data}}/>
<LocationEditor data={{training_location: null}} setModal={setModal} sendData={locationModalCallback}/>
<HoraireEditor data={{training_day_time: null}}/>
</>
}
</div>
<div className="row mb-3">
<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>
</form>
<LocationEditorModal modal={modal} sendData={locationModalCallback}/>
</>
}

View File

@@ -0,0 +1,158 @@
import {useNavigate, useParams} from "react-router-dom";
import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {useFetch} from "../../../hooks/useFetch.js";
import {toast} from "react-toastify";
import {apiAxios} from "../../../utils/Tools.js";
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
import {AxiosError} from "../../../components/AxiosError.jsx";
import {AffiliationCard} from "./AffiliationCard.jsx";
import {CountryList, TextField} from "../../../components/MemberCustomFiels.jsx";
import {useRef, useState} from "react";
import {LocationEditor, LocationEditorModal} from "../../../components/Club/LocationEditor.jsx";
import {ContactEditor} from "../../../components/Club/ContactEditor.jsx";
import {HoraireEditor} from "../../../components/Club/HoraireEditor.jsx";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faFilePdf} from "@fortawesome/free-solid-svg-icons";
const vite_url = import.meta.env.VITE_URL;
export function ClubPage() {
const {id} = useParams()
const navigate = useNavigate();
const setLoading = useLoadingSwitcher()
const {data, error} = useFetch(`/club/${id}`, setLoading, 1)
const handleRm = () => {
toast.promise(
apiAxios.delete(`/club/${id}`),
{
pending: "Suppression du club en cours...",
success: "Club supprimé avec succès 🎉",
error: "Échec de la suppression du club 😕"
}
).then(_ => {
navigate("/admin/club")
})
}
return <>
<h2>Page club</h2>
<button type="button" className="btn btn-link" onClick={() => navigate("/admin/club")}>
&laquo; retour
</button>
{data
? <div>
<div className="row">
<div className="col-lg-9">
<LoadingProvider>
<InformationForm data={data}/>
</LoadingProvider>
</div>
<div className="col-lg-3">
<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 club
</button>
</div>
<ConfirmDialog title="Supprimer le club"
message="Êtes-vous sûr de vouloir supprimer ce club ?"
onConfirm={handleRm}/>
</div>
</div>
</div>
: error && <AxiosError error={error}/>
}
</>
}
function InformationForm({data}) {
const [switchOn, setSwitchOn] = useState(data.international);
const [modal, setModal] = useState({id: -1})
const locationModalCallback = useRef(null)
const handleSubmit = (event) => {
event.preventDefault();
const formData = new FormData(event.target);
toast.promise(
apiAxios.put(`/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">
<input name="id" value={data.id} readOnly hidden/>
<div className="card-header">Affiliation 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.clubId}/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="logo">Blason</label>
<input type="file" className="form-control" id="logo" name="logo"
accept=".jpg,.jpeg,.gif,.png,.svg"/>
</div>
<div className="form-text" id="logo">Laissez vide pour ne rien changer.</div>
</div>
<div className="input-group mb-3">
<div className="input-group-text">
<input type="checkbox" className="form-check-input mt-0" name="international" id="international"
checked={switchOn} onChange={() => setSwitchOn(!switchOn)}/>
<label className="input-group-text" htmlFor="international">Club externe</label>
</div>
</div>
{!switchOn && <>
<TextField name="siret" text="SIRET" value={data.siret} type="number"/>
<TextField name="rna" text="RNA" value={data.rna} required={false}/>
<TextField name="contact_intern" text="Contact interne" value={data.contact_intern} required={false}
placeholder="example@test.com"/>
<TextField name="address" text="Adresse administrative" value={data.address} placeholder="Adresse administrative"/>
<div className="mb-3">
<div className="input-group">
<label className="input-group-text" htmlFor="status">Status</label>
<a href={`${vite_url}/api/club/${data.id}/status`} target='_blank'>
<button className="btn btn-outline-secondary" type="button" id="button-addon1"
onClick={e => null}><FontAwesomeIcon icon={faFilePdf}></FontAwesomeIcon>
</button>
</a>
<input type="file" className="form-control" id="status" name="status" accept=".pdf,.txt"/>
</div>
<div className="form-text" id="status">Laissez vide pour ne rien changer.</div>
</div>
<ContactEditor data={data}/>
<LocationEditor data={data} setModal={setModal} sendData={locationModalCallback}/>
<HoraireEditor data={data}/>
</>
}
</div>
<div className="row mb-3">
<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>
</form>
<LocationEditorModal modal={modal} sendData={locationModalCallback}/>
</>
}