Add invite request system and Gitea Actions CI/CD pipeline
Some checks failed
Build & Deploy Staging / build (release) Waiting to run
Build & Deploy Staging / deploy (release) Has been cancelled

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:
adlee-was-taken
2026-04-07 19:38:52 -04:00
parent 0c0588f920
commit ef54ac201a
16 changed files with 1003 additions and 50 deletions

View File

@@ -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>