Add invite request system and Gitea Actions CI/CD pipeline
Invite request feature: - Public form to request an invite when INVITE_REQUEST_ENABLED=true - Stores requests in new invite_requests DB table - Emails admins on new request, emails requester on approve/deny - Admin panel tab to review, approve, and deny requests - Approval auto-creates invite code and sends signup link CI/CD pipeline: - Build & push Docker image to Gitea registry on release - Auto-deploy to staging with health check - Manual workflow_dispatch for production deploys Also includes client layout/sizing improvements for card grid and opponent spacing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -322,38 +322,38 @@
|
||||
<div class="game-table">
|
||||
<div id="opponents-row" class="opponents-row"></div>
|
||||
|
||||
<div class="player-row">
|
||||
<div class="table-center">
|
||||
<div class="deck-area">
|
||||
<!-- Held card slot (left of deck) -->
|
||||
<div id="held-card-slot" class="held-card-slot hidden">
|
||||
<div id="held-card-display" class="card card-front">
|
||||
<span id="held-card-content"></span>
|
||||
</div>
|
||||
<span class="held-label">Holding</span>
|
||||
<div class="table-center">
|
||||
<div class="deck-area">
|
||||
<!-- Held card slot (left of deck) -->
|
||||
<div id="held-card-slot" class="held-card-slot hidden">
|
||||
<div id="held-card-display" class="card card-front">
|
||||
<span id="held-card-content"></span>
|
||||
</div>
|
||||
<div class="pile-wrapper">
|
||||
<span class="pile-label">DRAW</span>
|
||||
<div id="deck" class="card card-back"></div>
|
||||
</div>
|
||||
<div class="pile-wrapper">
|
||||
<span class="pile-label">DISCARD</span>
|
||||
<div class="discard-stack">
|
||||
<div id="discard" class="card">
|
||||
<span id="discard-content"></span>
|
||||
</div>
|
||||
<!-- Floating held card (appears larger over discard when holding) -->
|
||||
<div id="held-card-floating" class="card card-front held-card-floating hidden">
|
||||
<span id="held-card-floating-content"></span>
|
||||
</div>
|
||||
<button id="discard-btn" class="btn btn-small hidden">Discard</button>
|
||||
<button id="skip-flip-btn" class="btn btn-small btn-secondary hidden">Skip Flip</button>
|
||||
<button id="knock-early-btn" class="btn btn-small btn-danger hidden">Knock!</button>
|
||||
<span class="held-label">Holding</span>
|
||||
</div>
|
||||
<div class="pile-wrapper">
|
||||
<span class="pile-label">DRAW</span>
|
||||
<div id="deck" class="card card-back"></div>
|
||||
</div>
|
||||
<div class="pile-wrapper">
|
||||
<span class="pile-label">DISCARD</span>
|
||||
<div class="discard-stack">
|
||||
<div id="discard" class="card">
|
||||
<span id="discard-content"></span>
|
||||
</div>
|
||||
<!-- Floating held card (appears larger over discard when holding) -->
|
||||
<div id="held-card-floating" class="card card-front held-card-floating hidden">
|
||||
<span id="held-card-floating-content"></span>
|
||||
</div>
|
||||
<button id="discard-btn" class="btn btn-small hidden">Discard</button>
|
||||
<button id="skip-flip-btn" class="btn btn-small btn-secondary hidden">Skip Flip</button>
|
||||
<button id="knock-early-btn" class="btn btn-small btn-danger hidden">Knock!</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="player-row">
|
||||
<div class="player-section">
|
||||
<div class="player-area">
|
||||
<h4 id="player-header"><span class="player-name">You</span><span id="your-score" class="player-showing">0</span></h4>
|
||||
@@ -427,6 +427,26 @@
|
||||
|
||||
<!-- Drawer backdrop for mobile -->
|
||||
<div id="drawer-backdrop" class="drawer-backdrop"></div>
|
||||
|
||||
<!-- Desktop scorecard button + overlay -->
|
||||
<button id="desktop-scorecard-btn">Scorecard</button>
|
||||
<div id="desktop-scorecard-overlay" class="side-panel desktop-scorecard-overlay">
|
||||
<h4>Current Standings</h4>
|
||||
<div id="desktop-standings-list" class="standings-list"></div>
|
||||
<h4>Scores</h4>
|
||||
<table id="desktop-score-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Player</th>
|
||||
<th>Hole</th>
|
||||
<th>Tot</th>
|
||||
<th>W</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="desktop-scorecard-backdrop" class="desktop-scorecard-backdrop"></div>
|
||||
</div>
|
||||
|
||||
<!-- Rules Screen -->
|
||||
@@ -911,6 +931,28 @@ TOTAL: 0 + 8 + 16 = 24 points</pre>
|
||||
</form>
|
||||
<p class="auth-switch">Already have an account? <a href="#" id="show-login">Login</a></p>
|
||||
</div>
|
||||
|
||||
<!-- Request Invite Form -->
|
||||
<div id="request-invite-container" class="hidden">
|
||||
<h3>Request an Invite</h3>
|
||||
<p class="auth-hint">Registration is invite-only. Request access and we'll get back to you.</p>
|
||||
<form id="request-invite-form">
|
||||
<div class="form-group">
|
||||
<input type="text" id="request-invite-name" placeholder="Your name" required maxlength="100">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="email" id="request-invite-email" placeholder="Email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<textarea id="request-invite-message" placeholder="Why do you want to join? (optional)" rows="3" maxlength="500"></textarea>
|
||||
</div>
|
||||
<p id="request-invite-error" class="error"></p>
|
||||
<p id="request-invite-success" class="success"></p>
|
||||
<button type="submit" class="btn btn-primary btn-full">Request Invite</button>
|
||||
</form>
|
||||
<p class="auth-switch">Already have an invite? <a href="#" id="request-back-signup">Sign up</a></p>
|
||||
<p class="auth-switch">Already have an account? <a href="#" id="request-back-login">Login</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user