feat: cm home page

This commit is contained in:
Thibaut Valentin 2025-12-19 14:37:24 +01:00
parent 4b969e6d69
commit 73f026210c
7 changed files with 81 additions and 27 deletions

View File

@ -66,15 +66,19 @@ public class MatchModel {
List<CardboardModel> cardboard = new ArrayList<>(); List<CardboardModel> cardboard = new ArrayList<>();
public String getC1Name() { public String getC1Name() {
if (c1_id == null) if (c1_id != null)
return c1_guest.fname + " " + c1_guest.lname;
return c1_id.fname + " " + c1_id.lname; return c1_id.fname + " " + c1_id.lname;
if (c1_guest != null)
return c1_guest.fname + " " + c1_guest.lname;
return "";
} }
public String getC2Name() { public String getC2Name() {
if (c2_id == null) if (c2_id != null)
return c2_guest.fname + " " + c2_guest.lname;
return c2_id.fname + " " + c2_id.lname; return c2_id.fname + " " + c2_id.lname;
if (c2_guest != null)
return c2_guest.fname + " " + c2_guest.lname;
return "";
} }
public int win() { public int win() {

View File

@ -56,7 +56,7 @@ public class CompetPermService {
CompletableFuture<SimpleCompet> f = new CompletableFuture<>(); CompletableFuture<SimpleCompet> f = new CompletableFuture<>();
SReqCompet.getConfig(serverCustom.clients, id, f); SReqCompet.getConfig(serverCustom.clients, id, f);
try { try {
return f.get(1500, TimeUnit.MILLISECONDS); return f.get(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) { } catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -71,7 +71,8 @@ public class CompetPermService {
.chain(competitionModels -> { .chain(competitionModels -> {
CompletableFuture<HashMap<String, String>> f = new CompletableFuture<>(); CompletableFuture<HashMap<String, String>> f = new CompletableFuture<>();
SReqCompet.getAllHaveAccess(serverCustom.clients, securityCtx.getSubject(), f); SReqCompet.getAllHaveAccess(serverCustom.clients, securityCtx.getSubject(), f);
return Uni.createFrom().future(f, Duration.ofMillis(1500)) return Uni.createFrom().future(f, Duration.ofMillis(500))
.onFailure().recoverWithItem(new HashMap<>())
.map(map_ -> { .map(map_ -> {
HashMap<Long, String> map = new HashMap<>(); HashMap<Long, String> map = new HashMap<>();
map_.forEach((key, value) -> map.put(Long.parseLong(key), value)); map_.forEach((key, value) -> map.put(Long.parseLong(key), value));

View File

@ -162,6 +162,14 @@ public class CompetitionService {
.map(pouleModels -> pouleModels.stream().map(CompetitionData::fromModel).toList()); .map(pouleModels -> pouleModels.stream().map(CompetitionData::fromModel).toList());
} }
public Uni<List<CompetitionData>> getAllSystemTable(SecurityCtx securityCtx,
CompetitionSystem system) {
return repository.list("system = ?1", system)
.chain(l -> Uni.join().all(l.stream().map(cm -> permService.hasTablePerm(securityCtx, cm)).toList())
.andCollectFailures())
.map(l -> l.stream().filter(Objects::nonNull).map(CompetitionData::fromModel).toList());
}
public Uni<CompetitionData> addOrUpdate(SecurityCtx securityCtx, CompetitionData data) { public Uni<CompetitionData> addOrUpdate(SecurityCtx securityCtx, CompetitionData data) {
if (data.getId() == null) { if (data.getId() == null) {
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult() return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
@ -496,6 +504,10 @@ public class CompetitionService {
.andCollectFailures())) .andCollectFailures()))
.call(competitionModel -> Panache.withTransaction( .call(competitionModel -> Panache.withTransaction(
() -> categoryRepository.delete("compet = ?1", competitionModel))) () -> categoryRepository.delete("compet = ?1", competitionModel)))
.call(competitionModel -> Panache.withTransaction(
() -> registerRepository.delete("competition = ?1", competitionModel)))
.call(competitionModel -> Panache.withTransaction(
() -> competitionGuestRepository.delete("competition = ?1", competitionModel)))
.chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId()))) .chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId())))
.invoke(o -> SReqCompet.rmCompet(serverCustom.clients, id)) .invoke(o -> SReqCompet.rmCompet(serverCustom.clients, id))
.call(__ -> cache.invalidate(id)); .call(__ -> cache.invalidate(id));

View File

@ -47,4 +47,12 @@ public class CompetitionAdminEndpoints {
public Uni<List<CompetitionData>> getAllSystemAdmin(@PathParam("system") CompetitionSystem system) { public Uni<List<CompetitionData>> getAllSystemAdmin(@PathParam("system") CompetitionSystem system) {
return service.getAllSystemAdmin(securityCtx, system); return service.getAllSystemAdmin(securityCtx, system);
} }
@GET
@Path("all/{system}/table")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<CompetitionData>> getAllSystemTable(@PathParam("system") CompetitionSystem system) {
return service.getAllSystemTable(securityCtx, system);
}
} }

View File

@ -43,9 +43,9 @@ function AffiliationMenu() {
} }
function CompMenu() { function CompMenu() {
const {is_authenticated} = useAuth() const {is_authenticated, userinfo} = useAuth()
if (!is_authenticated) if (!is_authenticated || !userinfo?.roles?.includes("federation_admin"))
return <></> return <></>
return <li className="nav-item dropdown"> return <li className="nav-item dropdown">

View File

@ -36,6 +36,10 @@ export function CompetitionEdit() {
}) })
} }
useEffect(() => {
refresh(`/competition/${id}?light=false`)
}, [id]);
return <> return <>
<button type="button" className="btn btn-link" onClick={() => navigate("/competition")}> <button type="button" className="btn btn-link" onClick={() => navigate("/competition")}>
&laquo; retour &laquo; retour
@ -284,17 +288,18 @@ function Content({data}) {
toast.promise( toast.promise(
apiAxios.post(`/competition`, out), apiAxios.post(`/competition`, out),
{ {
pending: "Enregistrement du club en cours", pending: "Enregistrement de la competition en cours",
success: "Club enregistrée avec succès 🎉", success: "Competition enregistrée avec succès 🎉",
error: { error: {
render({data}) { render({data}) {
return errFormater(data, "Échec de l'enregistrement du club") return errFormater(data, "Échec de l'enregistrement de la competition")
} }
}, },
} }
).then(data => { ).then(data => {
if (data.id !== undefined) console.log(data.data)
navigate("/competition/" + data.id) if (data.data.id !== undefined)
navigate("/competition/" + data.data.id)
}) })
} }
@ -315,7 +320,7 @@ function Content({data}) {
<div id="collapseOne" className="accordion-collapse collapse" data-bs-parent="#accordionExample"> <div id="collapseOne" className="accordion-collapse collapse" data-bs-parent="#accordionExample">
<div className="accordion-body"> <div className="accordion-body">
<TextField name="uuid" text="UUID" value={data.uuid} disabled={true}/> <TextField name="uuid" text="UUID" value={data.uuid} disabled={true}/>
<OptionField name="system" text="System" value={data.system} values={{SAFCA: 'SAFCA', NONE: "intranet"}} <OptionField name="system" text="System" value={data.system} values={{SAFCA: 'SAFCA', INTERNAL: "Intranet"}}
disabled={data.id !== null}/> disabled={data.id !== null}/>
{data.id !== null && {data.id !== null &&
<div className="row"> <div className="row">
@ -383,11 +388,11 @@ function Content({data}) {
<span className="input-group-text" id="startRegister">Du</span> <span className="input-group-text" id="startRegister">Du</span>
<input type="datetime-local" className="form-control" placeholder="jj/mm/aaaa" aria-label="date" <input type="datetime-local" className="form-control" placeholder="jj/mm/aaaa" aria-label="date"
name="startRegister" aria-describedby="startRegister" name="startRegister" aria-describedby="startRegister"
defaultValue={data.startRegister ? data.startRegister.split('+')[0] : ''}/> defaultValue={data.startRegister ? data.startRegister.substring(0, 16) : ''}/>
<span className="input-group-text" id="endRegister">Au</span> <span className="input-group-text" id="endRegister">Au</span>
<input type="datetime-local" className="form-control" placeholder="jj/mm/aaaa" aria-label="endRegister" <input type="datetime-local" className="form-control" placeholder="jj/mm/aaaa" aria-label="endRegister"
name="endRegister" aria-describedby="endRegister" name="endRegister" aria-describedby="endRegister"
defaultValue={data.endRegister ? data.endRegister.split('+')[0] : ''}/> defaultValue={data.endRegister ? data.endRegister.substring(0, 16) : ''}/>
</div> </div>
<div style={{display: registerMode === "HELLOASSO" ? "initial" : "none"}}> <div style={{display: registerMode === "HELLOASSO" ? "initial" : "none"}}>

View File

@ -1,11 +1,14 @@
import {Route, Routes, useNavigate, useParams} from "react-router-dom"; import {Route, Routes, useNavigate, useParams} from "react-router-dom";
import {LoadingProvider} from "../../../hooks/useLoading.jsx"; import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import {useWS, WSProvider} from "../../../hooks/useWS.jsx"; import {useWS, WSProvider} from "../../../hooks/useWS.jsx";
import {ColoredCircle} from "../../../components/ColoredCircle.jsx"; import {ColoredCircle} from "../../../components/ColoredCircle.jsx";
import {CMAdmin} from "./CMAdmin.jsx"; import {CMAdmin} from "./CMAdmin.jsx";
import {CombsProvider} from "../../../hooks/useComb.jsx"; import {CombsProvider} from "../../../hooks/useComb.jsx";
import {CMTable} from "./CMTable.jsx"; import {CMTable} from "./CMTable.jsx";
import {ThreeDots} from "react-loader-spinner";
import {AxiosError} from "../../../components/AxiosError.jsx";
import {useFetch} from "../../../hooks/useFetch.js";
const vite_url = import.meta.env.VITE_URL; const vite_url = import.meta.env.VITE_URL;
@ -22,13 +25,33 @@ export default function CompetitionManagerRoot() {
} }
function Home() { function Home() {
const nav = useNavigate(); const navigate = useNavigate();
return <div> const setLoading = useLoadingSwitcher()
<h2>Home</h2> const {data, error} = useFetch(`/competition/admin/all/INTERNAL/table`, setLoading, 1)
<button onClick={() => nav("d3dc76a6-2058-423a-b34b-6d15d7ae5848")}>Go comp</button>
return <div className="row">
{data
? <MakeCentralPanel data={data} navigate={navigate}/>
: error
? <AxiosError error={error}/>
: <Def/>
}
</div> </div>
} }
function MakeCentralPanel({data, navigate}) {
return <>
<div className="mb-4">
<h4>Compétition:</h4>
<div className="list-group">
{data.sort((a, b) => new Date(b.date.split('T')[0]) - new Date(a.date.split('T')[0])).map((o) => (
<li className="list-group-item list-group-item-action" key={o.id}
onClick={__ => navigate(o.uuid)}>{o.name}</li>))}
</div>
</div>
</>
}
function HomeComp() { function HomeComp() {
let {compUuid} = useParams(); let {compUuid} = useParams();
const [perm, setPerm] = useState("") const [perm, setPerm] = useState("")
@ -98,11 +121,12 @@ function Home2({perm}) {
</div> </div>
} }
function Test2() { function Def() {
let {compUuid} = useParams(); return <div className="list-group">
const nav = useNavigate(); <li className="list-group-item"><ThreeDots/></li>
return <div> <li className="list-group-item"><ThreeDots/></li>
<h2>Product ID: {compUuid}</h2> <li className="list-group-item"><ThreeDots/></li>
<button onClick={() => nav(-1)}>Go Back</button> <li className="list-group-item"><ThreeDots/></li>
<li className="list-group-item"><ThreeDots/></li>
</div> </div>
} }