327 lines
17 KiB
HTML
327 lines
17 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Leagues · myboker.org{% endblock %}
|
|
{% block page_class %}page--session page--leagues{% endblock %}
|
|
{% block content %}
|
|
|
|
<div class="li-page-header">
|
|
<div class="li-page-header__left">
|
|
<p class="eyebrow" style="margin:0 0 6px;">Your account</p>
|
|
<h1 class="li-page-header__title">Your leagues</h1>
|
|
</div>
|
|
<div class="li-page-header__stats">
|
|
<div class="li-header-stat">
|
|
<span class="li-header-stat__label">Leagues</span>
|
|
<strong class="li-header-stat__val">{{ page_summary.league_count }}</strong>
|
|
</div>
|
|
<div class="li-header-stat">
|
|
<span class="li-header-stat__label">Live now</span>
|
|
<strong class="li-header-stat__val {{ 'num-pos' if page_summary.live_count > 0 else '' }}">{{ page_summary.live_count }}</strong>
|
|
</div>
|
|
<div class="li-header-stat">
|
|
<span class="li-header-stat__label">Sessions</span>
|
|
<strong class="li-header-stat__val">{{ page_summary.sessions_count }}</strong>
|
|
</div>
|
|
<div class="li-header-stat">
|
|
<span class="li-header-stat__label">Total paid out</span>
|
|
<strong class="li-header-stat__val">{{ page_summary.total_paid_out_cents | money }}</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="leagues-split">
|
|
|
|
<aside class="leagues-split__form">
|
|
<form class="li-new-form" method="post" action="{{ url_for('leagues.index') }}" id="newLeagueForm">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<p class="eyebrow" style="margin:0 0 4px;">League setup</p>
|
|
<h2 class="li-new-form__title">New league</h2>
|
|
|
|
<div class="li-new-form__field">
|
|
<label class="li-new-form__label" for="lf-name">Name</label>
|
|
<input class="li-new-form__input" id="lf-name" type="text" name="name" value="{{ form.name or '' }}" placeholder="Friday Poker" required>
|
|
</div>
|
|
|
|
<div class="li-new-form__field">
|
|
<label class="li-new-form__label" for="lf-desc">Description <span class="form-opt-hint">optional</span></label>
|
|
<textarea class="li-new-form__input li-new-form__textarea" id="lf-desc" name="description" placeholder="Private home league">{{ form.description or '' }}</textarea>
|
|
</div>
|
|
|
|
<div class="li-new-form__field">
|
|
<span class="li-new-form__label">Visibility</span>
|
|
<div class="li-visibility-toggle">
|
|
<button type="button" class="li-vis-btn {% if (form.visibility or 'private') == 'private' %}is-on{% endif %}" data-vis="private">Private</button>
|
|
<button type="button" class="li-vis-btn {% if (form.visibility or 'private') == 'public' %}is-on{% endif %}" data-vis="public">Public</button>
|
|
</div>
|
|
<input type="hidden" name="visibility" id="lf-visibility" value="{{ form.visibility or 'private' }}">
|
|
<p class="li-new-form__hint" id="visHint">
|
|
{% if (form.visibility or 'private') == 'public' %}Visible in Explore. Anyone can view.{% else %}Invite-only. Nothing appears in Explore.{% endif %}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="li-new-form__field">
|
|
<span class="li-new-form__label">Default buy-in</span>
|
|
<div class="li-preset-row">
|
|
<button type="button" class="li-preset-btn" data-field="buyin" data-val="5">$5</button>
|
|
<button type="button" class="li-preset-btn" data-field="buyin" data-val="10">$10</button>
|
|
<button type="button" class="li-preset-btn" data-field="buyin" data-val="20">$20</button>
|
|
<button type="button" class="li-preset-btn" data-field="buyin" data-val="50">$50</button>
|
|
<input class="li-preset-custom" type="number" name="default_buyin_dollars" id="lf-buyin" min="0" step="0.01" placeholder="Custom" value="{{ form.default_buyin_dollars or '' }}">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="li-new-form__field">
|
|
<span class="li-new-form__label">Default rebuy</span>
|
|
<div class="li-preset-row">
|
|
<button type="button" class="li-preset-btn" data-field="rebuy" data-val="5">$5</button>
|
|
<button type="button" class="li-preset-btn" data-field="rebuy" data-val="10">$10</button>
|
|
<button type="button" class="li-preset-btn" data-field="rebuy" data-val="20">$20</button>
|
|
<button type="button" class="li-preset-btn" data-field="rebuy" data-val="50">$50</button>
|
|
<input class="li-preset-custom" type="number" name="default_rebuy_dollars" id="lf-rebuy" min="0" step="0.01" placeholder="Custom" value="{{ form.default_rebuy_dollars or '' }}">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="li-new-form__two-col">
|
|
<div class="li-new-form__field">
|
|
<label class="li-new-form__label" for="lf-min-sessions">Sessions to rank <span class="form-opt-hint">optional</span></label>
|
|
<input class="li-new-form__input" id="lf-min-sessions" type="number" name="eligible_min_sessions" min="1" step="1" placeholder="3 (default)" value="{{ form.eligible_min_sessions or '' }}">
|
|
<p class="li-new-form__hint">Min sessions to appear on leaderboard.</p>
|
|
</div>
|
|
<div class="li-new-form__field">
|
|
<label class="li-new-form__label" for="lf-break-even">Break-even threshold <span class="form-opt-hint">optional</span></label>
|
|
<div class="li-input-prefix-wrap">
|
|
<span class="li-input-prefix">$</span>
|
|
<input class="li-new-form__input li-input-has-prefix" id="lf-break-even" type="number" name="break_even_dollars" min="0" step="0.01" placeholder="1.00 (default)" value="{{ form.break_even_dollars or '' }}">
|
|
</div>
|
|
<p class="li-new-form__hint">Net within this = break even.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="li-new-form__submit" type="submit">Create league</button>
|
|
</form>
|
|
</aside>
|
|
|
|
<section class="leagues-split__list">
|
|
<div class="li-toolbar">
|
|
<input class="li-search" type="search" id="liSearch" placeholder="Search leagues…" autocomplete="off">
|
|
<div class="li-filter-pills">
|
|
<button class="li-filter-btn is-on" data-filter="all">All</button>
|
|
<button class="li-filter-btn" data-filter="live">Live</button>
|
|
<button class="li-filter-btn" data-filter="owner">Owner</button>
|
|
<button class="li-filter-btn" data-filter="public">Public</button>
|
|
</div>
|
|
<div class="li-sort-tabs">
|
|
<span class="li-sort-label">Sort</span>
|
|
<button class="li-sort-btn is-on" data-sort="activity">Activity</button>
|
|
<button class="li-sort-btn" data-sort="money">Money</button>
|
|
<button class="li-sort-btn" data-sort="name">Name</button>
|
|
</div>
|
|
</div>
|
|
|
|
{% if leagues %}
|
|
<div class="li-board" id="liBoard">
|
|
{% for item in leagues %}
|
|
{% set league = item.league %}
|
|
{% set membership = item.membership %}
|
|
<article class="li-card{% if item.live_session %} li-card--live{% endif %}"
|
|
data-name="{{ league.name | lower }}"
|
|
data-live="{{ '1' if item.live_session else '0' }}"
|
|
data-role="{{ membership.role }}"
|
|
data-visibility="{{ league.visibility }}"
|
|
data-sessions="{{ item.sessions_count }}"
|
|
data-paid="{{ item.cash_paid_out_cents }}"
|
|
data-name-raw="{{ league.name }}">
|
|
|
|
<div class="li-card__top">
|
|
<div class="li-card__name-row">
|
|
<a class="li-card__name" href="{{ url_for('leagues.dashboard', league_ref=league.url_ref) }}">{{ league.name }}</a>
|
|
{% if item.live_session %}
|
|
<span class="li-pill li-pill--live">Live</span>
|
|
{% else %}
|
|
<span class="li-pill li-pill--idle">Idle</span>
|
|
{% endif %}
|
|
{% if item.latest_session %}<span class="li-card__season">{{ session_label(item.latest_session) }}</span>{% endif %}
|
|
</div>
|
|
<div style="display:flex;align-items:center;gap:8px;flex-shrink:0;">
|
|
<span class="li-badge">{{ membership.role }}</span>
|
|
<span class="li-badge">{{ league.visibility }}</span>
|
|
<a class="li-open-btn" href="{{ url_for('leagues.dashboard', league_ref=league.url_ref) }}">Open</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="li-card__body">
|
|
<div class="li-card__stats-col">
|
|
<span class="li-stat-label">Players</span>
|
|
<strong class="li-stat-num">{{ item.players_count }}</strong>
|
|
</div>
|
|
<div class="li-card__stats-col">
|
|
<span class="li-stat-label">Sessions</span>
|
|
<strong class="li-stat-num">{{ item.sessions_count }}</strong>
|
|
</div>
|
|
<div class="li-card__stats-col">
|
|
<span class="li-stat-label">Paid out</span>
|
|
<strong class="li-stat-num">{{ item.cash_paid_out_cents | money }}</strong>
|
|
</div>
|
|
<div class="li-card__stats-col">
|
|
<span class="li-stat-label">Open</span>
|
|
<strong class="li-stat-num {{ 'num-neg' if item.open_items_cents > 0 else 'num-muted' }}">{{ item.open_items_cents | money }}</strong>
|
|
</div>
|
|
{% if item.top_players %}
|
|
<div class="li-card__leaders-col">
|
|
<span class="li-stat-label">Leaders</span>
|
|
<div class="li-leaders-list">
|
|
{% for player in item.top_players %}
|
|
<div class="li-leader-row">
|
|
<span class="li-leader-rank">#{{ loop.index }}</span>
|
|
<span class="li-leader-name">{{ player.player_name }}</span>
|
|
<strong class="li-leader-net {{ 'num-pos' if player.total_net_cents > 0 else 'num-neg' if player.total_net_cents < 0 else 'num-muted' }}">
|
|
{{ '+' if player.total_net_cents > 0 else '' }}{{ player.total_net_cents | money }}
|
|
</strong>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="li-card__footer">
|
|
<span class="li-card__footer-meta">
|
|
{% if item.live_session %}Live now · table open{% else %}{% if item.latest_session %}Last played {{ session_label(item.latest_session) }}{% else %}No sessions yet{% endif %}{% endif %}
|
|
</span>
|
|
<div class="li-card__footer-links">
|
|
<a class="li-footer-link" href="{{ url_for('leagues.sessions', league_ref=league.url_ref) }}">Sessions</a>
|
|
<a class="li-footer-link" href="{{ url_for('leagues.leaderboard', league_ref=league.url_ref) }}">Leaderboard</a>
|
|
{% if membership.role in ('owner', 'manager') %}
|
|
<a class="li-footer-link" href="{{ url_for('leagues.ledger', league_ref=league.url_ref) }}">Ledger</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
</article>
|
|
{% endfor %}
|
|
</div>
|
|
<p class="li-empty-search" id="liEmptySearch" style="display:none;">No leagues match your search or filter.</p>
|
|
{% else %}
|
|
<div class="panel empty-panel">
|
|
<h2 class="panel__title">No leagues yet</h2>
|
|
<p class="muted-text">Fill in the form to create your first league.</p>
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
(function () {
|
|
// --- Visibility toggle ---
|
|
var visInput = document.getElementById('lf-visibility');
|
|
var visHint = document.getElementById('visHint');
|
|
document.querySelectorAll('.li-vis-btn').forEach(function (btn) {
|
|
btn.addEventListener('click', function () {
|
|
document.querySelectorAll('.li-vis-btn').forEach(function (b) { b.classList.remove('is-on'); });
|
|
this.classList.add('is-on');
|
|
visInput.value = this.dataset.vis;
|
|
visHint.textContent = this.dataset.vis === 'public'
|
|
? 'Visible in Explore. Anyone can view.'
|
|
: 'Invite-only. Nothing appears in Explore.';
|
|
});
|
|
});
|
|
|
|
// --- Preset buy-in / rebuy buttons ---
|
|
document.querySelectorAll('.li-preset-btn').forEach(function (btn) {
|
|
btn.addEventListener('click', function () {
|
|
var field = this.dataset.field;
|
|
var val = this.dataset.val;
|
|
var input = document.getElementById('lf-' + field);
|
|
// toggle: clicking active preset clears it
|
|
var active = this.classList.contains('is-on');
|
|
document.querySelectorAll('.li-preset-btn[data-field="' + field + '"]').forEach(function (b) {
|
|
b.classList.remove('is-on');
|
|
});
|
|
if (!active) {
|
|
this.classList.add('is-on');
|
|
input.value = val;
|
|
} else {
|
|
input.value = '';
|
|
}
|
|
});
|
|
});
|
|
// Sync custom input → deselect preset if user types a custom value
|
|
['buyin', 'rebuy'].forEach(function (field) {
|
|
var input = document.getElementById('lf-' + field);
|
|
if (!input) return;
|
|
input.addEventListener('input', function () {
|
|
var v = this.value.trim();
|
|
document.querySelectorAll('.li-preset-btn[data-field="' + field + '"]').forEach(function (b) {
|
|
b.classList.toggle('is-on', b.dataset.val === v);
|
|
});
|
|
});
|
|
// Highlight preset if form was repopulated with an error
|
|
var cur = input.value.trim();
|
|
if (cur) {
|
|
document.querySelectorAll('.li-preset-btn[data-field="' + field + '"]').forEach(function (b) {
|
|
if (b.dataset.val === cur) b.classList.add('is-on');
|
|
});
|
|
}
|
|
});
|
|
|
|
// --- Search + filter + sort ---
|
|
var cards = Array.from(document.querySelectorAll('.li-card'));
|
|
var board = document.getElementById('liBoard');
|
|
var empty = document.getElementById('liEmptySearch');
|
|
var search = document.getElementById('liSearch');
|
|
var activeFilter = 'all';
|
|
var activeSort = 'activity';
|
|
|
|
function applyAll() {
|
|
var q = search ? search.value.trim().toLowerCase() : '';
|
|
var visible = [];
|
|
cards.forEach(function (c) {
|
|
var nameMatch = !q || c.dataset.name.includes(q);
|
|
var filterMatch = true;
|
|
if (activeFilter === 'live') filterMatch = c.dataset.live === '1';
|
|
if (activeFilter === 'owner') filterMatch = c.dataset.role === 'owner';
|
|
if (activeFilter === 'public') filterMatch = c.dataset.visibility === 'public';
|
|
var show = nameMatch && filterMatch;
|
|
c.style.display = show ? '' : 'none';
|
|
if (show) visible.push(c);
|
|
});
|
|
// Sort
|
|
visible.sort(function (a, b) {
|
|
if (activeSort === 'name') return a.dataset.nameRaw < b.dataset.nameRaw ? -1 : 1;
|
|
if (activeSort === 'money') return Number(b.dataset.paid) - Number(a.dataset.paid);
|
|
// activity: live first, then by sessions desc
|
|
var la = a.dataset.live === '1' ? 1 : 0;
|
|
var lb = b.dataset.live === '1' ? 1 : 0;
|
|
if (lb !== la) return lb - la;
|
|
return Number(b.dataset.sessions) - Number(a.dataset.sessions);
|
|
});
|
|
visible.forEach(function (c) { board && board.appendChild(c); });
|
|
if (empty) empty.style.display = (visible.length === 0) ? 'block' : 'none';
|
|
}
|
|
|
|
if (search) search.addEventListener('input', applyAll);
|
|
|
|
document.querySelectorAll('.li-filter-btn').forEach(function (btn) {
|
|
btn.addEventListener('click', function () {
|
|
document.querySelectorAll('.li-filter-btn').forEach(function (b) { b.classList.remove('is-on'); });
|
|
this.classList.add('is-on');
|
|
activeFilter = this.dataset.filter;
|
|
applyAll();
|
|
});
|
|
});
|
|
|
|
document.querySelectorAll('.li-sort-btn').forEach(function (btn) {
|
|
btn.addEventListener('click', function () {
|
|
document.querySelectorAll('.li-sort-btn').forEach(function (b) { b.classList.remove('is-on'); });
|
|
this.classList.add('is-on');
|
|
activeSort = this.dataset.sort;
|
|
applyAll();
|
|
});
|
|
});
|
|
|
|
applyAll();
|
|
})();
|
|
</script>
|
|
|
|
{% endblock %}
|