Compare commits

...

9 Commits

10 changed files with 189 additions and 60 deletions

View File

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

View File

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

View File

@ -118,7 +118,8 @@ public class RCategorie {
uni = uni.chain(__ -> treeRepository.delete("category = ?1", cat.getId()))
.chain(__ -> matchRepository.delete("category = ?1 AND category_ord = -42", cat));
}
return uni;
Uni<Long> finalUni = uni;
return Panache.withTransaction(() -> finalUni);
})
.call(cat -> SSCategorie.sendCategory(connection, cat))
.replaceWithVoid();
@ -205,6 +206,16 @@ public class RCategorie {
.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
public record JustCategorie(long id, String name, int type, String liceName) {
public static JustCategorie from(CategoryModel m) {

View File

@ -30,4 +30,8 @@ public class SSCategorie {
public static Uni<Void> sendTreeCategory(WebSocketConnection connection, List<TreeEntity> 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}) => {
if (!cat || data.id !== cat.id)
return
setCat({
...cat,
setCat(cat_ => ({
...cat_,
name: data.name,
liceName: data.liceName,
type: data.type
})
}))
}
dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}})
return () => dispatch({type: 'removeListener', payload: categoryListener})
}, []);
}, [cat]);
return <>
<div className="card">
@ -60,18 +60,34 @@ function CategoryHeader({cat, setCatId}) {
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'}})
return () => dispatch({type: 'removeListener', payload: categoryListener})
dispatch({type: 'addListener', payload: {callback: sendAddCategory, code: 'sendAddCategory'}})
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]);
useEffect(() => {
if (cats && cats.length > 0 && !cat) {
if (cats && cats.length > 0 && !cat || (cats && !cats.find(c => c.id === cat.id))) {
setCatId(cats.sort((a, b) => a.name.localeCompare(b.name))[0].id);
} else if (cats && cats.length === 0) {
setModal({});
bthRef.current.click();
}
}, [cats]);
}, [cats, cat]);
const handleCatChange = (e) => {
const selectedCatId = e.target.value;
@ -88,7 +104,7 @@ function CategoryHeader({cat, setCatId}) {
<div className="col-auto">
<div className="input-group">
<h5 style={{margin: "auto 0.5em auto 0"}}>Edition de la catégorie</h5>
<select className="form-select" onChange={handleCatChange}>
<select className="form-select" onChange={handleCatChange} value={cat?.id || ""}>
{cats && cats.sort((a, b) => a.name.localeCompare(b.name)).map(c => (
<option key={c.id} value={c.id}>{c.name}</option>))}
{cats && <option value={-1}>Nouvelle...</option>}
@ -98,7 +114,7 @@ function CategoryHeader({cat, setCatId}) {
<div className="col" style={{margin: "auto 0", textAlign: "center"}}>
{cat &&
<div>Type: {(cat.type & 1) !== 0 ? "Poule" : ""}{cat.type === 3 ? " & " : ""}{(cat.type & 2) !== 0 ? "Tournois" : ""} |
Lice: {cat.liceName}</div>}
Zone: {cat.liceName}</div>}
</div>
<div className="col-auto">
<button className="btn btn-primary float-end" onClick={() => {
@ -128,7 +144,7 @@ function CategoryHeader({cat, setCatId}) {
function ModalContent({state, setCatId, setConfirm, confirmRef}) {
const [name, setName] = useState("")
const [lice, setLice] = useState("1")
const [lice, setLice] = useState("A")
const [poule, setPoule] = useState(true)
const [tournoi, setTournoi] = useState(false)
const [size, setSize] = useState(4)
@ -138,7 +154,7 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
useEffect(() => {
setName(state.name || "");
setLice(state.liceName || "1");
setLice(state.liceName || "A");
setPoule(((state.type || 1) & 1) !== 0);
setTournoi((state.type & 2) !== 0);
@ -164,7 +180,7 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
const regex = /^([^;]+;)*[^;]+$/;
if (regex.test(lice.trim()) === false) {
toast.error("Le format du nom des lices est invalide. Veuillez séparer les noms par des ';'.");
toast.error("Le format du nom des zones de combat est invalide. Veuillez séparer les noms par des ';'.");
return;
}
@ -270,8 +286,6 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
error: 'Erreur lors de la création de la catégorie'
}
).then(id => {
setCatId(id);
if (tournoi) {
const trees = build_tree(size, loserMatch)
console.log("Creating trees for new category:", trees);
@ -282,7 +296,9 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
success: 'Arbres créés !',
error: 'Erreur lors de la création des arbres'
}
)
).finally(() => setCatId(id))
} else {
setCatId(id);
}
})
}
@ -299,7 +315,7 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
return <form onSubmit={handleSubmit}>
<div className="modal-header">
<h1 className="modal-title fs-5" id="CategorieModalLabel">Ajouter une catégorie</h1>
<h1 className="modal-title fs-5" id="CategorieModalLabel">{state.id === undefined ? "Ajouter" : "Modifier"} une catégorie</h1>
<button type="button" className="btn-close" data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
@ -311,8 +327,8 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
</div>
<div className="mb-3">
<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="1;2" name="lice" value={lice}
<label htmlFor="liceInput1" className="form-label">Nom des zones de combat <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}
onChange={e => setLice(e.target.value)}/>
</div>
@ -373,6 +389,22 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
<div className="modal-footer">
<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>
{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>
</form>
}

View File

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

View File

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

View File

@ -47,10 +47,10 @@ export function CategoryContent({cat, catId, setCat}) {
const treeListener = ({data}) => {
if (!cat || data.length < 1 || data[0].categorie !== cat.id)
return
setCat({
...cat,
trees: data.map(d => from_sendTree(d, true))
})
setCat(cat_ => ({
...cat_,
trees: data.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true))
}))
let matches2 = [];
let combsToAdd = [];
@ -64,10 +64,16 @@ export function CategoryContent({cat, catId, setCat}) {
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)}});
if (data.c1 !== null && !groupsRef.current.some(g => g.id === data.c1?.id))
setGroups(prev => [...prev, {id: data.c1?.id, poule: data.poule}]);
if (data.c2 !== null && !groupsRef.current.some(g => g.id === data.c2?.id))
setGroups(prev => [...prev, {id: data.c2?.id, poule: data.poule}]);
setGroups(prev => {
if (data.c1 !== null && !prev.some(g => g.id === data.c1?.id))
return [...prev, {id: data.c1?.id, poule: data.poule}];
return prev;
})
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;
})
}
}
@ -103,7 +109,7 @@ export function CategoryContent({cat, catId, setCat}) {
name: data.name,
liceName: data.liceName,
type: data.type,
trees: data.trees.map(d => from_sendTree(d, true))
trees: data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true))
})
let matches2 = [];
@ -554,7 +560,7 @@ function MatchList({matches, cat, groups, reducer}) {
<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">Poule</th>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">Lice</th>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">Zone</th>
<th style={{textAlign: "center"}} scope="col"></th>
<th style={{textAlign: "center"}} scope="col">Rouge</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 <div className="row">
return <div className="row" style={{marginRight: "inherit"}}>
<h2 className="col">{name}</h2>
<div className="col-auto" style={{margin: "auto 0", padding: 0}}>Serveur: <ColoredCircle
color={isReady ? (inWait ? "#ffad32" : "#00c700") : "#e50000"}/>