visual redesign: landing, dashboard overview, session calendar (2.3.1)

- Redesign landing page hero with split layout, features section, and in-app session/result demos
- Add fixed sidebar scroll isolation (app-shell overflow lock pattern)
- Redesign leagues index cards with initial badge, podium leader chips, and action links
- Redesign league dashboard: color-coded nav tiles, cash/stats overview strip, session history heatmap calendar
- Dashboard cash stats use real cash only (excludes rollovers and carry-ins)
- Session history calendar: JS-driven full-year grid, year navigation, hover tooltips, full-width responsive layout
- Bump leaderboard tile color from gold to blue (distinct from ledger orange)
- Bump version to 2.3.1
This commit is contained in:
SowinskiBraeden committed 2026-06-26 21:24:00 -07:00
1 parent 56c9a1084c
commit a5cb488b91
22 files changed
+2975 -597

No files matched your search

+100 -105
View File
@@ -2,133 +2,128 @@
{% block title %}Leagues · myboker.org{% endblock %}
{% block content %}
<div class="hero" style="padding-bottom:12px;">
<p class="hero__kicker">Leagues</p>
<h1 class="hero__title">Your poker leagues</h1>
<p class="hero__sub">Owner and manager workspace.</p>
</div>
<div class="toolbar">
<div class="toolbar__left">
<a class="btn btn--primary" href="{{ url_for('leagues.new') }}">New league</a>
<div class="page-header">
<div>
<h1 class="page-header__title">Your leagues</h1>
{% if leagues %}
<p class="page-header__sub">
{{ page_summary.league_count }} league{{ 's' if page_summary.league_count != 1 else '' }}
{% if page_summary.live_count > 0 %}&nbsp;·&nbsp;<span style="color:var(--pos);">{{ page_summary.live_count }} live</span>{% endif %}
&nbsp;·&nbsp;{{ page_summary.sessions_count }} session{{ 's' if page_summary.sessions_count != 1 else '' }}
</p>
{% endif %}
</div>
<div class="page-header__actions">
<a class="btn btn--primary btn--sm" href="{{ url_for('leagues.new') }}">New league</a>
</div>
</div>
{% if leagues %}
<div class="league-hub-summary">
<div class="stat stat--rail" style="--rail:var(--accent);">
<span class="kicker">Leagues</span>
<div class="stat__val" style="color:var(--num)">{{ page_summary.league_count }}</div>
</div>
<div class="stat stat--rail" style="--rail:var(--pos);">
<span class="kicker">Live</span>
<div class="stat__val" style="color:var(--num)">{{ page_summary.live_count }}</div>
</div>
<div class="stat stat--rail" style="--rail:var(--rolled);">
<span class="kicker">Sessions</span>
<div class="stat__val" style="color:var(--num)">{{ page_summary.sessions_count }}</div>
</div>
<div class="stat stat--rail" style="--rail:var(--warn);">
<span class="kicker">Open items</span>
<div class="stat__val {{ 'num-neg' if page_summary.open_items_cents > 0 else 'num-muted' }}">{{ page_summary.open_items_cents | money }}</div>
</div>
</div>
<div class="league-board">
<div class="li-board">
{% for item in leagues %}
{% set league = item.league %}
{% set membership = item.membership %}
<article class="panel league-row">
<div class="league-row__identity">
<a class="league-row__title" href="{{ url_for('leagues.dashboard', league_ref=league.url_ref) }}">
<div>
<span class="kicker">{{ membership.role }}</span>
<h2>{{ league.name }}</h2>
</div>
{% if item.live_session %}
<span class="live-badge">{{ session_label(item.live_session) }}</span>
{% else %}
<span class="status-pill status-pill--closed">idle</span>
{% endif %}
</a>
<div class="league-row__chips">
<span class="db-chip {% if league.visibility == 'public' %}db-chip--public{% endif %}">
{% if league.visibility == 'public' %}
<svg width="11" height="11" viewBox="0 0 20 20" fill="none" aria-hidden="true">
<circle cx="10" cy="10" r="8" stroke="currentColor" stroke-width="1.6"/>
<path d="M10 2c-2.5 3-2.5 13 0 16" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" fill="none"/>
<path d="M10 2c2.5 3 2.5 13 0 16" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" fill="none"/>
<path d="M2.5 10h15" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
</svg>
public
{% else %}
<svg width="10" height="12" viewBox="0 0 10 12" fill="none" aria-hidden="true">
<rect x="1" y="5" width="8" height="6.5" rx="1.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M3 5V3.5A2 2 0 0 1 7 3.5V5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
private
<article class="li-card{% if item.live_session %} li-card--live{% endif %}">
<div class="li-card__aside">
<span class="li-card__initial">{{ league.name[0]|upper }}</span>
</div>
<div class="li-card__body">
<div class="li-card__head">
<div class="li-card__info">
<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="live-badge">{{ session_label(item.live_session) }}</span>
{% else %}
<span class="status-pill status-pill--closed">idle</span>
{% endif %}
</div>
<div class="li-card__chips">
<span class="db-chip db-chip--accent">{{ membership.role }}</span>
<span class="db-chip {% if league.visibility == 'public' %}db-chip--public{% endif %}">
{% if league.visibility == 'public' %}
<svg width="10" height="10" viewBox="0 0 20 20" fill="none" aria-hidden="true">
<circle cx="10" cy="10" r="8" stroke="currentColor" stroke-width="1.6"/>
<path d="M10 2c-2.5 3-2.5 13 0 16" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" fill="none"/>
<path d="M10 2c2.5 3 2.5 13 0 16" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" fill="none"/>
<path d="M2.5 10h15" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
</svg>
public
{% else %}
<svg width="9" height="11" viewBox="0 0 10 12" fill="none" aria-hidden="true">
<rect x="1" y="5" width="8" height="6.5" rx="1.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M3 5V3.5A2 2 0 0 1 7 3.5V5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
private
{% endif %}
</span>
</div>
{% if league.description %}
<p class="li-card__desc">{{ league.description }}</p>
{% endif %}
</span>
</div>
<p class="league-row__desc">
{% if league.description %}{{ league.description }}{% else %}{{ league.slug }}{% endif %}
</p>
<div class="league-row__meta">
{% if item.latest_session %}
<span>Latest {{ session_label(item.latest_session) }}</span>
{% else %}
<span>No sessions yet</span>
{% endif %}
{% if item.leader %}
<span>Leader {{ item.leader.player_name }} · {{ item.leader.total_net_cents | money }}</span>
{% endif %}
</div>
</div>
</div>
<div class="league-row__metrics">
<div><span>Players</span><strong>{{ item.players_count }}</strong></div>
<div><span>Sessions</span><strong>{{ item.sessions_count }}</strong></div>
<div><span>Paid out</span><strong>{{ item.cash_paid_out_cents | money }}</strong></div>
<div><span>Open</span><strong class="{{ 'num-neg' if item.open_items_cents > 0 else 'num-muted' }}">{{ item.open_items_cents | money }}</strong></div>
</div>
<div class="league-row__leaders">
<div class="league-row__section-head">
<span class="kicker">Top 3</span>
<a href="{{ url_for('leagues.leaderboard', league_ref=league.url_ref) }}">Full board</a>
<div class="li-card__stats">
<div class="li-stat">
<span>Players</span>
<strong>{{ item.players_count }}</strong>
</div>
<div class="li-stat">
<span>Sessions</span>
<strong>{{ item.sessions_count }}</strong>
</div>
<div class="li-stat">
<span>Paid out</span>
<strong>{{ item.cash_paid_out_cents | money }}</strong>
</div>
<div class="li-stat">
<span>Open</span>
<strong class="{{ 'num-neg' if item.open_items_cents > 0 else 'num-muted' }}">{{ item.open_items_cents | money }}</strong>
</div>
</div>
</div>
{% if item.top_players %}
<ol class="league-top-list">
{% for player in item.top_players %}
<li>
<span>{{ player.player_name }}</span>
<strong class="{{ '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>
</li>
{% endfor %}
</ol>
{% else %}
<div class="league-row__empty">
<span>No results yet</span>
<a href="{{ url_for('leagues.sessions', league_ref=league.url_ref) }}">Create a session</a>
<div class="li-card__leaders">
<span class="li-card__leaders-label">Leaders</span>
<div class="li-podium">
{% for player in item.top_players %}
<div class="li-podium-item li-podium-item--{{ loop.index }}">
<span class="li-podium-rank">#{{ loop.index }}</span>
<span class="li-podium-name">{{ player.player_name }}</span>
<strong class="li-podium-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 class="li-card__footer">
<span class="li-card__meta">
{% if item.latest_session %}Latest {{ session_label(item.latest_session) }}{% elif not item.top_players %}No sessions yet{% endif %}
</span>
<div class="li-card__actions">
<a class="btn btn--primary btn--sm" href="{{ url_for('leagues.dashboard', league_ref=league.url_ref) }}">Open league</a>
<a class="li-action-link" href="{{ url_for('leagues.sessions', league_ref=league.url_ref) }}">Sessions</a>
<a class="li-action-link" href="{{ url_for('leagues.leaderboard', league_ref=league.url_ref) }}">Leaderboard</a>
<a class="li-action-link" href="{{ url_for('leagues.ledger', league_ref=league.url_ref) }}">Ledger</a>
</div>
</div>
</div>
<div class="league-row__actions">
<a class="btn btn--primary btn--sm" href="{{ url_for('leagues.dashboard', league_ref=league.url_ref) }}">Open</a>
<a class="btn btn--ghost btn--sm" href="{{ url_for('leagues.sessions', league_ref=league.url_ref) }}">Sessions</a>
<a class="btn btn--ghost btn--sm" href="{{ url_for('leagues.ledger', league_ref=league.url_ref) }}">Ledger</a>
<a class="btn btn--ghost btn--sm" href="{{ url_for('leagues.players', league_ref=league.url_ref) }}">Players</a>
</div>
</article>
{% endfor %}
</div>
{% else %}
<div class="panel empty-panel">
<h2 class="panel__title">No leagues yet</h2>
<p class="muted-text">Create the first league to start adding players, sessions, and ledger events.</p>
<p class="muted-text">Create your first league to start adding players, sessions, and ledger events.</p>
<a class="btn btn--primary" href="{{ url_for('leagues.new') }}">Create league</a>
</div>
{% endif %}