One deck form per session; rename leagues; e2e test script

- A player's deck for a night is edited in a single form and written to
  their match rows and their standings row, so the two can't disagree.
  Matches, standings and hand imports all fill blank decks from the decks
  already entered, whatever order the files are imported in. The imported
  standings table shows decks read-only.
- Admins can rename a league.
- test/e2e.sh runs end-to-end checks against the local podman stack
  (fixtures by default, or real exports: test/e2e.sh sampledata).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
derekandClaude Opus 5.5 committed 2026-10-28 17:20:45 +00:00
1 parent a99997b96d
commit 022d161934
6 files changed
+147 -36

No files matched your search

+1
View File
@@ -1,5 +1,6 @@
<%- include('head') %>
<h1><%= lg.name %></h1>
<% if (admin) { %><form method="post" action="<%= base %>/admin/league/<%= lg.id %>/rename"><input name="name" value="<%= lg.name %>" required> <button>Rename league</button></form><% } %>
<h2>Leaderboard <small>(best <%= lg.best_n %> sessions)</small></h2>
<table><tr><th>#</th><th>Player</th><th>Counted</th><th>All sessions</th><th>Played</th><th>W-D-L</th><th>OMW%</th><th>GW%</th><th>OGW%</th></tr>
<% const pct = (r, k) => r.tb ? (r.tb[k] * 100).toFixed(2) : '–' %>
+5 -9
View File
@@ -9,17 +9,13 @@
<td><b><%= m.p1_wins %>–<%= m.p2_wins %><%= m.draws ? '–' + m.draws : '' %></b></td></tr>
<% }) %></table><% }) %>
<% if (!rounds.length && !results.length) { %><p>No results yet.</p><% } %>
<% if (admin && matchPlayers.length) { %><h2>Decks played</h2>
<form method="post" action="<%= base %>/admin/session/<%= gs.id %>/match-decks"><table><tr><th>Player</th><th>Deck</th></tr>
<% matchPlayers.forEach(p => { %><tr><td><%= p.name %></td><td><input name="mdeck_<%= p.id %>" value="<%= p.deck || '' %>" list="decks"></td></tr><% }) %></table>
<% if (admin && players.length) { %><h2>Decks played</h2>
<form method="post" action="<%= base %>/admin/session/<%= gs.id %>/decks"><table><tr><th>Player</th><th>Deck</th></tr>
<% players.forEach(p => { %><tr><td><%= p.name %></td><td><input name="deck_<%= p.id %>" value="<%= p.deck || '' %>" list="decks"></td></tr><% }) %></table>
<button>Save decks</button></form><% } %>
<% if (results.length) { %><h2>Standings <small>(imported)</small></h2>
<form method="post" action="<%= base %>/admin/session/<%= gs.id %>/decks">
<table><tr><th>Player</th><th>Points</th><th>W-D-L</th><th>Deck</th></tr>
<% results.forEach(r => { %><tr><td><%= r.name %></td><td><b><%= r.points %></b></td><td><%= r.rec %></td>
<td><% if (admin) { %><input name="deck_<%= r.id %>" value="<%= r.deck || '' %>" list="decks"><% } else { %><%= r.deck || '' %><% } %></td></tr><% }) %></table>
<% if (admin) { %><button>Save decks</button><% } %>
</form>
<% results.forEach(r => { %><tr><td><%= r.name %></td><td><b><%= r.points %></b></td><td><%= r.rec %></td><td><%= r.deck || '' %></td></tr><% }) %></table>
<p><small>If this session also has match rows, those are used for scoring instead.</small></p><% } %>
<% if (admin) { %>
<h2>Import Melee export</h2>
@@ -31,7 +27,7 @@ If anything in the batch is invalid, nothing is imported. If a session has match
<textarea name="data" rows="5" style="width:100%;font-family:monospace" placeholder="...or paste here"></textarea><br><button>Import</button></form>
<h2>Import matches by hand</h2>
<p>One match per line: <code>round, player1, deck1, player2, deck2, p1 wins, p2 wins, draws</code>. Leave player 2 blank for a bye.
Comma- or tab-separated. Saving <b>replaces</b> this session's matches.</p>
Comma- or tab-separated. Saving <b>replaces</b> this session's matches; a blank deck keeps the one already entered.</p>
<form method="post" action="<%= base %>/admin/session/<%= gs.id %>/import">
<textarea name="data" rows="12" style="width:100%;font-family:monospace"><%= csvtext %></textarea><br><button>Save matches</button></form>
<form method="post" action="<%= base %>/admin/session/<%= gs.id %>/delete" data-confirm="Delete this session and its results?"><button>Delete session</button></form>