Add a 'final' filter to registration page; coherence check on server for final and skipping

This commit is contained in:
Claude Brisson
2024-02-28 19:39:03 +01:00
parent e692dbeabb
commit 024a8e8a5e
7 changed files with 58 additions and 13 deletions

View File

@@ -34,7 +34,18 @@ object PlayerHandler: PairgothApiHandler {
val player = tournament.players[id] ?: badRequest("invalid player id") val player = tournament.players[id] ?: badRequest("invalid player id")
val payload = getObjectPayload(request) val payload = getObjectPayload(request)
val updated = Player.fromJson(payload, player) val updated = Player.fromJson(payload, player)
tournament.players[updated.id] = updated // check coherence
if (player.final && !updated.final && tournament.pairedPlayers().contains(updated.id)) {
badRequest("player is playing")
}
val leavingRounds = updated.skip.toSet().minus(player.skip.toSet())
leavingRounds.forEach { round ->
val playing = tournament.games(round).values.flatMap { listOf(it.black, it.white) }
if (playing.contains(id)) {
throw badRequest("player is playing in round #$round")
}
}
tournament.players[id] = updated
tournament.dispatchEvent(PlayerUpdated, player.toJson()) tournament.dispatchEvent(PlayerUpdated, player.toJson())
return Json.Object("success" to true) return Json.Object("success" to true)
} }
@@ -42,6 +53,11 @@ object PlayerHandler: PairgothApiHandler {
override fun delete(request: HttpServletRequest, response: HttpServletResponse): Json { override fun delete(request: HttpServletRequest, response: HttpServletResponse): Json {
val tournament = getTournament(request) val tournament = getTournament(request)
val id = getSubSelector(request)?.toIntOrNull() ?: badRequest("missing or invalid player selector") val id = getSubSelector(request)?.toIntOrNull() ?: badRequest("missing or invalid player selector")
// check coherence
val player = tournament.players[id] ?: badRequest("invalid player id")
if (player.final && tournament.pairedPlayers().contains(id)) {
badRequest("player is playing")
}
tournament.players.remove(id) ?: badRequest("invalid player id") tournament.players.remove(id) ?: badRequest("invalid player id")
tournament.dispatchEvent(PlayerDeleted, Json.Object("id" to id)) tournament.dispatchEvent(PlayerDeleted, Json.Object("id" to id))
return Json.Object("success" to true) return Json.Object("success" to true)

View File

@@ -114,6 +114,8 @@ sealed class Tournament <P: Pairable>(
} }
return changed return changed
} }
fun pairedPlayers() = games.flatMap { it.values }.flatMap { listOf(it.black, it.white) }.toSet()
} }
// standard tournament of individuals // standard tournament of individuals

View File

@@ -434,9 +434,14 @@
} }
.toggle { .toggle {
display: inline-block; padding-top: 0.2em;
display: inline-flex;
flex-flow: column nowrap;
justify-content: space-evenly;
align-items: center;
cursor: pointer; cursor: pointer;
text-align: center; text-align: center;
vertical-align: middle;
input { input {
display: none !important; display: none !important;
} }

View File

@@ -63,6 +63,9 @@
#players-list { #players-list {
max-width: 95vw; max-width: 95vw;
#players tr.filtered {
display: none;
}
} }
#player { #player {

View File

@@ -199,7 +199,7 @@ onLoad(() => {
switch (e.key) { switch (e.key) {
case 'Escape': { case 'Escape': {
if (tab === '#registration') { if (tab === '#registration') {
if ($('#player').hasClass('shown') && $('#needle')[0].value) { if ($('#player').hasClass('shown') && searchResultShown()) {
$('#needle')[0].value = ''; $('#needle')[0].value = '';
initSearch(); initSearch();
} else { } else {

View File

@@ -260,7 +260,7 @@ onLoad(() => {
} }
} }
} }
$('.toggle').on('click', e => { $('#search-form .toggle').on('click', e => {
let chk = e.target.closest('.toggle'); let chk = e.target.closest('.toggle');
let checkbox = chk.find('input')[0]; let checkbox = chk.find('input')[0];
checkbox.checked = !checkbox.checked; checkbox.checked = !checkbox.checked;
@@ -269,6 +269,16 @@ onLoad(() => {
store(id, value); store(id, value);
initSearch(); initSearch();
}); });
$('#list-header .toggle').on('click', e => {
let chk = e.target.closest('.toggle');
let checkbox = chk.find('input')[0];
checkbox.checked = !checkbox.checked;
if (checkbox.checked) {
$('td.reg-status:not(.final)').forEach(node => node.parentNode.addClass('filtered'));
} else {
$('td.reg-status:not(.final)').forEach(node => node.parentNode.removeClass('filtered'));
}
});
document.on('click', e => { document.on('click', e => {
let resultLine = e.target.closest('.result-line'); let resultLine = e.target.closest('.result-line');
if (resultLine) { if (resultLine) {

View File

@@ -3,10 +3,19 @@
<div class="tab-content" id="registration-tab"> <div class="tab-content" id="registration-tab">
<div id="reg-view"> <div id="reg-view">
<div id="list-header"> <div id="list-header">
<div>
<div id="filter-box" class="ui icon input"> <div id="filter-box" class="ui icon input">
<input type="text" id="filter" placeholder="Filter..."/> <input type="text" id="filter" placeholder="Filter..."/>
<i class="circular times link icon"></i> <i class="circular times link icon"></i>
</div> </div>
<div class="toggle">
<input id="filter-final" name="filter-final" type="checkbox"/>
<div class="checkbox">
<div class="circle"></div>
</div>
<label>Final</label>
</div>
</div>
<div> <div>
$parts.size() participants, $utils.countFinals($parts) confirmed. $parts.size() participants, $utils.countFinals($parts) confirmed.
</div> </div>