feat: mePage

This commit is contained in:
2024-07-18 21:40:36 +02:00
parent 8ba3f45215
commit a58dcdd08e
17 changed files with 333 additions and 38 deletions

View File

@@ -12,6 +12,7 @@ import './App.css'
import 'react-toastify/dist/ReactToastify.css';
import {ClubRoot, getClubChildren} from "./pages/club/ClubRoot.jsx";
import {DemandeAff, DemandeAffOk} from "./pages/DemandeAff.jsx";
import {MePage} from "./pages/MePage.jsx";
const router = createBrowserRouter([
{
@@ -45,6 +46,10 @@ const router = createBrowserRouter([
element: <DemandeAffOk/>
}
]
},
{
path: 'me',
element: <MePage/>
}
]
},
@@ -82,7 +87,7 @@ function Root() {
<div className="container my-4">
<Outlet/>
<ToastContainer
position="top-right"
position="top-center"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}

View File

@@ -1,5 +1,6 @@
import {useEffect, useState} from "react";
import {getCategoryFormBirthDate} from "../utils/Tools.js";
import {useCountries} from "../hooks/useCountries.jsx";
export function BirthDayField({inti_date, inti_category, required = true}) {
const [date, setDate] = useState(inti_date)
@@ -26,7 +27,7 @@ export function BirthDayField({inti_date, inti_category, required = true}) {
<div className="input-group mb-3">
<span className="input-group-text" id="category">Catégorie</span>
<input type="text" className="form-control" placeholder="" name="category"
aria-label="category" value={category? category : ""} aria-describedby="category"
aria-label="category" value={category ? category : ""} aria-describedby="category"
disabled/>
{canUpdate && <button className="btn btn-outline-secondary" type="button" id="button-addon1"
onClick={updateCat}>Mettre à jours</button>}
@@ -63,11 +64,28 @@ export function RoleList({name, text, value, disabled = false}) {
}
export function CountryList({name, text, value, values = undefined, disabled = false}) {
if (values === undefined){
values = {NA: 'Sélectionner...', fr: 'FR', es: 'ES', be: 'BE'}
const country = useCountries('fr')
const [value_, setValue] = useState(value)
if (values === undefined) {
values = {...country}
}
return <OptionField name={name} text={text} value={value} values={values} disabled={disabled}/>
return <div className="row">
<div className="input-group mb-3">
<label className="input-group-text" id={name}>{text}</label>
<select className="form-select" id={name} name={name} value={value_} required disabled={disabled}
onChange={e => setValue(e.target.value)}>
{Object.keys(values).sort((a, b) => {
if (a < b) return -1
if (a > b) return 1
return 0
}).map((key, _) => {
return (<option key={key} value={key}>{values[key]}</option>)
})}
</select>
</div>
</div>
}
export function TextField({name, text, value, placeholder, type = "text", disabled = false, required = true}) {

View File

@@ -80,11 +80,23 @@ function AdminMenu() {
function LoginMenu() {
const {is_authenticated} = useAuth()
return <li className="nav-item">
{!is_authenticated ? (
<div className="nav-link" onClick={() => login()}>Connexion</div>
) : (
<div className="nav-link" onClick={() => logout()}>Déconnexion</div>
)}
</li>
return <>
{!is_authenticated ?
<li className="nav-item">
<div className="nav-link" onClick={() => login()}>Connexion</div>
</li>
:
<li className="nav-item dropdown">
<div className="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Mon compte
</div>
<ul className="dropdown-menu">
<li className="nav-item"><NavLink className="nav-link" to="/me">Mon espace</NavLink></li>
<li className="nav-item">
<div className="nav-link" onClick={() => logout()}>Déconnexion</div>
</li>
</ul>
</li>
}
</>
}

View File

@@ -0,0 +1,24 @@
import {useEffect, useState} from "react";
import {apiAxios} from "../utils/Tools.js";
const countries = {}
export function useCountries(country = 'fr') {
const [out, setOut] = useState(null)
useEffect(() => {
if (countries[country] === undefined) {
console.log('fetch')
apiAxios.get(`/countries/${country}/${country}`).then(data => {
console.log(data.data)
countries[country] = data.data
setOut(data.data)
})
} else {
setOut(countries[country])
}
}, [country]);
return out;
}

View File

@@ -0,0 +1,115 @@
import {LoadingProvider, useLoadingSwitcher} from "../hooks/useLoading.jsx";
import {AxiosError} from "../components/AxiosError.jsx";
import {useFetch} from "../hooks/useFetch.js";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {
faCalendarDay,
faEnvelope, faFlag,
faInfoCircle,
faMars,
faMarsAndVenus,
faUser,
faUserGroup,
faVenus
} from "@fortawesome/free-solid-svg-icons";
const vite_url = import.meta.env.VITE_URL;
export function MePage() {
const setLoading = useLoadingSwitcher()
const {data, error} = useFetch(`/member/me`, setLoading, 1)
return <div>
<h1>Mon espace</h1>
{data
? <div>
<div className="row">
<div className="col-lg-4">
<PhotoCard data={data}/>
</div>
<div className="col-lg-8">
<InformationForm data={data}/>
<div className="row">
<div className="col-md-6">
<LoadingProvider><LicenceCard userData={data}/></LoadingProvider>
</div>
<div className="col-md-6">
<LoadingProvider><SelectCard/></LoadingProvider>
</div>
</div>
</div>
</div>
</div>
: error && <AxiosError error={error}/>
}
</div>
}
export function LicenceCard({userData}) {
return <div className="card mb-4 mb-md-0">
<div className="card-header container-fluid">
<div className="row">
<div className="col">Licence</div>
</div>
</div>
<div className="card-body">
<ul className="list-group">
{userData.licences.map((licence, index) => {
return <div key={index}
className={"list-group-item d-flex justify-content-between align-items-start list-group-item-" +
(licence.validate ? "success" : (licence.certificate ? "warning" : "danger"))}>
<div className="me-auto">{licence?.saison}-{licence?.saison + 1}</div>
</div>
})}
</ul>
</div>
</div>;
}
function PhotoCard({data}) {
return <div className="card mb-4">
<div className="card-header">Licence n°{data.licence}</div>
<div className="card-body text-center">
<div className="input-group mb-3">
<img
src={`${vite_url}/api/member/${data.id}/photo`}
alt="avatar"
className="rounded-circle img-fluid" style={{object_fit: 'contain'}}/>
</div>
</div>
</div>;
}
function SelectCard() {
return <div className="card mb-4 mb-md-0">
<div className="card-header">Sélection en équipe de France</div>
<div className="card-body">
</div>
</div>;
}
export function InformationForm({data}) {
const style = {marginRight: '0.7em'}
return <div className="card mb-4">
<div className="card-header">Information</div>
<div className="card-body">
<div className="row mb-2">
<p>
<FontAwesomeIcon icon={faUser} style={style}/>{data.lname} {data.fname}<br/>
<FontAwesomeIcon icon={faEnvelope} style={style}/>{data.email}<br/>
{data.genre === 'Homme' && <FontAwesomeIcon icon={faMars} style={style}/>
|| data.genre === 'Femme' && <FontAwesomeIcon icon={faVenus} style={style}/>
|| <FontAwesomeIcon icon={faMarsAndVenus} style={style}/>}{data.genre}<br/>
<FontAwesomeIcon icon={faCalendarDay} style={style}/>{data.birth_date ? data.birth_date.split('T')[0] : ''}<br/>
<FontAwesomeIcon icon={faUserGroup} style={style}/>{data.categorie}<br/>
<FontAwesomeIcon icon={faFlag} style={style}/>Nationalité : <img src={"/flags/flags_" + data.country.toLowerCase() + ".png"} alt=""/><br/>
<FontAwesomeIcon icon={faInfoCircle} style={style}/>Rôle au sien du club : {data.role}<br/>
<FontAwesomeIcon icon={faInfoCircle} style={style}/>Formation d'arbitrage : {data.grade_arbitrage}
</p>
</div>
</div>
</div>;
}

View File

@@ -62,7 +62,7 @@ function InformationForm() {
<div className="card-body text-center">
<TextField name="name" text="Nom*"/>
<CountryList name="country" text="Pays*" value={"fr"}/>
<CountryList name="country" text="Pays*" value={"FR"}/>
<div className="mb-3">
<div className="input-group">

View File

@@ -2,7 +2,7 @@ import {useNavigate} from "react-router-dom";
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {apiAxios} from "../../../utils/Tools.js";
import {toast} from "react-toastify";
import {BirthDayField, OptionField, RoleList, TextField} from "../../../components/MemberCustomFiels.jsx";
import {BirthDayField, CountryList, OptionField, RoleList, TextField} from "../../../components/MemberCustomFiels.jsx";
import {ClubSelect} from "../../../components/ClubSelect.jsx";
import {addPhoto} from "./InformationForm.jsx";
@@ -73,8 +73,7 @@ function Form() {
<TextField name="email" text="Email" placeholder="name@example.com"
type="email" required={false}/>
<OptionField name="genre" text="Genre" values={{NA: 'N/A', H: 'H', F: 'F'}}/>
<OptionField name="country" text="Pays" value={'fr'}
values={{NA: 'Sélectionner...', fr: 'FR', es: 'ES', be: 'BE'}}/>
<CountryList name="country" text="Pays" value={"FR"}/>
<BirthDayField/>
<div className="row">
<ClubSelect name="club" na={true}/>

View File

@@ -2,7 +2,7 @@ import {useNavigate} from "react-router-dom";
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {apiAxios} from "../../../utils/Tools.js";
import {toast} from "react-toastify";
import {BirthDayField, OptionField, TextField} from "../../../components/MemberCustomFiels.jsx";
import {BirthDayField, CountryList, OptionField, TextField} from "../../../components/MemberCustomFiels.jsx";
import {addPhoto} from "../../admin/member/InformationForm.jsx";
export function NewMemberPage() {
@@ -69,8 +69,7 @@ function Form() {
<TextField name="email" text="Email" placeholder="name@example.com"
type="email"/>
<OptionField name="genre" text="Genre" values={{NA: 'N/A', H: 'H', F: 'F'}}/>
<OptionField name="country" text="Pays" value={'fr'}
values={{NA: 'Sélectionner...', fr: 'FR', es: 'ES', be: 'BE'}}/>
<CountryList name="country" text="Pays" value={"FR"}/>
<BirthDayField/>
<div className="row">
<div className="input-group mb-3">