19 Commits

Author SHA1 Message Date
5afef2bcab Merge pull request 'fix: change obs lice name location' (#118) from dev into master
Reviewed-on: #118
2026-03-20 15:40:37 +00:00
55d39e02d2 Merge pull request 'fix: clear pub aff when no cat select' (#117) from dev into master
Reviewed-on: #117
2026-03-20 15:11:17 +00:00
d5c1273e18 Merge pull request 'fix: change methode for move upload file' (#116) from dev into master
Reviewed-on: #116
2026-03-18 12:07:58 +00:00
eda96d5ed2 Merge pull request 'fix: add more log for file copy' (#115) from dev into master
Reviewed-on: #115
2026-03-18 10:07:00 +00:00
3a4e2f160f Merge pull request 'fix: add log for file copy' (#114) from dev into master
Reviewed-on: #114
2026-03-18 09:48:37 +00:00
2b84854ef2 Merge pull request 'fix: obs support wss' (#113) from dev into master
Reviewed-on: #113
2026-03-17 22:16:29 +00:00
fc3bea7f76 Merge pull request 'fix: language detection' (#112) from dev into master
Reviewed-on: #112
2026-03-16 11:19:50 +00:00
e67e01640a Merge pull request 'fix: club guest mixing in result' (#111) from dev into master
Reviewed-on: #111
2026-03-10 17:47:33 +00:00
e870ffdda0 Merge pull request 'fix: eslint version' (#110) from dev into master
Reviewed-on: #110
2026-03-09 12:19:27 +00:00
378a17ba75 Merge pull request 'dev' (#109) from dev into master
Reviewed-on: #109
2026-03-09 12:10:46 +00:00
3cb12826d0 Merge pull request 'dev' (#108) from dev into master
Reviewed-on: #108
2026-02-20 16:59:40 +00:00
72b248d09d Merge pull request 'dev' (#107) from dev into master
Reviewed-on: #107
2026-02-06 16:18:39 +00:00
34a6911fa0 Merge pull request 'dev' (#106) from dev into master
Reviewed-on: #106
2026-01-30 14:07:08 +00:00
59ba31ae2d Merge pull request 'dev' (#105) from dev into master
Reviewed-on: #105
2026-01-23 15:49:24 +00:00
4b56aa0209 Merge pull request 'dev' (#104) from dev into master
Reviewed-on: #104
2026-01-18 15:19:12 +00:00
eefa77369a Merge pull request 'feat: update js npm lib' (#103) from dev into master
Reviewed-on: #103
2026-01-16 15:21:11 +00:00
a942798a6c Merge pull request 'feat: add match eq support' (#102) from dev into master
Reviewed-on: #102
2026-01-16 14:37:55 +00:00
876545630a Merge pull request 'fix: translate backend asset includes in native' (#101) from dev into master
Reviewed-on: #101
2026-01-16 13:22:09 +00:00
cad6d14ba8 Merge pull request 'fix: translate backend add log' (#100) from dev into master
Reviewed-on: #100
2026-01-16 12:59:33 +00:00
13 changed files with 1219 additions and 1039 deletions

View File

@@ -122,9 +122,7 @@ public class LicenceService {
.call(genLicenceNumberAndAccountIfNeed()) .call(genLicenceNumberAndAccountIfNeed())
: Uni.createFrom().nullItem() : Uni.createFrom().nullItem()
)) ))
.call(licenceModel -> ls.logA(LogModel.ActionType.ADD, .call(licenceModel -> ls.logA(LogModel.ActionType.ADD, membreModel.getObjectName(),
"%s (valid=%b, pay=%b, %s)" .formatted(membreModel.getObjectName(), model.isValidate(),
model.isPay(), model.getCertificate()),
licenceModel)); licenceModel));
}); });
} else { } else {

View File

@@ -184,17 +184,11 @@ public class MembreService {
String finalSearch = search; String finalSearch = search;
return getLicenceListe(licenceRequest, payState) return getLicenceListe(licenceRequest, payState)
.chain(l -> { .map(l -> l.stream().map(l2 -> l2.getMembre().getId()).toList())
.chain(ids -> {
PanacheQuery<MembreModel> query; PanacheQuery<MembreModel> query;
query = repository.find(queryStr, sort, finalSearch, query = repository.find(queryStr, sort, finalSearch, ids, club).page(Page.ofSize(limit));
l.stream().map(l2 -> l2.getMembre().getId()).toList(), club).page(Page.ofSize(limit)); return getPageResult(query, limit, page);
return getPageResult(query, limit, page)
.call(r -> (r.getResult().isEmpty() || !l.isEmpty() ?
Uni.createFrom().item(l) :
licenceRepository.list("saison = ?1 AND membre.id IN ?2", Utils.getSaison(),
r.getResult().stream().map(SimpleMembre::getId).toList()))
.invoke(l2 -> r.setAdditionalData(l2.stream().map(SimpleLicence::fromModel)))
);
}); });
} }
@@ -216,7 +210,7 @@ public class MembreService {
public Uni<List<SimpleMembreInOutData>> getAllExport(String subject) { public Uni<List<SimpleMembreInOutData>> getAllExport(String subject) {
return repository.find("userId = ?1", subject).firstResult() return repository.find("userId = ?1", subject).firstResult()
.chain(membreModel -> repository.list("club = ?1", membreModel.getClub())) .chain(membreModel -> repository.list("club = ?1", membreModel.getClub()))
.chain(membres -> licenceRepository.list("membre IN ?1", membres) .chain(membres -> licenceRepository.list("saison = ?1 AND membre IN ?2", Utils.getSaison(), membres)
.map(l -> membres.stream().map(m -> SimpleMembreInOutData.fromModel(m, l)).toList())); .map(l -> membres.stream().map(m -> SimpleMembreInOutData.fromModel(m, l)).toList()));
} }

View File

@@ -58,7 +58,6 @@ public class ResultService {
@Inject @Inject
@CacheName("club-classement") @CacheName("club-classement")
Cache cacheClubClassement; Cache cacheClubClassement;
private static final HashMap<String, Long> cacheClubClassementExp = new HashMap<>();
private static final HashMap<Long, String> combTempIds = new HashMap<>(); private static final HashMap<Long, String> combTempIds = new HashMap<>();
@@ -755,10 +754,14 @@ public class ResultService {
public Uni<List<ClubClassement>> getAllClubArray(String uuid, boolean cache) { public Uni<List<ClubClassement>> getAllClubArray(String uuid, boolean cache) {
List<CardModel> cards = new java.util.ArrayList<>(); List<CardModel> cards = new java.util.ArrayList<>();
return (!cache || cacheClubClassementExp.getOrDefault(uuid, 0L) < System.currentTimeMillis() ? //noinspection unchecked
cacheClubClassement.invalidate(uuid).invoke(() -> cacheClubClassementExp.remove(uuid)) : cacheClubClassement.invalidateIf(
Uni.createFrom().voidItem() (p) -> ((Pair<Long, List<ClubClassement>>) p).getKey() > System.currentTimeMillis());
).chain(o -> cacheClubClassement.getAsync(uuid, k -> cardRepository.list("competition.uuid = ?1", uuid)
if (!cache)
cacheClubClassement.invalidate(uuid);
return cacheClubClassement.getAsync(uuid, k -> cardRepository.list("competition.uuid = ?1", uuid)
.invoke(__ -> System.out.println("Cache miss for club classement with uuid " + uuid)) .invoke(__ -> System.out.println("Cache miss for club classement with uuid " + uuid))
.invoke(cards::addAll) .invoke(cards::addAll)
.chain(__ -> matchRepository.list("category.compet.uuid = ?1", uuid)) .chain(__ -> matchRepository.list("category.compet.uuid = ?1", uuid))
@@ -775,8 +778,7 @@ public class ResultService {
.map(entry -> { .map(entry -> {
ResultCategoryData tmp = new ResultCategoryData(); ResultCategoryData tmp = new ResultCategoryData();
getArray2(entry.getValue().stream().map(m -> new MatchModelExtend(m, cards)) getArray2(entry.getValue().stream().map(m -> new MatchModelExtend(m, cards)).toList(),
.toList(),
null, tmp); null, tmp);
getClassementArray(entry.getKey(), null, cards, tmp); getClassementArray(entry.getKey(), null, cards, tmp);
@@ -787,23 +789,20 @@ public class ResultService {
.map(categoryData -> { .map(categoryData -> {
HashMap<String, ClubClassement> clubMap = new HashMap<>(); HashMap<String, ClubClassement> clubMap = new HashMap<>();
categoryData.forEach( categoryData.forEach(c -> c.getClassement().stream().map(ResultCategoryData.ClassementData::comb)
c -> c.getClassement().stream().map(ResultCategoryData.ClassementData::comb) .filter(Objects::nonNull)
.filter(Objects::nonNull) .distinct()
.distinct() .map(comb -> {
.map(comb -> { if (comb instanceof MembreModel membreModel2) {
if (comb instanceof MembreModel membreModel2) { return (membreModel2.getClub() != null) ? membreModel2.getClub().getName() : "";
return (membreModel2.getClub() != null) ? membreModel2.getClub() } else if (comb instanceof CompetitionGuestModel guestModel) {
.getName() : ""; return guestModel.getClub();
} else if (comb instanceof CompetitionGuestModel guestModel) { }
return guestModel.getClub(); return "";
} })
return ""; .filter(s -> s != null && !s.isBlank() && !s.equals("Team"))
}) .distinct()
.filter(s -> s != null && !s.isBlank() && !s.equals("Team")) .forEach(clubName -> clubMap.putIfAbsent(clubName, new ClubClassement(clubName))));
.distinct()
.forEach(clubName -> clubMap.putIfAbsent(clubName,
new ClubClassement(clubName))));
categoryData.forEach(c -> c.getClassement().forEach(classementData -> { categoryData.forEach(c -> c.getClassement().forEach(classementData -> {
if (classementData.rank() > 3) if (classementData.rank() > 3)
@@ -812,8 +811,7 @@ public class ResultService {
if (classementData.comb() != null) { if (classementData.comb() != null) {
String clubName = ""; String clubName = "";
if (classementData.comb() instanceof MembreModel membreModel2) { if (classementData.comb() instanceof MembreModel membreModel2) {
clubName = (membreModel2.getClub() != null) ? membreModel2.getClub() clubName = (membreModel2.getClub() != null) ? membreModel2.getClub().getName() : "";
.getName() : "";
} else if (classementData.comb() instanceof CompetitionGuestModel guestModel) { } else if (classementData.comb() instanceof CompetitionGuestModel guestModel) {
clubName = guestModel.getClub(); clubName = guestModel.getClub();
} }
@@ -834,8 +832,8 @@ public class ResultService {
.thenComparingInt(c -> c.score[2]).reversed()) .thenComparingInt(c -> c.score[2]).reversed())
.toList(); .toList();
}) })
.invoke(__ -> cacheClubClassementExp.put(uuid, System.currentTimeMillis() + 60 * 1000L)) .map(l -> new Pair<>(System.currentTimeMillis() + 60 * 1000L, l))
)); ).map(Pair::getValue);
} }
@Data @Data

View File

@@ -2,14 +2,10 @@ package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.LicenceModel; import fr.titionfire.ffsaf.data.model.LicenceModel;
import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@@ -28,27 +24,7 @@ public class SimpleMembreInOutData {
public static SimpleMembreInOutData fromModel(MembreModel membreModel, List<LicenceModel> lc) { public static SimpleMembreInOutData fromModel(MembreModel membreModel, List<LicenceModel> lc) {
LicenceModel currentLicence = lc.stream().filter(l -> l.getMembre().getId().equals(membreModel.getId())) LicenceModel currentLicence = lc.stream().filter(l -> l.getMembre().getId().equals(membreModel.getId()))
.max(Comparator.comparingInt(LicenceModel::getSaison)).orElse(null); .findFirst().orElse(null);
String certif = null;
if (currentLicence != null && currentLicence.getCertificate() != null) {
String[] strings = currentLicence.getCertificate().split("¤");
if (currentLicence.getSaison() == Utils.getSaison()) {
certif = currentLicence.getCertificate();
} else if (strings.length > 1) {
try {
Date date = new SimpleDateFormat("yyyy-MM-dd").parse(strings[1]);
Calendar max = Utils.getFirstDateOfSaison();
max.add(Calendar.YEAR, -2);
if (max.getTime().compareTo(date) < 0) {
certif = currentLicence.getCertificate();
}
} catch (Exception ignored) {
}
}
}
return new SimpleMembreInOutData( return new SimpleMembreInOutData(
membreModel.getLicence(), membreModel.getLicence(),
@@ -57,8 +33,8 @@ public class SimpleMembreInOutData {
membreModel.getEmail(), membreModel.getEmail(),
membreModel.getGenre().str, membreModel.getGenre().str,
membreModel.getBirth_date(), membreModel.getBirth_date(),
currentLicence != null && currentLicence.getSaison() == Utils.getSaison(), currentLicence != null,
certif currentLicence == null ? null : currentLicence.getCertificate()
); );
} }
} }

View File

@@ -10,14 +10,13 @@ import java.util.List;
@Data @Data
@RegisterForReflection @RegisterForReflection
public class PageResult<T> { public class PageResult<T> {
@Schema(description = "Le numéro de la page courante.", examples = "1") @Schema(description = "Le numéro de la page courante.", example = "1")
private int page; private int page;
@Schema(description = "Le nombre d'éléments par page.", examples = "10") @Schema(description = "Le nombre d'éléments par page.", example = "10")
private int page_size; private int page_size;
@Schema(description = "Le nombre total de pages.", examples = "5") @Schema(description = "Le nombre total de pages.", example = "5")
private int page_count; private int page_count;
@Schema(description = "Le nombre total d'éléments.", examples = "47") @Schema(description = "Le nombre total d'éléments.", example = "47")
private long result_count; private long result_count;
private List<T> result = new ArrayList<>(); private List<T> result = new ArrayList<>();
private Object additionalData;
} }

View File

@@ -119,15 +119,6 @@ public class Utils {
} }
} }
public static Calendar getFirstDateOfSaison() {
Calendar calendar = Calendar.getInstance();
calendar.set(getSaison(), Calendar.SEPTEMBER, 1, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar;
}
public static Calendar toCalendar(Date date) { public static Calendar toCalendar(Date date) {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTime(date); cal.setTime(date);

File diff suppressed because it is too large Load Diff

View File

@@ -13,42 +13,42 @@
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0", "@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
"@fortawesome/fontawesome-svg-core": "^7.3.1", "@fortawesome/fontawesome-svg-core": "^7.2.0",
"@fortawesome/free-brands-svg-icons": "^7.3.1", "@fortawesome/free-brands-svg-icons": "^7.2.0",
"@fortawesome/free-regular-svg-icons": "^7.3.1", "@fortawesome/free-regular-svg-icons": "^7.2.0",
"@fortawesome/free-solid-svg-icons": "^7.3.1", "@fortawesome/free-solid-svg-icons": "^7.2.0",
"@fortawesome/react-fontawesome": "^3.5.0", "@fortawesome/react-fontawesome": "^3.2.0",
"axios": "1.18.0", "axios": "^1.13.6",
"browser-image-compression": "^2.0.2", "browser-image-compression": "^2.0.2",
"i18next": "^26.3.6", "i18next": "^25.8.15",
"i18next-browser-languagedetector": "^8.2.1", "i18next-browser-languagedetector": "^8.2.1",
"i18next-http-backend": "^4.0.1", "i18next-http-backend": "^3.0.2",
"jspdf": "4.2.1", "jspdf": "^4.2.0",
"jspdf-autotable": "^5.0.8", "jspdf-autotable": "^5.0.7",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"obs-websocket-js": "^5.0.8", "obs-websocket-js": "^5.0.7",
"proj4": "^2.21.0", "proj4": "^2.20.4",
"react": "^19.2.8", "react": "^19.2.4",
"react-dom": "^19.2.8", "react-dom": "^19.2.4",
"react-i18next": "^17.0.11", "react-i18next": "^16.5.6",
"react-is": "^19.2.8", "react-is": "^19.2.4",
"react-leaflet": "^5.0.0", "react-leaflet": "^5.0.0",
"react-loader-spinner": "^8.0.2", "react-loader-spinner": "^8.0.2",
"react-router-dom": "^7.18.2", "react-router-dom": "^7.13.1",
"react-toastify": "^11.1.0", "react-toastify": "^11.0.5",
"recharts": "^3.10.1", "recharts": "^3.8.0",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
"xlsx-js-style": "^1.2.0" "xlsx-js-style": "^1.2.0"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^19.2.18", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.4", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.5", "@vitejs/plugin-react": "^5.1.4",
"eslint": "^9.39.5", "eslint": "^9.7.0",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.3", "eslint-plugin-react-refresh": "^0.5.2",
"vite": "8.0.16" "vite": "^7.3.1"
} }
} }

View File

@@ -23,6 +23,8 @@ export function MemberList({source}) {
const {t} = useTranslation(); const {t} = useTranslation();
const [memberData, setMemberData] = useState([]); const [memberData, setMemberData] = useState([]);
const [licenceData, setLicenceData] = useState([]);
const [showLicenceState, setShowLicenceState] = useState((sessionStorage.getItem("showLicenceState") || "false") === "true");
const setFilter = (filter) => { const setFilter = (filter) => {
navigate("#" + encodeURI(JSON.stringify(filter))) navigate("#" + encodeURI(JSON.stringify(filter)))
@@ -35,7 +37,7 @@ export function MemberList({source}) {
payment: 2, payment: 2,
order: "", order: "",
categorie: "", categorie: "",
archived: sessionStorage.getItem("showMembreArchived") || true, archived: false,
...JSON.parse(decodeURI(hash.substring(1)) || "{}"), ...JSON.parse(decodeURI(hash.substring(1)) || "{}"),
} }
@@ -70,11 +72,23 @@ export function MemberList({source}) {
club: e.club, club: e.club,
categorie: e.categorie, categorie: e.categorie,
licence_number: e.licence, licence_number: e.licence,
licence: data.additionalData?.find(licence => licence.membre === e.id) licence: showLicenceState ? licenceData.find(licence => licence.membre === e.id) : null
}) })
} }
setMemberData(data2); setMemberData(data2);
}, [data]); }, [data, licenceData]);
useEffect(() => {
sessionStorage.setItem("showLicenceState", showLicenceState);
if (!showLicenceState)
return;
toast.promise(
apiAxios.get(`/licence/current/${source}`), getToastMessage("membre.toast.licences.load"))
.then(data => {
setLicenceData(data.data);
});
}, [showLicenceState]);
const search = (search) => { const search = (search) => {
if (search === filter.search) if (search === filter.search)
@@ -88,8 +102,8 @@ export function MemberList({source}) {
<div className="col-lg-9"> <div className="col-lg-9">
<SearchBar search={search} defaultValue={filter.search}/> <SearchBar search={search} defaultValue={filter.search}/>
{data {data
? <MakeCentralPanel data={data} visibleMember={memberData} navigate={navigate} page={filter.page} ? <MakeCentralPanel data={data} visibleMember={memberData} navigate={navigate} showLicenceState={showLicenceState}
setPage={e => setFilter({...filter, page: e})} source={source}/> page={filter.page} setPage={e => setFilter({...filter, page: e})} source={source}/>
: error : error
? <AxiosError error={error}/> ? <AxiosError error={error}/>
: <Def/> : <Def/>
@@ -116,11 +130,10 @@ export function MemberList({source}) {
<div className="card mb-4"> <div className="card mb-4">
<div className="card-header">{t('filtre')}</div> <div className="card-header">{t('filtre')}</div>
<div className="card-body"> <div className="card-body">
<FiltreBar showArchived={filter.archived} <FiltreBar showLicenceState={showLicenceState}
setShowArchived={e => { setShowLicenceState={setShowLicenceState}
setFilter({...filter, archived: e}) showArchived={filter.archived}
sessionStorage.setItem("showMembreArchived", e); setShowArchived={e => setFilter({...filter, archived: e})}
}}
clubFilter={filter.club} clubFilter={filter.club}
setClubFilter={e => setFilter({...filter, club: e})} setClubFilter={e => setFilter({...filter, club: e})}
source={source} source={source}
@@ -150,7 +163,6 @@ export function MemberList({source}) {
function FileOutput() { function FileOutput() {
const {t} = useTranslation(); const {t} = useTranslation();
function formatColumnDate(worksheet, col) { function formatColumnDate(worksheet, col) {
const range = XLSX.utils.decode_range(worksheet['!ref']) const range = XLSX.utils.decode_range(worksheet['!ref'])
// note: range.s.r + 1 skips the header row // note: range.s.r + 1 skips the header row
@@ -169,7 +181,6 @@ function FileOutput() {
toast.promise( toast.promise(
apiAxios.get(`/member/club/export`), getToastMessage("membre.toast.licences.export")) apiAxios.get(`/member/club/export`), getToastMessage("membre.toast.licences.export"))
.then(data => { .then(data => {
console.log(data)
const dataOut = [] const dataOut = []
for (const e of data.data) { for (const e of data.data) {
const tmp = { const tmp = {
@@ -343,7 +354,7 @@ function FileInput() {
); );
} }
function MakeCentralPanel({data, visibleMember, navigate, page, setPage, source}) { function MakeCentralPanel({data, visibleMember, navigate, showLicenceState, page, setPage, source}) {
const {t} = useTranslation(); const {t} = useTranslation();
const pages = [] const pages = []
@@ -363,7 +374,7 @@ function MakeCentralPanel({data, visibleMember, navigate, page, setPage, source}
})}</small> })}</small>
<div className="list-group"> <div className="list-group">
{visibleMember.map(member => ( {visibleMember.map(member => (
<MakeRow key={member.id} member={member} navigate={navigate} source={source}/>))} <MakeRow key={member.id} member={member} navigate={navigate} showLicenceState={showLicenceState} source={source}/>))}
</div> </div>
</div> </div>
<div className="mb-4"> <div className="mb-4">
@@ -380,11 +391,11 @@ function MakeCentralPanel({data, visibleMember, navigate, page, setPage, source}
</> </>
} }
function MakeRow({member, navigate, source}) { function MakeRow({member, showLicenceState, navigate, source}) {
const rowContent = <> const rowContent = <>
<div className="row" style={{padding: "0.6em 0"}}> <div className="row" style={{padding: "0.6em 0"}}>
<span className="col-auto">{(member.licence_number ? String(member.licence_number).padStart(5, '0') : "-------") + " "} <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> {(showLicenceState && member.licence != null && member.licence.pay) ? <FontAwesomeIcon icon={faEuroSign}/> : <>&nbsp;&nbsp;</>}</span>
<div className="ms-2 col-auto"> <div className="ms-2 col-auto">
<div className="fw-bold">{member.fname} {member.lname}</div> <div className="fw-bold">{member.fname} {member.lname}</div>
</div> </div>
@@ -400,7 +411,7 @@ function MakeRow({member, navigate, source}) {
</> </>
if (member.licence != null) { if (showLicenceState && member.licence != null) {
return <a className={"list-group-item d-flex justify-content-between align-items-start list-group-item-action list-group-item-" return <a className={"list-group-item d-flex justify-content-between align-items-start list-group-item-action list-group-item-"
+ (member.licence.validate ? "success" : (member.licence.certificate.length > 1 ? "warning" : "danger"))} + (member.licence.validate ? "success" : (member.licence.certificate.length > 1 ? "warning" : "danger"))}
style={{padding: "0 1em"}} style={{padding: "0 1em"}}
@@ -498,6 +509,8 @@ function OrderBar({onOrderChange, defaultValues = "", source}) {
} }
function FiltreBar({ function FiltreBar({
showLicenceState,
setShowLicenceState,
showArchived, showArchived,
setShowArchived, setShowArchived,
clubFilter, clubFilter,
@@ -513,6 +526,9 @@ function FiltreBar({
const {t} = useTranslation(); const {t} = useTranslation();
return <div> return <div>
<div className="mb-3">
<Checkbox value={showLicenceState} onChange={setShowLicenceState} label={t('membre.filtre.licence')}/>
</div>
<div className="mb-3"> <div className="mb-3">
<Checkbox value={showArchived} onChange={setShowArchived} name="checkbox2" label={t('membre.filtre.inactif')}/> <Checkbox value={showArchived} onChange={setShowArchived} name="checkbox2" label={t('membre.filtre.inactif')}/>
</div> </div>

View File

@@ -4,7 +4,7 @@ import {useEffect, useReducer, useState} from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faEuroSign, faPen} from "@fortawesome/free-solid-svg-icons"; import {faEuroSign, faPen} from "@fortawesome/free-solid-svg-icons";
import {AxiosError} from "../../../components/AxiosError.jsx"; import {AxiosError} from "../../../components/AxiosError.jsx";
import {apiAxios, getFirstDateOfSaison, getSaison, getToastMessage} from "../../../utils/Tools.js"; import {apiAxios, getSaison, getToastMessage} from "../../../utils/Tools.js";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
@@ -51,34 +51,13 @@ export function LicenceCard({userData}) {
dispatch({type: 'SORT'}) dispatch({type: 'SORT'})
}, [data]); }, [data]);
const handleAsk = () => {
const currentLicence = licences[0];
let certif = undefined;
if (currentLicence != null && currentLicence.certificate != null) {
const strings = currentLicence.certificate.split('¤');
if (currentLicence.saison === getSaison()) {
certif = currentLicence.certificate;
} else if (strings.length > 1) {
const date = new Date(strings[1]);
const max = getFirstDateOfSaison();
max.setFullYear(max.getFullYear() - 2);
if (max < date) {
certif = currentLicence.certificate;
}
}
}
setModal({id: -1, membre: userData.id, certificate: certif});
}
return <div className="card mb-4 mb-md-0"> return <div className="card mb-4 mb-md-0">
<div className="card-header container-fluid"> <div className="card-header container-fluid">
<div className="row"> <div className="row">
<div className="col">{t('licence')}</div> <div className="col">{t('licence')}</div>
<div className="col" style={{textAlign: 'right'}}> <div className="col" style={{textAlign: 'right'}}>
<button className="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#LicenceModal" <button className="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#LicenceModal"
onClick={handleAsk}>{t('button.ajouter')} onClick={_ => setModal({id: -1, membre: userData.id})}>{t('button.ajouter')}
</button> </button>
</div> </div>
</div> </div>
@@ -162,6 +141,13 @@ function ModalContent({licence, dispatch}) {
if (licence.id !== -1) { if (licence.id !== -1) {
setNew(false) setNew(false)
setSaison(licence.saison) setSaison(licence.saison)
if (licence.certificate === null) {
setCertificateBy("")
setCertificateDate("")
} else {
setCertificateBy(licence.certificate.split('¤')[0])
setCertificateDate(licence.certificate.split('¤')[1])
}
setValidate(licence.validate) setValidate(licence.validate)
setPay(licence.pay); setPay(licence.pay);
} else { } else {
@@ -172,13 +158,6 @@ function ModalContent({licence, dispatch}) {
setValidate(false) setValidate(false)
setPay(false); setPay(false);
} }
if (licence.certificate) {
setCertificateBy(licence.certificate.split('¤')[0])
setCertificateDate(licence.certificate.split('¤')[1])
} else {
setCertificateBy("")
setCertificateDate("")
}
}, [licence]); }, [licence]);
return <form onSubmit={e => sendLicence(e, dispatch)}> return <form onSubmit={e => sendLicence(e, dispatch)}>

View File

@@ -4,7 +4,7 @@ import {useEffect, useReducer, useState} from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faInfo, faPen} from "@fortawesome/free-solid-svg-icons"; import {faInfo, faPen} from "@fortawesome/free-solid-svg-icons";
import {AxiosError} from "../../../components/AxiosError.jsx"; import {AxiosError} from "../../../components/AxiosError.jsx";
import {apiAxios, getFirstDateOfSaison, getSaison, getToastMessage} from "../../../utils/Tools.js"; import {apiAxios, getSaison, getToastMessage} from "../../../utils/Tools.js";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {ColoredText} from "../../../components/ColoredCircle.jsx"; import {ColoredText} from "../../../components/ColoredCircle.jsx";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
@@ -32,7 +32,7 @@ function licenceReducer(licences, action) {
} }
export function LicenceCard({userData}) { export function LicenceCard({userData}) {
const defaultLicence = {id: -1, membre: userData.id, validate: false, saison: getSaison(), certificate: null} const defaultLicence = {id: -1, membre: userData.id, validate: false, saison: getSaison(), certificate: false}
const {t} = useTranslation(); const {t} = useTranslation();
const setLoading = useLoadingSwitcher() const setLoading = useLoadingSwitcher()
@@ -48,33 +48,13 @@ export function LicenceCard({userData}) {
dispatch({type: 'SORT'}) dispatch({type: 'SORT'})
}, [data]); }, [data]);
const handleAsk = () => {
const currentLicence = licences[0];
let certif = undefined;
if (currentLicence != null && currentLicence.certificate != null) {
const strings = currentLicence.certificate.split('¤');
if (currentLicence.saison === getSaison()) {
certif = currentLicence.certificate;
} else if (strings.length > 1) {
const date = new Date(strings[1]);
const max = getFirstDateOfSaison();
max.setFullYear(max.getFullYear() - 2);
if (max < date)
certif = currentLicence.certificate;
}
}
setModal({...defaultLicence, certificate: certif});
}
return <div className="card mb-4 mb-md-0"> return <div className="card mb-4 mb-md-0">
<div className="card-header container-fluid"> <div className="card-header container-fluid">
<div className="row"> <div className="row">
<div className="col">{t('licence')}</div> <div className="col">{t('licence')}</div>
<div className="col" style={{textAlign: 'right'}}> <div className="col" style={{textAlign: 'right'}}>
<button className="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#LicenceModal" <button className="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#LicenceModal"
onClick={handleAsk} onClick={() => setModal(defaultLicence)}
disabled={licences.some(licence => licence.saison === getSaison())}>{t('demander')} disabled={licences.some(licence => licence.saison === getSaison())}>{t('demander')}
</button> </button>
</div> </div>
@@ -147,14 +127,15 @@ function ModalContent({licence, dispatch}) {
useEffect(() => { useEffect(() => {
if (licence.id !== -1) { if (licence.id !== -1) {
setNew(false) setNew(false)
if (licence.certificate === null) {
setCertificateBy("")
setCertificateDate("")
} else {
setCertificateBy(licence.certificate.split('¤')[0])
setCertificateDate(licence.certificate.split('¤')[1])
}
} else { } else {
setNew(true) setNew(true)
}
if (licence.certificate) {
setCertificateBy(licence.certificate.split('¤')[0])
setCertificateDate(licence.certificate.split('¤')[1])
} else {
setCertificateBy("") setCertificateBy("")
setCertificateDate("") setCertificateDate("")
} }

View File

@@ -48,7 +48,7 @@ export function PointPanel({menuActions}) {
<div className="col row align-items-center"> <div className="col row align-items-center">
<button className="btn btn-danger" onClick={handleReset}>{t('réinitialiser')}</button> <button className="btn btn-danger" onClick={handleReset}>{t('réinitialiser')}</button>
{ /*<button className="btn btn-success" onClick={handleSave}>{t('sauvegarder')}</button> */} <button className="btn btn-success" onClick={handleSave}>{t('sauvegarder')}</button>
</div> </div>
<SendScore scoreRouge={scoreRouge} scoreBleu={scoreBleu}/> <SendScore scoreRouge={scoreRouge} scoreBleu={scoreBleu}/>
</div> </div>

View File

@@ -117,14 +117,6 @@ export function getSaison(currentDate = new Date()) {
} }
} }
export function getFirstDateOfSaison() {
const year = getSaison();
const firstDate = new Date(year, 8, 1); // 8 = septembre, 1 = premier jour
firstDate.setHours(0, 0, 0, 0); // Heure, minutes, secondes, millisecondes à 0
return firstDate;
}
export function getCatName(cat) { export function getCatName(cat) {
switch (cat) { switch (cat) {
case "SUPER_MINI": case "SUPER_MINI":