Compare commits

..

No commits in common. "b7a1e5843687fa960ab14426189b558df85e873b" and "847674d62c312202383a559857ed5aa9aedaaed2" have entirely different histories.

10 changed files with 60 additions and 189 deletions

View File

@ -270,16 +270,14 @@ public class MembreService {
if (model.getEmail() != null && !model.getEmail().isBlank()) { if (model.getEmail() != null && !model.getEmail().isBlank()) {
if (model.getLicence() != null && !model.getLicence().equals(dataIn.getLicence())) { if (model.getLicence() != null && !model.getLicence().equals(dataIn.getLicence())) {
LOGGER.info("Similar membres found: " + model); LOGGER.info("Similar membres found: " + model);
throw new DBadRequestException( throw new DBadRequestException("Email '" + model.getEmail() + "' déja utilisé");
"Email '" + model.getEmail() + "' déja utilisé par " + model.getFname() + " " + model.getFname());
} }
if (StringSimilarity.similarity(model.getLname().toUpperCase(), if (StringSimilarity.similarity(model.getLname().toUpperCase(),
dataIn.getNom().toUpperCase()) > 3 || StringSimilarity.similarity( dataIn.getNom().toUpperCase()) > 3 || StringSimilarity.similarity(
model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3) { model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3) {
LOGGER.info("Similar membres found: " + model); LOGGER.info("Similar membres found: " + model);
throw new DBadRequestException( throw new DBadRequestException("Email '" + model.getEmail() + "' déja utilisé");
"Email '" + model.getEmail() + "' déja utilisé par " + model.getFname() + " " + model.getFname());
} }
} }
@ -290,8 +288,7 @@ public class MembreService {
model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3)) { model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3)) {
LOGGER.info("Similar membres found: " + model); LOGGER.info("Similar membres found: " + model);
throw new DBadRequestException( throw new DBadRequestException(
"Pour enregistrer un nouveau membre, veuillez laisser le champ licence vide. (tentative de changement non-autotiser de nom sur la licence " "Pour enregistrer un nouveau membre, veuillez laisser le champ licence vide.");
+ model.getLicence() + " pour " + model.getFname() + " " + model.getFname() + ")");
} }
ls.logChange("Nom", model.getLname(), dataIn.getNom().toUpperCase(), model); ls.logChange("Nom", model.getLname(), dataIn.getNom().toUpperCase(), model);
@ -299,7 +296,8 @@ public class MembreService {
dataIn.getPrenom().toUpperCase().charAt(0) + dataIn.getPrenom().substring(1), model); dataIn.getPrenom().toUpperCase().charAt(0) + dataIn.getPrenom().substring(1), model);
model.setLname(dataIn.getNom().toUpperCase()); model.setLname(dataIn.getNom().toUpperCase());
model.setFname(Utils.formatPrenom(dataIn.getPrenom())); model.setFname(dataIn.getPrenom().toUpperCase().charAt(0) + dataIn.getPrenom().substring(1));
if (dataIn.getEmail() != null && !dataIn.getEmail().isBlank()) { if (dataIn.getEmail() != null && !dataIn.getEmail().isBlank()) {
ls.logChange("Email", model.getEmail(), dataIn.getEmail(), model); ls.logChange("Email", model.getEmail(), dataIn.getEmail(), model);
@ -419,7 +417,7 @@ public class MembreService {
private Uni<String> update(Uni<MembreModel> uni, FullMemberForm membre, boolean admin) { private Uni<String> update(Uni<MembreModel> uni, FullMemberForm membre, boolean admin) {
return uni.chain(target -> { return uni.chain(target -> {
ls.logChange("Prénom", target.getFname(), membre.getFname(), target); ls.logChange("Prénom", target.getFname(), membre.getFname(), target);
target.setFname(Utils.formatPrenom(membre.getFname())); target.setFname(membre.getFname());
ls.logChange("Nom", target.getLname(), membre.getLname(), target); ls.logChange("Nom", target.getLname(), membre.getLname(), target);
target.setLname(membre.getLname().toUpperCase()); target.setLname(membre.getLname().toUpperCase());
ls.logChange("Pays", target.getCountry(), membre.getCountry(), target); ls.logChange("Pays", target.getCountry(), membre.getCountry(), target);
@ -562,8 +560,8 @@ public class MembreService {
private static MembreModel getMembreModel(FullMemberForm input, ClubModel clubModel) { private static MembreModel getMembreModel(FullMemberForm input, ClubModel clubModel) {
MembreModel model = new MembreModel(); MembreModel model = new MembreModel();
model.setFname(Utils.formatPrenom(input.getFname())); model.setFname(input.getFname());
model.setLname(input.getLname().toUpperCase()); model.setLname(input.getLname());
model.setEmail(input.getEmail()); model.setEmail(input.getEmail());
model.setLicence(null); model.setLicence(null);
model.setGenre(input.getGenre()); model.setGenre(input.getGenre());

View File

@ -335,34 +335,4 @@ public class Utils {
return (int) ((calendar.getTimeInMillis() - now.getTimeInMillis()) / (1000 * 60 * 60 * 24)); return (int) ((calendar.getTimeInMillis() - now.getTimeInMillis()) / (1000 * 60 * 60 * 24));
} }
public static String formatPrenom(String input) {
if (input == null || input.isEmpty()) {
return input;
}
StringBuilder result = new StringBuilder();
String[] mots = input.split(" ");
for (String mot : mots) {
if (!mot.isEmpty()) {
String[] parties = mot.split("[-']");
StringBuilder motFormate = new StringBuilder();
for (int i = 0; i < parties.length; i++) {
if (!parties[i].isEmpty()) {
String premiereLettre = parties[i].substring(0, 1).toUpperCase();
String reste = parties[i].substring(1).toLowerCase();
motFormate.append(premiereLettre).append(reste);
}
if (i < parties.length - 1) {
motFormate.append(mot.charAt(mot.indexOf(parties[i]) + parties[i].length()));
}
}
result.append(motFormate).append(" ");
}
}
return result.toString().trim();
}
} }

View File

@ -21,7 +21,6 @@ import org.jboss.logging.Logger;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.time.Duration;
import java.util.*; import java.util.*;
import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER; import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER;
@ -167,7 +166,6 @@ public class CompetitionWS {
return Uni.createFrom().item(makeError(message, "Permission denied")).toMulti(); return Uni.createFrom().item(makeError(message, "Permission denied")).toMulti();
return ((Uni<?>) method.invoke(entry.getValue(), connection, return ((Uni<?>) method.invoke(entry.getValue(), connection,
MAPPER.treeToValue(message.data(), method.getParameterTypes()[1]))) MAPPER.treeToValue(message.data(), method.getParameterTypes()[1])))
.ifNoItem().after(Duration.ofSeconds(5)).fail()
.map(o -> makeReply(message, o)) .map(o -> makeReply(message, o))
.onFailure() .onFailure()
.recoverWithItem(t -> { .recoverWithItem(t -> {

View File

@ -118,8 +118,7 @@ public class RCategorie {
uni = uni.chain(__ -> treeRepository.delete("category = ?1", cat.getId())) uni = uni.chain(__ -> treeRepository.delete("category = ?1", cat.getId()))
.chain(__ -> matchRepository.delete("category = ?1 AND category_ord = -42", cat)); .chain(__ -> matchRepository.delete("category = ?1 AND category_ord = -42", cat));
} }
Uni<Long> finalUni = uni; return uni;
return Panache.withTransaction(() -> finalUni);
}) })
.call(cat -> SSCategorie.sendCategory(connection, cat)) .call(cat -> SSCategorie.sendCategory(connection, cat))
.replaceWithVoid(); .replaceWithVoid();
@ -206,16 +205,6 @@ public class RCategorie {
.replaceWithVoid(); .replaceWithVoid();
} }
@WSReceiver(code = "deleteCategory", permission = PermLevel.ADMIN)
public Uni<Void> deleteCategory(WebSocketConnection connection, Long id) {
return getById(id, connection)
.call(cat -> Panache.withTransaction(() -> treeRepository.delete("category = ?1", cat.getId())
.call(__ -> matchRepository.delete("category = ?1", cat))))
.chain(cat -> Panache.withTransaction(() -> categoryRepository.delete(cat)))
.call(__ -> SSCategorie.sendDelCategory(connection, id))
.replaceWithVoid();
}
@RegisterForReflection @RegisterForReflection
public record JustCategorie(long id, String name, int type, String liceName) { public record JustCategorie(long id, String name, int type, String liceName) {
public static JustCategorie from(CategoryModel m) { public static JustCategorie from(CategoryModel m) {

View File

@ -30,8 +30,4 @@ public class SSCategorie {
public static Uni<Void> sendTreeCategory(WebSocketConnection connection, List<TreeEntity> treeEntities) { public static Uni<Void> sendTreeCategory(WebSocketConnection connection, List<TreeEntity> treeEntities) {
return CompetitionWS.sendNotifyToOtherEditor(connection, "sendTreeCategory", treeEntities); return CompetitionWS.sendNotifyToOtherEditor(connection, "sendTreeCategory", treeEntities);
} }
public static Uni<?> sendDelCategory(WebSocketConnection connection, Long id) {
return CompetitionWS.sendNotifyToOtherEditor(connection, "sendDelCategory", id);
}
} }

View File

@ -15,16 +15,16 @@ export function CMAdmin() {
const categoryListener = ({data}) => { const categoryListener = ({data}) => {
if (!cat || data.id !== cat.id) if (!cat || data.id !== cat.id)
return return
setCat(cat_ => ({ setCat({
...cat_, ...cat,
name: data.name, name: data.name,
liceName: data.liceName, liceName: data.liceName,
type: data.type type: data.type
})) })
} }
dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}}) dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}})
return () => dispatch({type: 'removeListener', payload: categoryListener}) return () => dispatch({type: 'removeListener', payload: categoryListener})
}, [cat]); }, []);
return <> return <>
<div className="card"> <div className="card">
@ -60,34 +60,18 @@ function CategoryHeader({cat, setCatId}) {
data data
]) ])
} }
const sendAddCategory = ({data}) => {
setCats([...cats, data])
}
const sendDelCategory = ({data}) => {
setCatId(catId => {
if (catId === data) return null;
return catId;
})
setCats([...cats.filter(c => c.id !== data)])
}
dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}}) dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}})
dispatch({type: 'addListener', payload: {callback: sendAddCategory, code: 'sendAddCategory'}}) return () => dispatch({type: 'removeListener', payload: categoryListener})
dispatch({type: 'addListener', payload: {callback: sendDelCategory, code: 'sendDelCategory'}})
return () => {
dispatch({type: 'removeListener', payload: categoryListener})
dispatch({type: 'removeListener', payload: sendAddCategory})
dispatch({type: 'removeListener', payload: sendDelCategory})
}
}, [cats]); }, [cats]);
useEffect(() => { useEffect(() => {
if (cats && cats.length > 0 && !cat || (cats && !cats.find(c => c.id === cat.id))) { if (cats && cats.length > 0 && !cat) {
setCatId(cats.sort((a, b) => a.name.localeCompare(b.name))[0].id); setCatId(cats.sort((a, b) => a.name.localeCompare(b.name))[0].id);
} else if (cats && cats.length === 0) { } else if (cats && cats.length === 0) {
setModal({}); setModal({});
bthRef.current.click(); bthRef.current.click();
} }
}, [cats, cat]); }, [cats]);
const handleCatChange = (e) => { const handleCatChange = (e) => {
const selectedCatId = e.target.value; const selectedCatId = e.target.value;
@ -104,7 +88,7 @@ function CategoryHeader({cat, setCatId}) {
<div className="col-auto"> <div className="col-auto">
<div className="input-group"> <div className="input-group">
<h5 style={{margin: "auto 0.5em auto 0"}}>Edition de la catégorie</h5> <h5 style={{margin: "auto 0.5em auto 0"}}>Edition de la catégorie</h5>
<select className="form-select" onChange={handleCatChange} value={cat?.id || ""}> <select className="form-select" onChange={handleCatChange}>
{cats && cats.sort((a, b) => a.name.localeCompare(b.name)).map(c => ( {cats && cats.sort((a, b) => a.name.localeCompare(b.name)).map(c => (
<option key={c.id} value={c.id}>{c.name}</option>))} <option key={c.id} value={c.id}>{c.name}</option>))}
{cats && <option value={-1}>Nouvelle...</option>} {cats && <option value={-1}>Nouvelle...</option>}
@ -114,7 +98,7 @@ function CategoryHeader({cat, setCatId}) {
<div className="col" style={{margin: "auto 0", textAlign: "center"}}> <div className="col" style={{margin: "auto 0", textAlign: "center"}}>
{cat && {cat &&
<div>Type: {(cat.type & 1) !== 0 ? "Poule" : ""}{cat.type === 3 ? " & " : ""}{(cat.type & 2) !== 0 ? "Tournois" : ""} | <div>Type: {(cat.type & 1) !== 0 ? "Poule" : ""}{cat.type === 3 ? " & " : ""}{(cat.type & 2) !== 0 ? "Tournois" : ""} |
Zone: {cat.liceName}</div>} Lice: {cat.liceName}</div>}
</div> </div>
<div className="col-auto"> <div className="col-auto">
<button className="btn btn-primary float-end" onClick={() => { <button className="btn btn-primary float-end" onClick={() => {
@ -144,7 +128,7 @@ function CategoryHeader({cat, setCatId}) {
function ModalContent({state, setCatId, setConfirm, confirmRef}) { function ModalContent({state, setCatId, setConfirm, confirmRef}) {
const [name, setName] = useState("") const [name, setName] = useState("")
const [lice, setLice] = useState("A") const [lice, setLice] = useState("1")
const [poule, setPoule] = useState(true) const [poule, setPoule] = useState(true)
const [tournoi, setTournoi] = useState(false) const [tournoi, setTournoi] = useState(false)
const [size, setSize] = useState(4) const [size, setSize] = useState(4)
@ -154,7 +138,7 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
useEffect(() => { useEffect(() => {
setName(state.name || ""); setName(state.name || "");
setLice(state.liceName || "A"); setLice(state.liceName || "1");
setPoule(((state.type || 1) & 1) !== 0); setPoule(((state.type || 1) & 1) !== 0);
setTournoi((state.type & 2) !== 0); setTournoi((state.type & 2) !== 0);
@ -180,7 +164,7 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
const regex = /^([^;]+;)*[^;]+$/; const regex = /^([^;]+;)*[^;]+$/;
if (regex.test(lice.trim()) === false) { if (regex.test(lice.trim()) === false) {
toast.error("Le format du nom des zones de combat est invalide. Veuillez séparer les noms par des ';'."); toast.error("Le format du nom des lices est invalide. Veuillez séparer les noms par des ';'.");
return; return;
} }
@ -286,6 +270,8 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
error: 'Erreur lors de la création de la catégorie' error: 'Erreur lors de la création de la catégorie'
} }
).then(id => { ).then(id => {
setCatId(id);
if (tournoi) { if (tournoi) {
const trees = build_tree(size, loserMatch) const trees = build_tree(size, loserMatch)
console.log("Creating trees for new category:", trees); console.log("Creating trees for new category:", trees);
@ -296,9 +282,7 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
success: 'Arbres créés !', success: 'Arbres créés !',
error: 'Erreur lors de la création des arbres' error: 'Erreur lors de la création des arbres'
} }
).finally(() => setCatId(id)) )
} else {
setCatId(id);
} }
}) })
} }
@ -315,7 +299,7 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
return <form onSubmit={handleSubmit}> return <form onSubmit={handleSubmit}>
<div className="modal-header"> <div className="modal-header">
<h1 className="modal-title fs-5" id="CategorieModalLabel">{state.id === undefined ? "Ajouter" : "Modifier"} une catégorie</h1> <h1 className="modal-title fs-5" id="CategorieModalLabel">Ajouter une catégorie</h1>
<button type="button" className="btn-close" data-bs-dismiss="modal" <button type="button" className="btn-close" data-bs-dismiss="modal"
aria-label="Close"></button> aria-label="Close"></button>
</div> </div>
@ -327,8 +311,8 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
</div> </div>
<div className="mb-3"> <div className="mb-3">
<label htmlFor="liceInput1" className="form-label">Nom des zones de combat <small>(séparée par des ';')</small></label> <label htmlFor="liceInput1" className="form-label">Nom des lices <small>(séparée par des ';')</small></label>
<input type="text" className="form-control" id="liceInput1" placeholder="A;B" name="zone de combat" value={lice} <input type="text" className="form-control" id="liceInput1" placeholder="1;2" name="lice" value={lice}
onChange={e => setLice(e.target.value)}/> onChange={e => setLice(e.target.value)}/>
</div> </div>
@ -389,22 +373,6 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
<div className="modal-footer"> <div className="modal-footer">
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">Fermer</button> <button type="button" className="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal">Enregistrer</button> <button type="submit" className="btn btn-primary" data-bs-dismiss="modal">Enregistrer</button>
{state.id !== undefined && <button type="button" className="btn btn-danger" data-bs-dismiss="modal" onClick={() => {
setConfirm({
title: "Suppression de la catégorie",
message: `Voulez-vous vraiment supprimer la catégorie ${state.name}. Cela va supprimer tous les matchs associés !`,
confirm: () => {
toast.promise(sendRequest('deleteCategory', state.id),
{
pending: 'Suppression de la catégorie...',
success: 'Catégorie supprimée !',
error: 'Erreur lors de la suppression de la catégorie'
}
).then(() => setCatId(null));
}
})
confirmRef.current.click();
}}>Supprimer</button>}
</div> </div>
</form> </form>
} }

View File

@ -27,22 +27,8 @@ export function CategorieSelect({catId, setCatId, menuActions}) {
const categoryListener = ({data}) => { const categoryListener = ({data}) => {
setCats([...cats.filter(c => c.id !== data.id), data]) setCats([...cats.filter(c => c.id !== data.id), data])
} }
const sendAddCategory = ({data}) => {
setCats([...cats, data])
}
const sendDelCategory = ({data}) => {
if (catId === data)
setCatId(-1);
setCats([...cats.filter(c => c.id !== data)])
}
dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}}) dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}})
dispatch({type: 'addListener', payload: {callback: sendAddCategory, code: 'sendAddCategory'}}) return () => dispatch({type: 'removeListener', payload: categoryListener})
dispatch({type: 'addListener', payload: {callback: sendDelCategory, code: 'sendDelCategory'}})
return () => {
dispatch({type: 'removeListener', payload: categoryListener})
dispatch({type: 'removeListener', payload: sendAddCategory})
dispatch({type: 'removeListener', payload: sendDelCategory})
}
}, [cats]); }, [cats]);
const cat = cats?.find(c => c.id === catId); const cat = cats?.find(c => c.id === catId);
@ -145,15 +131,10 @@ function ListMatch({cat, matches, trees, menuActions}) {
const [type, setType] = useState(1); const [type, setType] = useState(1);
useEffect(() => { useEffect(() => {
if (!cat)
return;
if ((cat.type & type) === 0) if ((cat.type & type) === 0)
setType(cat.type); setType(cat.type);
}, [cat]); }, [cat]);
if (!cat)
return <></>;
return <div style={{marginTop: "1em"}}> return <div style={{marginTop: "1em"}}>
{cat && cat.type === 3 && <> {cat && cat.type === 3 && <>
<ul className="nav nav-tabs"> <ul className="nav nav-tabs">
@ -192,10 +173,6 @@ function MatchList({matches, cat, menuActions}) {
.map(m => ({...m, win: win(m.scores)})) .map(m => ({...m, win: win(m.scores)}))
const firstIndex = marches2.findLastIndex(m => m.poule === '-') + 1; const firstIndex = marches2.findLastIndex(m => m.poule === '-') + 1;
const isActiveMatch = (index) => {
return liceName.length === 1 || (liceName[(index - firstIndex) % liceName.length] === lice)
}
const match = matches.find(m => m.id === activeMatch) const match = matches.find(m => m.id === activeMatch)
useEffect(() => { useEffect(() => {
if (!match) { if (!match) {
@ -206,7 +183,7 @@ function MatchList({matches, cat, menuActions}) {
payload: { payload: {
c1: match.c1, c1: match.c1,
c2: match.c2, c2: match.c2,
next: marches2.filter((m, index) => !m.end && isActiveMatch(index) && m.id !== activeMatch).map(m => ({ next: marches2.filter((m, index) => !m.end && liceName[(index - firstIndex) % liceName.length] === lice && m.id !== activeMatch).map(m => ({
c1: m.c1, c1: m.c1,
c2: m.c2 c2: m.c2
})) }))
@ -221,7 +198,7 @@ function MatchList({matches, cat, menuActions}) {
useEffect(() => { useEffect(() => {
if (match && match.poule !== lice) if (match && match.poule !== lice)
setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id) setActiveMatch(marches2.find((m, index) => !m.end && liceName[(index - firstIndex) % liceName.length] === lice)?.id)
}, [lice]); }, [lice]);
useEffect(() => { useEffect(() => {
@ -230,12 +207,12 @@ function MatchList({matches, cat, menuActions}) {
if (marches2.some(m => m.id === activeMatch)) if (marches2.some(m => m.id === activeMatch))
return; return;
setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id); setActiveMatch(marches2.find((m, index) => !m.end && liceName[(index - firstIndex) % liceName.length] === lice)?.id);
}, [matches]) }, [matches])
return <> return <>
{liceName.length > 1 && {liceName.length > 1 &&
<div className="input-group" style={{maxWidth: "15em", marginTop: "0.5em"}}> <div className="input-group" style={{maxWidth: "10em", marginTop: "0.5em"}}>
<label className="input-group-text" htmlFor="selectLice">Zone de combat</label> <label className="input-group-text" htmlFor="selectLice">Lice</label>
<select className="form-select" id="selectLice" value={lice} onChange={e => { <select className="form-select" id="selectLice" value={lice} onChange={e => {
setLice(e.target.value); setLice(e.target.value);
localStorage.setItem("cm_lice", e.target.value); localStorage.setItem("cm_lice", e.target.value);
@ -247,11 +224,11 @@ function MatchList({matches, cat, menuActions}) {
</div> </div>
} }
<div className="table-responsive-xxl overflow-y-auto" style={{maxHeight: "50vh"}}> <div className="table-responsive-xxl">
<table className="table table-striped table-hover"> <table className="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">Z</th> <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">L</th>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">P</th> <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">P</th>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">N°</th> <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">N°</th>
<th style={{textAlign: "center"}} scope="col"></th> <th style={{textAlign: "center"}} scope="col"></th>
@ -263,7 +240,7 @@ function MatchList({matches, cat, menuActions}) {
<tbody className="table-group-divider"> <tbody className="table-group-divider">
{marches2.map((m, index) => ( {marches2.map((m, index) => (
<tr key={m.id} <tr key={m.id}
className={m.id === activeMatch ? "table-info" : (isActiveMatch(index) ? "" : "table-warning")} className={m.id === activeMatch ? "table-info" : (liceName[(index - firstIndex) % liceName.length] === lice ? "" : "table-warning")}
onClick={() => setActiveMatch(m.id)}> onClick={() => setActiveMatch(m.id)}>
<td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}> <td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>
{liceName[(index - firstIndex) % liceName.length]}</td> {liceName[(index - firstIndex) % liceName.length]}</td>
@ -282,7 +259,7 @@ function MatchList({matches, cat, menuActions}) {
</table> </table>
</div> </div>
{activeMatch && <LoadingProvider><ScorePanel matchId={activeMatch} matchs={matches} match={match} menuActions={menuActions}/></LoadingProvider>} {activeMatch && <LoadingProvider><ScorePanel matchId={activeMatch} match={match} menuActions={menuActions}/></LoadingProvider>}
</> </>
} }
@ -348,29 +325,27 @@ function BuildTree({treeData, matches, menuActions}) {
return <div> return <div>
<div className="overflow-y-auto" style={{maxHeight: "50vh"}}> <div ref={scrollRef} className="overflow-x-auto" style={{position: "relative"}}>
<div ref={scrollRef} className="overflow-x-auto" style={{position: "relative"}}> <DrawGraph root={trees} scrollRef={scrollRef} onMatchClick={onMatchClick} onClickVoid={onClickVoid}
<DrawGraph root={trees} scrollRef={scrollRef} onMatchClick={onMatchClick} onClickVoid={onClickVoid} matchSelect={currentMatch?.matchSelect} matchNext={currentMatch?.matchNext} size={23}/>
matchSelect={currentMatch?.matchSelect} matchNext={currentMatch?.matchNext} size={23}/>
</div>
</div> </div>
{currentMatch?.matchSelect && {currentMatch?.matchSelect &&
<LoadingProvider><ScorePanel matchId={currentMatch?.matchSelect} matchs={matches} match={match} menuActions={menuActions}/></LoadingProvider>} <LoadingProvider><ScorePanel matchId={currentMatch?.matchSelect} match={match} menuActions={menuActions}/></LoadingProvider>}
</div> </div>
} }
function ScorePanel({matchId, matchs, match, menuActions}) { function ScorePanel({matchId, match, menuActions}) {
const onClickVoid = useRef(() => { const onClickVoid = useRef(() => {
}); });
return <div className="row" onClick={onClickVoid.current}> return <div className="row" onClick={onClickVoid.current}>
<ScorePanel_ matchId={matchId} matchs={matchs} match={match} menuActions={menuActions} onClickVoid_={onClickVoid}/> <ScorePanel_ matchId={matchId} match={match} menuActions={menuActions} onClickVoid_={onClickVoid}/>
<CardPanel matchId={matchId} match={match}/> <CardPanel matchId={matchId} match={match}/>
</div> </div>
} }
function ScorePanel_({matchId, matchs, match, menuActions, onClickVoid_}) { function ScorePanel_({matchId, match, menuActions, onClickVoid_}) {
const {sendRequest} = useWS() const {sendRequest} = useWS()
const setLoading = useLoadingSwitcher() const setLoading = useLoadingSwitcher()
@ -380,11 +355,6 @@ function ScorePanel_({matchId, matchs, match, menuActions, onClickVoid_}) {
const tableRef = useRef(null) const tableRef = useRef(null)
const scoreRef = useRef([]) const scoreRef = useRef([])
const lastScoreClick = useRef(null) const lastScoreClick = useRef(null)
const scoreInRef = useRef(null)
useEffect(() => {
scoreInRef.current = scoreIn;
}, [scoreIn]);
useEffect(() => { useEffect(() => {
menuActions.current.saveScore = (scoreRed, scoreBlue) => { menuActions.current.saveScore = (scoreRed, scoreBlue) => {
@ -433,12 +403,9 @@ function ScorePanel_({matchId, matchs, match, menuActions, onClickVoid_}) {
const {matchId, round, comb} = lastScoreClick.current; const {matchId, round, comb} = lastScoreClick.current;
lastScoreClick.current = null; lastScoreClick.current = null;
const scoreIn_ = String(scoreInRef.current).trim() === "" ? -1000 : Number(scoreInRef.current); const scoreIn_ = String(scoreIn).trim() === "" ? -1000 : Number(scoreIn);
const score = matchs.find(m => m.id === matchId).scores.find(s => s.n_round === round);
console.log("Updating score", matchId, round, comb, scoreIn_, score);
const score = match.scores.find(s => s.n_round === round);
let newScore; let newScore;
if (score) { if (score) {
if (comb === 1) if (comb === 1)
@ -507,18 +474,6 @@ function ScorePanel_({matchId, matchs, match, menuActions, onClickVoid_}) {
setEnd(match.end); setEnd(match.end);
}, [match]); }, [match]);
useEffect(() => {
const handleClickOutside = (event) => {
if (inputRef.current && !inputRef.current.contains(event.target)) {
onClickVoid();
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]') const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const o = [...tooltipTriggerList] const o = [...tooltipTriggerList]
o.map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)) o.map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))

View File

@ -49,6 +49,9 @@ export function CMTable() {
<CategorieSelect catId={catId} setCatId={setCatId} menuActions={menuActions}/> <CategorieSelect catId={catId} setCatId={setCatId} menuActions={menuActions}/>
</div> </div>
</div> </div>
<div style={{backgroundColor: "#c70000"}}>
D
</div>
</div> </div>
</div> </div>
<Menu menuActions={menuActions}/> <Menu menuActions={menuActions}/>

View File

@ -47,10 +47,10 @@ export function CategoryContent({cat, catId, setCat}) {
const treeListener = ({data}) => { const treeListener = ({data}) => {
if (!cat || data.length < 1 || data[0].categorie !== cat.id) if (!cat || data.length < 1 || data[0].categorie !== cat.id)
return return
setCat(cat_ => ({ setCat({
...cat_, ...cat,
trees: data.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)) trees: data.map(d => from_sendTree(d, true))
})) })
let matches2 = []; let matches2 = [];
let combsToAdd = []; let combsToAdd = [];
@ -64,16 +64,10 @@ export function CategoryContent({cat, catId, setCat}) {
reducer({type: 'UPDATE_OR_ADD', payload: {...data, c1: data.c1?.id, c2: data.c2?.id}}); reducer({type: 'UPDATE_OR_ADD', payload: {...data, c1: data.c1?.id, c2: data.c2?.id}});
combDispatch({type: 'SET_ALL', payload: {source: "match", data: [data.c1, data.c2].filter(d => d != null)}}); combDispatch({type: 'SET_ALL', payload: {source: "match", data: [data.c1, data.c2].filter(d => d != null)}});
setGroups(prev => { if (data.c1 !== null && !groupsRef.current.some(g => g.id === data.c1?.id))
if (data.c1 !== null && !prev.some(g => g.id === data.c1?.id)) setGroups(prev => [...prev, {id: data.c1?.id, poule: data.poule}]);
return [...prev, {id: data.c1?.id, poule: data.poule}]; if (data.c2 !== null && !groupsRef.current.some(g => g.id === data.c2?.id))
return prev; setGroups(prev => [...prev, {id: data.c2?.id, poule: data.poule}]);
})
setGroups(prev => {
if (data.c2 !== null && !prev.some(g => g.id === data.c2?.id))
return [...prev, {id: data.c2?.id, poule: data.poule}];
return prev;
})
} }
} }
@ -109,7 +103,7 @@ export function CategoryContent({cat, catId, setCat}) {
name: data.name, name: data.name,
liceName: data.liceName, liceName: data.liceName,
type: data.type, type: data.type,
trees: data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)) trees: data.trees.map(d => from_sendTree(d, true))
}) })
let matches2 = []; let matches2 = [];
@ -560,7 +554,7 @@ function MatchList({matches, cat, groups, reducer}) {
<tr> <tr>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">N°</th> <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">N°</th>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">Poule</th> <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">Poule</th>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">Zone</th> <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">Lice</th>
<th style={{textAlign: "center"}} scope="col"></th> <th style={{textAlign: "center"}} scope="col"></th>
<th style={{textAlign: "center"}} scope="col">Rouge</th> <th style={{textAlign: "center"}} scope="col">Rouge</th>
<th style={{textAlign: "center"}} scope="col">Blue</th> <th style={{textAlign: "center"}} scope="col">Blue</th>

View File

@ -95,7 +95,7 @@ function WSStatus({setPerm}) {
return () => dispatch({type: 'removeListener', payload: welcomeListener}) return () => dispatch({type: 'removeListener', payload: welcomeListener})
}, []) }, [])
return <div className="row" style={{marginRight: "inherit"}}> return <div className="row">
<h2 className="col">{name}</h2> <h2 className="col">{name}</h2>
<div className="col-auto" style={{margin: "auto 0", padding: 0}}>Serveur: <ColoredCircle <div className="col-auto" style={{margin: "auto 0", padding: 0}}>Serveur: <ColoredCircle
color={isReady ? (inWait ? "#ffad32" : "#00c700") : "#e50000"}/> color={isReady ? (inWait ? "#ffad32" : "#00c700") : "#e50000"}/>