feat: club set role
This commit is contained in:
parent
fe1af4d78f
commit
53d59a5b56
@ -10,6 +10,7 @@ import fr.titionfire.ffsaf.data.repository.CombRepository;
|
||||
import fr.titionfire.ffsaf.net2.ServerCustom;
|
||||
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
|
||||
import fr.titionfire.ffsaf.net2.request.SReqClub;
|
||||
import fr.titionfire.ffsaf.rest.data.DeskMember;
|
||||
import fr.titionfire.ffsaf.rest.data.RenewAffData;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleClubList;
|
||||
import fr.titionfire.ffsaf.rest.from.FullClubForm;
|
||||
@ -36,6 +37,7 @@ import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER;
|
||||
|
||||
@ -128,6 +130,16 @@ public class ClubService {
|
||||
.call(club -> Mutiny.fetch(club.getContact()));
|
||||
}
|
||||
|
||||
public Uni<List<DeskMember>> getClubDesk(Consumer<ClubModel> consumer, long id) {
|
||||
return repository.findById(id).invoke(consumer)
|
||||
.chain(club -> combRepository.list("club = ?1", club))
|
||||
.map(combs -> combs.stream()
|
||||
.filter(o -> o.getRole() != null && o.getRole().level >= RoleAsso.MEMBREBUREAU.level)
|
||||
.sorted((o1, o2) -> o2.getRole().level - o1.getRole().level)
|
||||
.map(DeskMember::fromModel)
|
||||
.toList());
|
||||
}
|
||||
|
||||
public Uni<String> updateOfUser(JsonWebToken idToken, PartClubForm form) {
|
||||
TypeReference<HashMap<Contact, String>> typeRef = new TypeReference<>() {
|
||||
};
|
||||
@ -245,7 +257,7 @@ public class ClubService {
|
||||
.call(__ -> Utils.deleteMedia(id, media, "clubStatus"));
|
||||
}
|
||||
|
||||
public Uni<RenewAffData> getRenewData(long id) {
|
||||
public Uni<RenewAffData> getRenewData(long id, List<Long> mIds) {
|
||||
RenewAffData data = new RenewAffData();
|
||||
|
||||
return repository.findById(id)
|
||||
@ -260,11 +272,10 @@ public class ClubService {
|
||||
.map(AffiliationModel::getSaison).map(i -> Math.min(i + 1, Utils.getSaison() + 1))
|
||||
.orElse(Utils.getSaison()));
|
||||
})
|
||||
.chain(club -> combRepository.list("club = ?1", club))
|
||||
.chain(club -> combRepository.list("id IN ?1", mIds))
|
||||
.invoke(combs -> data.setMembers(combs.stream()
|
||||
.filter(o -> o.getRole() != null && o.getRole().level >= RoleAsso.MEMBREBUREAU.level)
|
||||
.sorted((o1, o2) -> o2.getRole().level - o1.getRole().level)
|
||||
.limit(3)
|
||||
.map(RenewAffData.RenewMember::new)
|
||||
.toList()))
|
||||
.map(o -> data);
|
||||
|
||||
@ -3,6 +3,7 @@ package fr.titionfire.ffsaf.rest;
|
||||
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||
import fr.titionfire.ffsaf.domain.service.ClubService;
|
||||
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
|
||||
import fr.titionfire.ffsaf.rest.data.DeskMember;
|
||||
import fr.titionfire.ffsaf.rest.data.RenewAffData;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleClub;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleClubList;
|
||||
@ -179,8 +180,18 @@ public class ClubEndpoints {
|
||||
@Path("/renew/{id}")
|
||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<RenewAffData> getOfUser(@PathParam("id") long id) {
|
||||
return Uni.createFrom().item(id).invoke(checkPerm2).chain(__ -> clubService.getRenewData(id));
|
||||
public Uni<RenewAffData> getOfUser(@PathParam("id") long id, @QueryParam("m1") long m1_id,
|
||||
@QueryParam("m2") long m2_id, @QueryParam("m3") long m3_id) {
|
||||
return Uni.createFrom().item(id).invoke(checkPerm2)
|
||||
.chain(__ -> clubService.getRenewData(id, List.of(m1_id, m2_id, m3_id)));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/desk/{id}")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<List<DeskMember>> getClubDesk(@PathParam("id") long id) {
|
||||
return clubService.getClubDesk(checkPerm, id);
|
||||
}
|
||||
|
||||
@GET
|
||||
|
||||
29
src/main/java/fr/titionfire/ffsaf/rest/data/DeskMember.java
Normal file
29
src/main/java/fr/titionfire/ffsaf/rest/data/DeskMember.java
Normal file
@ -0,0 +1,29 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class DeskMember {
|
||||
private Long id;
|
||||
private String lname;
|
||||
private String fname;
|
||||
private String role;
|
||||
|
||||
public static DeskMember fromModel(MembreModel membreModel) {
|
||||
if (membreModel == null)
|
||||
return null;
|
||||
|
||||
DeskMember deskMember = new DeskMember();
|
||||
deskMember.setId(membreModel.getId());
|
||||
deskMember.setLname(membreModel.getLname());
|
||||
deskMember.setFname(membreModel.getFname());
|
||||
deskMember.setRole(membreModel.getRole().toString());
|
||||
|
||||
return deskMember;
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,6 @@ import {useEffect, useState} from "react";
|
||||
import {apiAxios, getSaison} from "../utils/Tools.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import {RoleList} from "../components/MemberCustomFiels.jsx";
|
||||
import {useAuth} from "../hooks/useAuth.jsx";
|
||||
|
||||
const notUpperCase = ["de", "la", "le", "les", "des", "du", "d'", "l'", "sur"];
|
||||
|
||||
@ -96,7 +94,7 @@ export function DemandeAff() {
|
||||
).then(_ => {
|
||||
navigate("/club/me")
|
||||
})
|
||||
}else if (event.nativeEvent.submitter.value === "edit") {
|
||||
} else if (event.nativeEvent.submitter.value === "edit") {
|
||||
formData.append("id", initData.id)
|
||||
|
||||
toast.promise(
|
||||
@ -109,7 +107,7 @@ export function DemandeAff() {
|
||||
).then(_ => {
|
||||
navigate("/club/me")
|
||||
})
|
||||
}else {
|
||||
} else {
|
||||
formData.append("id", -1)
|
||||
|
||||
toast.promise(
|
||||
@ -284,15 +282,23 @@ function AssoInfo({initData, needFile}) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<label className="input-group-text" htmlFor="status">Status*</label>
|
||||
<input type="file" className="form-control" id="status" name="status" accept=".pdf,.txt" required={needFile}/>
|
||||
<div className="mb-3">
|
||||
<div className="input-group">
|
||||
<label className="input-group-text" htmlFor="logo">Blason{needFile && "*"}</label>
|
||||
<input type="file" className="form-control" id="logo" name="logo" accept=".jpg,.jpeg,.gif,.png,.svg"
|
||||
required={needFile}/>
|
||||
</div>
|
||||
{!needFile && <div className="form-text" id="status">Laissez vide pour ne rien changer. (Si un blason a déjà été envoyé lors de cette
|
||||
demande, il sera utilisé, sinon nous utiliserons celui de la précédant affiliation)</div>}
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<label className="input-group-text" htmlFor="logo">Logo*</label>
|
||||
<input type="file" className="form-control" id="logo" name="logo" accept=".jpg,.jpeg,.gif,.png,.svg"
|
||||
required={needFile}/>
|
||||
<div className="mb-3">
|
||||
<div className="input-group">
|
||||
<label className="input-group-text" htmlFor="status">Statue{needFile && "*"}</label>
|
||||
<input type="file" className="form-control" id="status" name="status" accept=".pdf,.txt" required={needFile}/>
|
||||
</div>
|
||||
{!needFile && <div className="form-text" id="status">Laissez vide pour ne rien changer. (Si un statu a déjà été envoyé lors de cette
|
||||
demande, il sera utilisé, sinon nous utiliserons celui de la précédant affiliation)</div>}
|
||||
</div>
|
||||
</>;
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ export function AffiliationCard({clubData}) {
|
||||
dispatch({type: 'SORT', payload: (a, b) => b.saison - a.saison})
|
||||
}, [data]);
|
||||
|
||||
return <div className="card mb-4 mb-md-0">
|
||||
return <div className="card mb-4">
|
||||
<div className="card-header container-fluid">
|
||||
<div className="row">
|
||||
<div className="col">Affiliation</div>
|
||||
|
||||
@ -52,6 +52,7 @@ export function ClubPage() {
|
||||
</div>
|
||||
<div className="col-lg-3">
|
||||
<LoadingProvider><AffiliationCard clubData={data}/></LoadingProvider>
|
||||
<LoadingProvider><BureauCard 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
|
||||
@ -60,6 +61,7 @@ export function ClubPage() {
|
||||
<ConfirmDialog title="Supprimer le club"
|
||||
message="Êtes-vous sûr de vouloir supprimer ce club ?"
|
||||
onConfirm={handleRm}/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -157,3 +159,25 @@ function InformationForm({data}) {
|
||||
<LocationEditorModal modal={modal} sendData={locationModalCallback}/>
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
export function BureauCard({clubData}) {
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error} = useFetch(`/club/desk/${clubData.id}`, setLoading, 1)
|
||||
|
||||
return <>
|
||||
<div className="card mb-4">
|
||||
<div className="card-header">Bureau</div>
|
||||
<div className="card-body">
|
||||
<ul className="list-group">
|
||||
{data && data.map((d, index) => {
|
||||
return <div key={index} className="list-group-item d-flex justify-content-between align-items-start">
|
||||
<div className="me-auto"><small>{d.role}</small><br/>{d.lname} {d.fname}</div>
|
||||
</div>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{error && <AxiosError error={error}/>}
|
||||
</>
|
||||
}
|
||||
@ -10,25 +10,16 @@ import {SimpleReducer} from "../../../utils/SimpleReducer.jsx";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
export function AffiliationCard({clubData}) {
|
||||
const navigate = useNavigate();
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error} = useFetch(`/affiliation/${clubData.id}`, setLoading, 1)
|
||||
const [modalAffiliation, setModal] = useState({id: 0, club: clubData.id})
|
||||
|
||||
const sendAffiliationRequest = () => {
|
||||
let createData = {}
|
||||
apiAxios.get(`/club/renew/${clubData.id}`).then(data => {
|
||||
navigate('/affiliation#d' + encodeURI(JSON.stringify(data.data)))
|
||||
})
|
||||
}
|
||||
|
||||
return <div className="card mb-4 mb-md-0">
|
||||
return <div className="card mb-4">
|
||||
<div className="card-header container-fluid">
|
||||
<div className="row">
|
||||
<div className="col">Affiliation</div>
|
||||
<div className="col" style={{textAlign: 'right'}}>
|
||||
<button className="btn btn-primary btn-sm" onClick={e => sendAffiliationRequest()}>Renouveler
|
||||
</button>
|
||||
<button className="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#RenewModal">Renouveler</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -92,4 +83,87 @@ function ModalContent({affiliation}) {
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
}
|
||||
|
||||
export function BureauCard({clubData}) {
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error} = useFetch(`/club/desk/${clubData.id}`, setLoading, 1)
|
||||
|
||||
return <>
|
||||
<div className="card mb-4">
|
||||
<div className="card-header">Bureau</div>
|
||||
<div className="card-body">
|
||||
<ul className="list-group">
|
||||
{data && data.map((d, index) => {
|
||||
return <div key={index} className="list-group-item d-flex justify-content-between align-items-start">
|
||||
<div className="me-auto"><small>{d.role}</small><br/>{d.lname} {d.fname}</div>
|
||||
</div>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="modal fade" id="RenewModal" tabIndex="-1" aria-labelledby="RenewModalLabel"
|
||||
aria-hidden="true">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<ModalContent2 clubData={clubData} data={data}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <AxiosError error={error}/>}
|
||||
</>
|
||||
}
|
||||
|
||||
function ModalContent2({clubData, data}) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const sendAffiliationRequest = (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
let list = []
|
||||
for (let i = 0; i < event.target.length; i++) {
|
||||
if (event.target[i].type === "checkbox") {
|
||||
if (event.target[i].checked) {
|
||||
list.push(event.target[i].name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (list.length !== 3) {
|
||||
toast.error("Il faut sélectionner 3 membres pour renouveler l'affiliation")
|
||||
return
|
||||
}
|
||||
apiAxios.get(`/club/renew/${clubData.id}?m1=${list[0]}&m2=${list[1]}&m3=${list[2]}`).then(data => {
|
||||
navigate('/affiliation#d' + encodeURI(JSON.stringify(data.data)))
|
||||
})
|
||||
}
|
||||
|
||||
return <form onSubmit={sendAffiliationRequest}>
|
||||
<div className="modal-header">
|
||||
<h1 className="modal-title fs-5" id="AffiliationModalLabel">Renouvellement de l'affiliation</h1>
|
||||
<button type="button" className="btn-close" data-bs-dismiss="modal"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p>Veuillez sélectionner 3 membres du bureau pour remplir la pré-demande. (Si un membre non-bureau va le devenir l'an prochain,
|
||||
vous pourrez toujours remplacer un des membres sélectionné à la prochaine étape)</p>
|
||||
{data && data.map((d, index) => {
|
||||
return <div key={index} className="input-group mb-1">
|
||||
<div className="input-group-text">
|
||||
<input className="form-check-input mt-0" type="checkbox" value="" aria-label={d.lname + " " + d.fname}
|
||||
name={d.id}/>
|
||||
</div>
|
||||
<span className="input-group-text">{d.role}</span>
|
||||
<span className="input-group-text">{d.lname} {d.fname}</span>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="reset" className="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
|
||||
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal">Suivant</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
@ -5,7 +5,7 @@ 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 {AffiliationCard, BureauCard} from "./AffiliationCard.jsx";
|
||||
import {CountryList, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
|
||||
import {useRef, useState} from "react";
|
||||
@ -32,7 +32,10 @@ export function MyClubPage() {
|
||||
</LoadingProvider>
|
||||
</div>
|
||||
<div className="col-lg-3">
|
||||
<LoadingProvider><AffiliationCard clubData={data}/></LoadingProvider>
|
||||
<LoadingProvider>
|
||||
<AffiliationCard clubData={data}/>
|
||||
<BureauCard clubData={data}/>
|
||||
</LoadingProvider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user