feat: add pay information to licence

This commit is contained in:
2025-08-13 22:05:19 +02:00
parent 09f6cd7463
commit 0563c7c8de
12 changed files with 114 additions and 71 deletions

View File

@@ -10,6 +10,8 @@ import {apiAxios, errFormater} from "../utils/Tools.js";
import {toast} from "react-toastify";
import {SearchBar} from "../components/SearchBar.jsx";
import * as XLSX from "xlsx-js-style";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faEuroSign} from "@fortawesome/free-solid-svg-icons";
export function MemberList({source}) {
const {hash} = useLocation();
@@ -23,14 +25,15 @@ export function MemberList({source}) {
const [clubFilter, setClubFilter] = useState("");
const [stateFilter, setStateFilter] = useState(4)
const [lastSearch, setLastSearch] = useState("");
const [paymentFilter, setPaymentFilter] = useState(2);
const setLoading = useLoadingSwitcher()
const {data, error, refresh} = useFetch(`/member/find/${source}?page=${page}&licenceRequest=${stateFilter}`, setLoading, 1)
const {data, error, refresh} = useFetch(`/member/find/${source}?page=${page}&licenceRequest=${stateFilter}&payment=${paymentFilter}`, setLoading, 1)
useEffect(() => {
refresh(`/member/find/${source}?page=${page}&search=${lastSearch}&club=${clubFilter}&licenceRequest=${stateFilter}`);
}, [hash, clubFilter, stateFilter]);
refresh(`/member/find/${source}?page=${page}&search=${lastSearch}&club=${clubFilter}&licenceRequest=${stateFilter}&payment=${paymentFilter}`);
}, [hash, clubFilter, stateFilter, lastSearch, paymentFilter]);
useEffect(() => {
if (!data)
@@ -74,7 +77,6 @@ export function MemberList({source}) {
if (search === lastSearch)
return;
setLastSearch(search);
refresh(`/member/find/${source}?page=${page}&search=${search}&club=${clubFilter}&licenceRequest=${stateFilter}`);
}
return <>
@@ -102,7 +104,8 @@ export function MemberList({source}) {
<div className="card-body">
<FiltreBar showLicenceState={showLicenceState} setShowLicenceState={setShowLicenceState} data={data}
clubFilter={clubFilter} setClubFilter={setClubFilter} source={source}
stateFilter={stateFilter} setStateFilter={setStateFilter}/>
stateFilter={stateFilter} setStateFilter={setStateFilter} paymentFilter={paymentFilter}
setPaymentFilter={setPaymentFilter}/>
</div>
</div>
@@ -361,7 +364,8 @@ function MakeCentralPanel({data, visibleMember, navigate, showLicenceState, page
function MakeRow({member, showLicenceState, navigate, source}) {
const rowContent = <>
<div className="row">
<span className="col-auto">{member.licence_number ? String(member.licence_number).padStart(5, '0') : "-------"}</span>
<span className="col-auto">{(member.licence_number ? String(member.licence_number).padStart(5, '0') : "-------") + " "}
{(showLicenceState && member.licence != null && member.licence.pay)? <FontAwesomeIcon icon={faEuroSign}/> : <>&nbsp;&nbsp;</>}</span>
<div className="ms-2 col-auto">
<div className="fw-bold">{member.fname} {member.lname}</div>
</div>
@@ -386,7 +390,7 @@ function MakeRow({member, showLicenceState, navigate, source}) {
let allClub = []
function FiltreBar({showLicenceState, setShowLicenceState, data, clubFilter, setClubFilter, source, stateFilter, setStateFilter}) {
function FiltreBar({showLicenceState, setShowLicenceState, data, clubFilter, setClubFilter, source, stateFilter, setStateFilter, paymentFilter, setPaymentFilter}) {
useEffect(() => {
if (!data)
return;
@@ -410,6 +414,14 @@ function FiltreBar({showLicenceState, setShowLicenceState, data, clubFilter, set
<option value={3}>Licence validée</option>
</select>
</div>
<div className="mb-3">
<select className="form-select" value={paymentFilter} onChange={event => setPaymentFilter(Number(event.target.value))}
hidden={stateFilter === 0 || stateFilter === 4}>
<option value={2}>Tout les états de paiement</option>
<option value={0}>Sans paiement</option>
<option value={1}>Avec paiement</option>
</select>
</div>
</div>
}

View File

@@ -8,6 +8,8 @@ import {apiAxios, errFormater} from "../utils/Tools.js";
import {toast} from "react-toastify";
import {SearchBar} from "../components/SearchBar.jsx";
import {ConfirmDialog} from "../components/ConfirmDialog.jsx";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faEuroSign} from "@fortawesome/free-solid-svg-icons";
export function ValidateList({source}) {
const {hash} = useLocation();
@@ -20,16 +22,21 @@ export function ValidateList({source}) {
const [clubFilter, setClubFilter] = useState("");
const [stateFilter, setStateFilter] = useState(2)
const [lastSearch, setLastSearch] = useState("");
const [paymentFilter, setPaymentFilter] = useState(2);
const [selectedMembers, setSelectedMembers] = useState([]);
const setLoading = useLoadingSwitcher()
const {data, error, refresh} = useFetch(`/member/find/${source}?page=${page}&licenceRequest=${stateFilter}`, setLoading, 1)
const {
data,
error,
refresh
} = useFetch(`/member/find/${source}?page=${page}&licenceRequest=${stateFilter}&payment=${paymentFilter}`, setLoading, 1)
useEffect(() => {
refresh(`/member/find/${source}?page=${page}&search=${lastSearch}&club=${clubFilter}&licenceRequest=${stateFilter}`);
}, [hash, clubFilter, stateFilter]);
refresh(`/member/find/${source}?page=${page}&search=${lastSearch}&club=${clubFilter}&licenceRequest=${stateFilter}&payment=${paymentFilter}`);
}, [hash, clubFilter, stateFilter, lastSearch, paymentFilter]);
useEffect(() => {
if (!data)
@@ -70,7 +77,6 @@ export function ValidateList({source}) {
if (search === lastSearch)
return;
setLastSearch(search);
refresh(`/member/find/${source}?page=${page}&search=${search}&club=${clubFilter}&licenceRequest=${stateFilter}`);
}
const handleValidation = () => {
@@ -92,7 +98,7 @@ export function ValidateList({source}) {
}
).then(() => {
setSelectedMembers([]);
refresh(`/member/find/${source}?page=${page}&search=${lastSearch}&club=${clubFilter}&licenceRequest=${stateFilter}`);
refresh(`/member/find/${source}?page=${page}&search=${lastSearch}&club=${clubFilter}&licenceRequest=${stateFilter}&payment=${paymentFilter}`);
});
}
@@ -119,7 +125,8 @@ export function ValidateList({source}) {
<div className="card-header">Filtre</div>
<div className="card-body">
<FiltreBar data={data} clubFilter={clubFilter} setClubFilter={setClubFilter} source={source}
stateFilter={stateFilter} setStateFilter={setStateFilter}/>
stateFilter={stateFilter} setStateFilter={setStateFilter} paymentFilter={paymentFilter}
setPaymentFilter={setPaymentFilter}/>
</div>
</div>}
@@ -128,7 +135,8 @@ export function ValidateList({source}) {
<button className="btn btn-primary" data-bs-toggle="modal" data-bs-target="#confirm-validation">Valider
les {selectedMembers.length} licences sélectionnée
</button>
<ConfirmDialog title="Validation des licences" message={"Êtes-vous sûr de vouloir valider les "+selectedMembers.length+" licences ?"}
<ConfirmDialog title="Validation des licences"
message={"Êtes-vous sûr de vouloir valider les " + selectedMembers.length + " licences ?"}
onConfirm={handleValidation} id="confirm-validation"/>
</>}
</div>
@@ -223,7 +231,8 @@ function MakeRow({member, source, isChecked, onCheckboxClick, onRowClick}) {
<input className="form-check-input me-1" type="checkbox" checked={isChecked || false} onChange={() => {
}}
onClick={(e) => onCheckboxClick(e, member.id)}/>
<span>{member.licence_number ? String(member.licence_number).padStart(5, '0') : "-------"}</span>
<span className="col-auto">{(member.licence_number ? String(member.licence_number).padStart(5, '0') : "-------") + " "}
{(member.licence != null && member.licence.pay)? <FontAwesomeIcon icon={faEuroSign}/> : <>&nbsp;&nbsp;</>}</span>
</div>
<div className="ms-2 col-auto">
<div className="fw-bold">{member.fname} {member.lname}</div>
@@ -251,7 +260,7 @@ function MakeRow({member, source, isChecked, onCheckboxClick, onRowClick}) {
let allClub = []
function FiltreBar({data, clubFilter, setClubFilter, source, stateFilter, setStateFilter}) {
function FiltreBar({data, clubFilter, setClubFilter, source, stateFilter, setStateFilter, paymentFilter, setPaymentFilter}) {
useEffect(() => {
if (!data)
return;
@@ -268,6 +277,13 @@ function FiltreBar({data, clubFilter, setClubFilter, source, stateFilter, setSta
<option value={6}>Demande incomplet</option>
</select>
</div>
<div className="mb-3">
<select className="form-select" value={paymentFilter} onChange={event => setPaymentFilter(Number(event.target.value))}>
<option value={2}>Tout les états de paiement</option>
<option value={0}>Sans paiement</option>
<option value={1}>Avec paiement</option>
</select>
</div>
</div>
}

View File

@@ -2,7 +2,7 @@ import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {useFetch} from "../../../hooks/useFetch.js";
import {useEffect, useReducer, useState} from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faPen} from "@fortawesome/free-solid-svg-icons";
import {faEuroSign, faPen} from "@fortawesome/free-solid-svg-icons";
import {AxiosError} from "../../../components/AxiosError.jsx";
import {apiAxios, errFormater, getSaison} from "../../../utils/Tools.js";
import {toast} from "react-toastify";
@@ -66,7 +66,7 @@ export function LicenceCard({userData}) {
return <div key={index}
className={"list-group-item d-flex justify-content-between align-items-start list-group-item-" +
(licence.validate ? "success" : (licence.certificate?.length > 1 ? "warning" : "danger"))}>
<div className="me-auto">{licence?.saison}-{licence?.saison + 1}</div>
<div className="me-auto">{licence?.saison}-{licence?.saison + 1} {(licence.pay) && <FontAwesomeIcon icon={faEuroSign}/>}</div>
<button className="badge btn btn-primary rounded-pill" data-bs-toggle="modal"
data-bs-target="#LicenceModal" onClick={_ => setModal(licence)}>
<FontAwesomeIcon icon={faPen}/></button>
@@ -134,6 +134,7 @@ function ModalContent({licence, dispatch}) {
const [certificateBy, setCertificateBy] = useState("")
const [certificateDate, setCertificateDate] = useState("")
const [validate, setValidate] = useState(false)
const [pay, setPay] = useState(false)
const [isNew, setNew] = useState(true)
const setSeason = (event) => {
setSaison(Number(event.target.value))
@@ -147,6 +148,9 @@ function ModalContent({licence, dispatch}) {
const handleValidateChange = (event) => {
setValidate(event.target.value === 'true');
}
const handlePayChange = (event) => {
setPay(event.target.value === 'true');
}
useEffect(() => {
if (licence.id !== -1) {
@@ -160,12 +164,14 @@ function ModalContent({licence, dispatch}) {
setCertificateDate(licence.certificate.split('¤')[1])
}
setValidate(licence.validate)
setPay(licence.pay);
} else {
setNew(true)
setSaison(getSaison())
setCertificateBy("")
setCertificateDate("")
setValidate(false)
setPay(false);
}
}, [licence]);
@@ -197,6 +203,8 @@ function ModalContent({licence, dispatch}) {
<input type="date" className="form-control" placeholder="jj/mm/aaaa" name="certificateDate"
aria-describedby="basic-addon2" value={certificateDate} onChange={handleCertificateDateChange}/>
</div>
<RadioGroupeOnOff name="pay" text="Paiement de la licence" value={pay}
onChange={handlePayChange}/>
<RadioGroupeOnOff name="validate" text="Validation de la licence" value={validate}
onChange={handleValidateChange}/>
</div>

View File

@@ -2,7 +2,7 @@ import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {useFetch} from "../../../hooks/useFetch.js";
import {useEffect, useReducer, useState} from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faInfo, faPen} from "@fortawesome/free-solid-svg-icons";
import {faEuroSign, faInfo, faPen} from "@fortawesome/free-solid-svg-icons";
import {AxiosError} from "../../../components/AxiosError.jsx";
import {apiAxios, errFormater, getSaison} from "../../../utils/Tools.js";
import {toast} from "react-toastify";
@@ -172,13 +172,16 @@ function ModalContent({licence, dispatch}) {
<span>Certificat médical</span>
<div className="input-group mb-3 ">
<span className="input-group-text" id="basic-addon2">Fait par</span>
<input type="text" className="form-control" placeholder="Fait par" name="certificateBy"
<input type="text" className="form-control" placeholder="Fait par" name="certificateBy" disabled={licence.validate}
aria-label="Fait par" aria-describedby="basic-addon2" value={certificateBy} onChange={handleCertificateByChange}/>
<span className="input-group-text" id="basic-addon2">, le</span>
<input type="date" className="form-control" placeholder="jj/mm/aaaa" name="certificateDate"
<input type="date" className="form-control" placeholder="jj/mm/aaaa" name="certificateDate" disabled={licence.validate}
aria-describedby="basic-addon2" value={certificateDate} onChange={handleCertificateDateChange}/>
</div>
<div className="input-group mb-3 justify-content-md-center">
<div>Paiement de la licence: <ColoredText boolean={licence.pay}/></div>
</div>
<div className="input-group mb-3 justify-content-md-center">
<div>Validation de la licence: <ColoredText boolean={licence.validate}/></div>
</div>
@@ -187,7 +190,7 @@ function ModalContent({licence, dispatch}) {
{currentSaison && !licence.validate &&
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal">Enregistrer</button>}
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
{currentSaison && !licence.validate && licence.id !== -1 &&
{currentSaison && !licence.validate && licence.id !== -1 && !licence.pay &&
<button type="button" className="btn btn-danger" data-bs-dismiss="modal"
onClick={() => removeLicence(licence.id, dispatch)}>Annuler</button>}
</div>