From ba72ead2295b63a0ee83390a001d34c72686e9b9 Mon Sep 17 00:00:00 2001 From: SowinskiBraeden Date: Sat, 21 Mar 2026 01:06:31 -0700 Subject: [PATCH] imrpoved session details page --- app.py | 2 + static/css/style.css | 106 +++++++++++++++++++++++++++- stats.py | 26 +++++++ templates/session_detail.html | 128 ++++++++++++++++++++++++++++------ 4 files changed, 238 insertions(+), 24 deletions(-) diff --git a/app.py b/app.py index 0efde3b..a7fbbb2 100644 --- a/app.py +++ b/app.py @@ -14,6 +14,7 @@ from stats import ( cumulative_profit_series, player_session_series, safe_date_label, + session_breakdown_series, session_events, unique_player_names, ) @@ -155,6 +156,7 @@ def session_detail(session_date: str) -> str: next_session=next_session, prev_session=prev_session, raw_events=session_events(events, session_date), + chart_data=session_breakdown_series(target_session), ) diff --git a/static/css/style.css b/static/css/style.css index 727b1be..f973944 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -755,11 +755,102 @@ td a:hover { } .leaderboard-layout, -.session-layout, .admin-layout { align-items: stretch; } +.session-layout { + align-items: stretch; + grid-template-columns: minmax(0, 1.45fr) minmax(320px, 0.85fr); +} + +.results-panel, +.chart-panel { + display: flex; + flex-direction: column; + height: 430px; + min-height: 430px; + max-height: 430px; +} + +.results-panel .table-scroll { + flex: 1; + min-height: 0; + overflow-x: auto; + overflow-y: auto; + padding-right: 0.25rem; + + scrollbar-gutter: stable; + scrollbar-width: auto; + scrollbar-color: rgba(255, 160, 64, 0.88) rgba(255, 255, 255, 0.08); +} + +.results-panel .table-scroll::-webkit-scrollbar { + width: 14px; + height: 12px; +} + +.results-panel .table-scroll::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.06); + border-radius: 999px; +} + +.results-panel .table-scroll::-webkit-scrollbar-thumb { + background: linear-gradient( + 180deg, + rgba(255, 186, 110, 0.95), + rgba(255, 136, 48, 0.95) + ); + border-radius: 999px; + border: 2px solid rgba(10, 12, 18, 0.9); + box-shadow: + 0 0 0 1px rgba(255, 170, 90, 0.18), + 0 0 12px rgba(255, 140, 60, 0.18); +} + +.results-panel .table-scroll::-webkit-scrollbar-thumb:hover { + background: linear-gradient( + 180deg, + rgba(255, 200, 130, 1), + rgba(255, 150, 60, 1) + ); +} + +.results-panel .table-scroll::-webkit-scrollbar-corner { + background: rgba(255, 255, 255, 0.04); +} + +.chart-panel .chart-frame { + flex: 1; + min-height: 0; + height: auto; +} + +.session-results-table { + min-width: 980px; +} + +.session-results-table th, +.session-results-table td { + vertical-align: top; + white-space: nowrap; +} + +.session-results-table th:last-child, +.session-results-table td:last-child { + min-width: 15rem; + width: 18rem; + white-space: normal; +} + +.session-results-table thead th { + position: sticky; + top: 0; + z-index: 2; + background: rgba(16, 20, 28, 0.96); + backdrop-filter: blur(6px); +} + .button-row { display: flex; gap: 0.75rem; @@ -880,6 +971,19 @@ td a:hover { border: 1px solid rgba(203, 213, 225, 0.14); } +.status-badge { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 5.5rem; + padding: 0.35rem 0.65rem; + border-radius: 999px; + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; +} + @media (max-width: 980px) { .site-shell { width: min(100% - 24px, 100%); diff --git a/stats.py b/stats.py index f742373..9ebe6c1 100644 --- a/stats.py +++ b/stats.py @@ -78,6 +78,18 @@ class SessionSummary: def total_net_cents(self) -> int: return sum(entry.net_cents for entry in self.entries) + @property + def total_payout_due_cents(self) -> int: + return sum(entry.payout_due_cents for entry in self.entries) + + @property + def total_paid_cents(self) -> int: + return sum(entry.paid_cents for entry in self.entries) + + @property + def total_remaining_cents(self) -> int: + return sum(entry.payout_remaining_cents for entry in self.entries) + @dataclass class PlayerStats: @@ -437,6 +449,20 @@ def player_session_series( } +def session_breakdown_series(session: SessionSummary) -> dict[str, Any]: + ordered_entries = sorted( + session.entries, + key=lambda entry: (entry.net_cents, entry.player_name.casefold()), + reverse=True, + ) + + return { + "labels": [entry.player_name for entry in ordered_entries], + "net_values": [round(entry.net_cents / 100, 2) for entry in ordered_entries], + "net_colors": [net_tone(entry.net_cents) for entry in ordered_entries], + } + + def session_events(events: list[EventRow], session_date: str) -> list[EventRow]: return [event for event in events if event["session_date"] == session_date] diff --git a/templates/session_detail.html b/templates/session_detail.html index 7a74b90..5fae13f 100644 --- a/templates/session_detail.html +++ b/templates/session_detail.html @@ -20,16 +20,35 @@ +
+
+ Total cash-out due + {{ session.total_payout_due_cents | money }} +
+ +
+ Total paid + {{ session.total_paid_cents | money }} +
+ +
+ Total remaining + + {{ session.total_remaining_cents | money }} + +
+
+
-
+

Results

Player totals

-
- +
+
@@ -49,7 +68,7 @@ -
Player{{ entry.buy_in_cents | money }} {{ entry.cash_out_cents | money }} {{ entry.paid_cents | money }} + {{ entry.payout_remaining_cents | money }} @@ -78,30 +97,93 @@ -
+
-

Audit trail

-

Raw ledger events

+

Breakdown

+

Net by player

-
- {% for event in raw_events | reverse %} -
-
- {{ event.player_name }} - {{ event.event_type }} -
-

{{ event.amount_cents | money }}

- {% if event.note %} -

{{ event.note }}

- {% endif %} -

{{ event.created_at }} · by {{ event.actor }}

-
- {% else %} -

No raw events recorded.

- {% endfor %} +
+
+ +
+
+
+

Audit trail

+

Raw ledger events

+
+
+
+ {% for event in raw_events | reverse %} +
+
+ {{ event.player_name or "Session" }} + {{ event.event_type }} +
+

{{ event.amount_cents | money }}

+ {% if event.note %} +

{{ event.note }}

+ {% endif %} +

{{ event.created_at }} · by {{ event.actor }}

+
+ {% else %} +

No raw events recorded.

+ {% endfor %} +
+
+ + {% endblock %}