595 lines
31 KiB
HTML
595 lines
31 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}{{ session_model.display_label }} · {{ league.name }} · myboker.org{% endblock %}
|
||
{% block sidebar %}{% include "_league_sidebar.html" %}{% endblock %}
|
||
{% block page_class %}page--app{% endblock %}
|
||
{% block content %}
|
||
|
||
<div class="hero" style="padding-top:0;padding-bottom:12px;display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;justify-content:space-between;">
|
||
<div>
|
||
<h1 class="hero__title">{{ session_model.display_label }}</h1>
|
||
<p class="hero__sub">{{ session_model.session_date.isoformat() | pretty_date }} · <span class="status-badge status-{{ session_model.status }}">{{ session_model.status }}</span></p>
|
||
</div>
|
||
<div class="button-row">
|
||
<a class="btn btn--ghost btn--sm" href="{{ url_for('leagues.session_public_view', league_ref=league.url_ref, session_id=session_model.id) }}">Public view</a>
|
||
{% if can_manage %}
|
||
<button class="btn btn--ghost btn--sm" type="button" data-modal="modal-edit-session">Edit session</button>
|
||
{% if session_model.status == 'open' %}
|
||
<form method="post" action="{{ url_for('leagues.close_league_session', league_ref=league.url_ref, session_id=session_model.id) }}" style="margin:0;">
|
||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||
<button class="btn btn--ghost btn--sm" type="submit">Close session</button>
|
||
</form>
|
||
{% else %}
|
||
<button class="btn btn--ghost btn--sm" type="button" data-modal="modal-reopen-session">Reopen session</button>
|
||
{% endif %}
|
||
{% endif %}
|
||
{% if is_owner %}
|
||
<button class="btn btn--danger btn--sm" type="button" data-modal="modal-delete-session">Delete session</button>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cash-strip">
|
||
<div class="stat">
|
||
<span class="kicker">Cash in</span>
|
||
<div class="stat__val" style="color:var(--num)">{{ session.total_buy_in_cents | money }}</div>
|
||
{% if session.total_front_cents > 0 %}<div class="stat__sub">+ {{ session.total_front_cents | money }} fronted</div>{% endif %}
|
||
</div>
|
||
{% if session.total_payout_carry_in_cents > 0 or session.total_rollover_in_cents > 0 %}
|
||
<div class="stat">
|
||
<span class="kicker">Carry in</span>
|
||
<div class="stat__val" style="color:var(--rolled)">{{ (session.total_payout_carry_in_cents + session.total_rollover_in_cents) | money }}</div>
|
||
</div>
|
||
{% endif %}
|
||
<div class="stat">
|
||
<span class="kicker">Claimed</span>
|
||
<div class="stat__val" style="color:var(--num)">{{ session.total_cash_out_cents | money }}</div>
|
||
</div>
|
||
<div class="stat">
|
||
<span class="kicker">Paid out</span>
|
||
<div class="stat__val" style="color:var(--pos)">{{ session.total_paid_out_cents | money }}</div>
|
||
</div>
|
||
{% set total_unsettled = session.total_current_due_to_player_cents + session.total_current_due_to_house_cents %}
|
||
<div class="stat">
|
||
<span class="kicker">Unsettled</span>
|
||
<div class="stat__val" style="color:{{ 'var(--warn)' if total_unsettled > 0 else 'var(--muted)' }}">{{ total_unsettled | money }}</div>
|
||
{% if session.total_current_due_to_house_cents > 0 %}<div class="stat__sub" style="color:var(--neg)">{{ session.total_current_due_to_house_cents | money }} owed to house</div>{% endif %}
|
||
{% if session.total_current_due_to_player_cents > 0 %}<div class="stat__sub" style="color:var(--warn)">{{ session.total_current_due_to_player_cents | money }} owed to players</div>{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="stack">
|
||
|
||
{% if can_manage %}
|
||
<form class="panel form-card ledger-form" method="post" id="appendForm">
|
||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||
<div class="ledger-form__head">
|
||
<span class="eyebrow">Ledger entry</span>
|
||
{% if session_model.status == 'open' %}
|
||
<span class="ledger-form__status ledger-form__status--open">Live</span>
|
||
{% else %}
|
||
<span class="ledger-form__status ledger-form__status--closed">Closed</span>
|
||
{% endif %}
|
||
</div>
|
||
{% if session_model.status != 'open' %}
|
||
<div class="session-locked-banner">
|
||
<strong>Session closed</strong>
|
||
<span>Reopen to add events.</span>
|
||
</div>
|
||
{% endif %}
|
||
<fieldset {% if session_model.status != 'open' %}disabled{% endif %} style="border:none;padding:0;margin:0;min-width:0;display:grid;gap:10px;">
|
||
<label>
|
||
<span>Player</span>
|
||
<div class="player-picker" id="playerPicker">
|
||
<div class="player-picker__input-wrap">
|
||
<input
|
||
type="text"
|
||
id="playerSearch"
|
||
class="player-picker__search"
|
||
placeholder="Select a player…"
|
||
autocomplete="off"
|
||
value="{{ players | selectattr('id', 'equalto', append_form.player_id) | map(attribute='display_name') | first | default('') }}"
|
||
>
|
||
<svg class="player-picker__chevron" width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M2 4l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
</div>
|
||
<div class="player-picker__dropdown" id="playerDropdown" hidden>
|
||
{% for player in players %}
|
||
<button type="button" class="player-picker__opt" data-name="{{ player.display_name }}" data-id="{{ player.id }}">{{ player.display_name }}</button>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
<input type="hidden" name="player_id" id="playerIdInput" value="{{ append_form.player_id or '' }}">
|
||
</label>
|
||
<div class="ledger-form__ta-row">
|
||
<div class="ledger-form__inputs">
|
||
<label>
|
||
<span>Type</span>
|
||
<select name="event_type" id="eventTypeSelect" required>
|
||
{% for value, label in event_types %}
|
||
<option value="{{ value }}" {{ 'selected' if append_form.event_type == value else '' }}>{{ label }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</label>
|
||
<label>
|
||
<span>Amount</span>
|
||
<input type="number" step="0.01" min="0" name="amount" id="amountInput" value="{{ append_form.amount }}" placeholder="0.00">
|
||
</label>
|
||
</div>
|
||
{% if default_buyin_cents > 0 or default_rebuy_cents > 0 %}
|
||
<div class="amount-quickset" id="amountQuickset">
|
||
<div class="amount-quickset__presets">
|
||
{% if default_buyin_cents > 0 %}
|
||
<button type="button" class="amount-quickset__btn"
|
||
data-event-type="buyin"
|
||
data-base="{{ '%.2f' % (default_buyin_cents / 100) }}">
|
||
<span class="amount-quickset__lbl">Buy-in</span>
|
||
<span class="amount-quickset__val">${{ '%.2f' % (default_buyin_cents / 100) }}</span>
|
||
</button>
|
||
{% endif %}
|
||
{% if default_rebuy_cents > 0 %}
|
||
<button type="button" class="amount-quickset__btn"
|
||
data-event-type="buyin"
|
||
data-base="{{ '%.2f' % (default_rebuy_cents / 100) }}">
|
||
<span class="amount-quickset__lbl">Rebuy</span>
|
||
<span class="amount-quickset__val">${{ '%.2f' % (default_rebuy_cents / 100) }}</span>
|
||
</button>
|
||
{% endif %}
|
||
</div>
|
||
<div class="amount-quickset__mults">
|
||
<button type="button" class="amount-quickpick__btn" data-mult="1">1×</button>
|
||
<button type="button" class="amount-quickpick__btn" data-mult="2">2×</button>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
<label>
|
||
<span>Note <span class="form-opt-hint">(optional)</span></span>
|
||
<textarea name="note" rows="1" placeholder="Optional context" style="resize:vertical;">{{ append_form.note or '' }}</textarea>
|
||
</label>
|
||
<button class="btn btn--primary ledger-form__submit" type="submit">Append event</button>
|
||
</fieldset>
|
||
</form>
|
||
{% else %}
|
||
<div class="panel empty-panel">
|
||
<h2 class="panel__title">Read only</h2>
|
||
<p class="muted-text">Your role can inspect this session but cannot append ledger events.</p>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="panel">
|
||
<div class="panel__head">
|
||
<div>
|
||
<p class="kicker" style="margin:0;">Results</p>
|
||
<h2 class="panel__title">Player totals</h2>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
|
||
{% set ns = namespace(playing=0, settled=0, owes=0, owed=0) %}
|
||
{% for entry in session.entries %}
|
||
{% if entry.cash_out_cents == 0 and entry.invested_cents > 0 %}{% set ns.playing = ns.playing + 1 %}
|
||
{% elif entry.current_due_to_house_cents > 0 %}{% set ns.owes = ns.owes + 1 %}
|
||
{% elif entry.current_due_to_player_cents > 0 %}{% set ns.owed = ns.owed + 1 %}
|
||
{% else %}{% set ns.settled = ns.settled + 1 %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
{% if ns.playing > 0 %}<span class="ledger-tally ledger-tally--playing">{{ ns.playing }} in play</span>{% endif %}
|
||
{% if ns.owes > 0 %}<span class="ledger-tally ledger-tally--owes">{{ ns.owes }} owe</span>{% endif %}
|
||
{% if ns.owed > 0 %}<span class="ledger-tally ledger-tally--owed">{{ ns.owed }} owed</span>{% endif %}
|
||
{% if ns.settled > 0 %}<span class="ledger-tally ledger-tally--settled">{{ ns.settled }} settled</span>{% endif %}
|
||
<span class="panel__tag">{{ session.entries|length }} players</span>
|
||
</div>
|
||
</div>
|
||
{% if session.entries %}
|
||
<div class="player-ledger-list">
|
||
{% for entry in session.entries %}
|
||
<div class="player-ledger-row">
|
||
<div class="player-ledger-row__who">
|
||
<span class="player-ledger-row__name">{{ entry.player_name }}</span>
|
||
{% if entry.cash_out_cents == 0 and entry.invested_cents > 0 %}
|
||
<span class="badge badge--playing">In play</span>
|
||
{% elif entry.current_due_to_house_cents > 0 %}
|
||
<span class="badge badge--owes">Owes {{ entry.current_due_to_house_cents | money }}</span>
|
||
{% elif entry.current_due_to_player_cents > 0 %}
|
||
<span class="badge badge--unpaid">Due {{ entry.current_due_to_player_cents | money }}</span>
|
||
{% else %}
|
||
<span class="badge badge--settled">Settled</span>
|
||
{% endif %}
|
||
</div>
|
||
<div class="player-ledger-row__stats">
|
||
<div class="player-ledger-row__stat">
|
||
<span class="player-ledger-row__lbl">In</span>
|
||
<span class="player-ledger-row__val">{{ entry.invested_cents | money }}</span>
|
||
</div>
|
||
<div class="player-ledger-row__stat">
|
||
<span class="player-ledger-row__lbl">Cashout</span>
|
||
<span class="player-ledger-row__val">{{ entry.cash_out_cents | money }}</span>
|
||
</div>
|
||
<div class="player-ledger-row__stat">
|
||
<span class="player-ledger-row__lbl">Paid out</span>
|
||
<span class="player-ledger-row__val" style="color:var(--pos)">{{ entry.paid_out_cents | money if entry.paid_out_cents else '—' }}</span>
|
||
</div>
|
||
<div class="player-ledger-row__stat player-ledger-row__stat--net">
|
||
<span class="player-ledger-row__lbl">Net</span>
|
||
<span class="player-ledger-row__val {{ 'num-pos' if entry.net_cents > 0 else 'num-neg' if entry.net_cents < 0 else 'num-muted' }}">{{ '+' if entry.net_cents > 0 else '' }}{{ entry.net_cents | money }}</span>
|
||
</div>
|
||
{% if entry.rollover_in_cents or entry.rollover_out_cents or entry.payout_carry_in_cents %}
|
||
<div class="player-ledger-row__stat">
|
||
<span class="player-ledger-row__lbl">Rolled in</span>
|
||
<span class="player-ledger-row__val" style="color:var(--rolled)">{{ entry.rollover_in_cents | money }}</span>
|
||
</div>
|
||
<div class="player-ledger-row__stat">
|
||
<span class="player-ledger-row__lbl">Carry in</span>
|
||
<span class="player-ledger-row__val" style="color:var(--rolled)">{{ entry.payout_carry_in_cents | money }}</span>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% if can_manage and session_model.status == 'open' %}
|
||
<div class="player-ledger-row__actions">
|
||
{% if default_rebuy_cents > 0 %}
|
||
<form method="post" style="margin:0;">
|
||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||
<input type="hidden" name="player_id" value="{{ entry.player_id }}">
|
||
<input type="hidden" name="event_type" value="buyin">
|
||
<input type="hidden" name="amount" value="{{ '%.2f' % (default_rebuy_cents / 100) }}">
|
||
<button type="submit" class="btn btn--ghost">Rebuy ${{ '%.2f' % (default_rebuy_cents / 100) }}</button>
|
||
</form>
|
||
{% elif default_buyin_cents > 0 %}
|
||
<form method="post" style="margin:0;">
|
||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||
<input type="hidden" name="player_id" value="{{ entry.player_id }}">
|
||
<input type="hidden" name="event_type" value="buyin">
|
||
<input type="hidden" name="amount" value="{{ '%.2f' % (default_buyin_cents / 100) }}">
|
||
<button type="submit" class="btn btn--ghost">Rebuy ${{ '%.2f' % (default_buyin_cents / 100) }}</button>
|
||
</form>
|
||
{% else %}
|
||
<button type="button" class="btn btn--ghost player-row-prefill"
|
||
data-player-id="{{ entry.player_id }}"
|
||
data-player-name="{{ entry.player_name }}"
|
||
data-event-type="buyin">Rebuy</button>
|
||
{% endif %}
|
||
<button type="button" class="btn btn--ghost player-row-prefill"
|
||
data-player-id="{{ entry.player_id }}"
|
||
data-player-name="{{ entry.player_name }}"
|
||
data-event-type="cashout">Cash out</button>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% else %}
|
||
<p class="muted-text" style="padding:28px;">No player ledger events yet.</p>
|
||
{% endif %}
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="panel" style="margin-top:20px;">
|
||
<div class="panel__head">
|
||
<div>
|
||
<p class="kicker" style="margin:0;">Audit trail</p>
|
||
<h2 class="panel__title">Raw ledger events</h2>
|
||
</div>
|
||
<span class="badge badge--closed">append-only</span>
|
||
</div>
|
||
<div class="event-feed feed feed--inline" style="max-height:none;">
|
||
{% for event in raw_events | reverse %}
|
||
<div class="event-card event-card--inline {% if event.voided_at %}event-card--voided{% endif %}">
|
||
<div class="event-card-top">
|
||
<strong>{{ event.player_name or "Session" }}</strong>
|
||
<span class="pill pill-{{ event.event_type }}">{{ event.event_type | replace('_', ' ') }}</span>
|
||
{% if event.voided_at %}<span class="void-badge">voided</span>{% endif %}
|
||
{% if event.note and not event.voided_at %}<span class="event-note">{{ event.note }}</span>{% endif %}
|
||
</div>
|
||
<p class="event-amount">{{ event.amount_cents | money }}</p>
|
||
{% if event.voided_at %}
|
||
<p class="tiny-text void-reason">{{ event.void_reason }}</p>
|
||
{% else %}
|
||
<p class="tiny-text">{{ event.created_at }}</p>
|
||
{% if is_owner %}
|
||
<button class="btn-void-toggle" type="button"
|
||
data-void-id="{{ event.id }}"
|
||
data-void-player="{{ event.player_name or 'Session' }}"
|
||
data-void-type="{{ event.event_type | replace('_', ' ') }}"
|
||
data-void-amount="${{ '%.2f' % (event.amount_cents / 100) }}"
|
||
data-void-action="{{ url_for('leagues.void_event', league_ref=league.url_ref, event_id=event.id) }}">
|
||
void
|
||
</button>
|
||
{% endif %}
|
||
{% endif %}
|
||
</div>
|
||
{% else %}
|
||
<p class="muted-text" style="padding:20px;">No events recorded.</p>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
|
||
{% if is_owner %}
|
||
<div class="modal-backdrop" id="modal-void-event" hidden>
|
||
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="modal-void-title">
|
||
<div class="modal-card__head">
|
||
<span class="kicker" style="margin:0;color:var(--warn);">Correct the record</span>
|
||
<h2 class="panel__title" id="modal-void-title">Void event</h2>
|
||
</div>
|
||
<p class="modal-card__text" id="modal-void-desc">This event will remain visible in the audit trail but will no longer count toward any calculations.</p>
|
||
<form id="modal-void-form" method="post" action="">
|
||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||
<label style="display:flex;flex-direction:column;gap:6px;margin-bottom:16px;">
|
||
<span style="font:600 12px/1 var(--font-ui);color:var(--text-2);text-transform:uppercase;letter-spacing:.04em;">Reason</span>
|
||
<input type="text" name="void_reason" placeholder="What was wrong with this event?" required maxlength="200"
|
||
style="background:var(--field);border:1px solid var(--border-strong);border-radius:var(--r-sm);padding:9px 12px;font:400 14px/1.4 var(--font-ui);color:var(--text-1);">
|
||
</label>
|
||
<div class="modal-card__actions">
|
||
<button class="btn btn--ghost btn--sm" type="button" data-close-modal>Cancel</button>
|
||
<button class="btn btn--danger btn--sm" type="submit">Void this event</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="modal-backdrop" id="modal-delete-session" hidden>
|
||
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="modal-delete-title">
|
||
<div class="modal-card__head">
|
||
<span class="kicker" style="margin:0;color:var(--neg);">Destructive action</span>
|
||
<h2 class="panel__title" id="modal-delete-title">Delete {{ session_model.display_label }}?</h2>
|
||
</div>
|
||
<p class="modal-card__text">This will permanently delete the session and <strong>all {{ session_model.event_count if session_model.event_count is defined else 'its' }} ledger events</strong> attached to it. There is no undo.</p>
|
||
<div class="modal-card__actions">
|
||
<button class="btn btn--ghost btn--sm" type="button" data-close-modal>Cancel</button>
|
||
<form method="post" action="{{ url_for('leagues.delete_session', league_ref=league.url_ref, session_id=session_model.id) }}" style="margin:0;">
|
||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||
<button class="btn btn--danger btn--sm" type="submit">Yes, delete permanently</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{% if can_manage %}
|
||
<div class="modal-backdrop" id="modal-edit-session" hidden>
|
||
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="modal-edit-session-title">
|
||
<div class="modal-card__head">
|
||
<span class="kicker" style="margin:0;">Session</span>
|
||
<h2 class="panel__title" id="modal-edit-session-title">Edit session</h2>
|
||
</div>
|
||
<form method="post" action="{{ url_for('leagues.edit_session', league_ref=league.url_ref, session_id=session_model.id) }}">
|
||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||
<div class="form-card" style="padding:0 20px 4px;gap:12px;">
|
||
<label>
|
||
<span>Date</span>
|
||
<input type="date" name="session_date" value="{{ session_model.session_date.isoformat() }}" required>
|
||
</label>
|
||
{% if seasons %}
|
||
<label>
|
||
<span>Season <span class="form-opt-hint">(optional)</span></span>
|
||
<select name="season_id">
|
||
<option value="">No season</option>
|
||
{% for s in seasons %}
|
||
<option value="{{ s.id }}" {{ 'selected' if session_model.season_id == s.id else '' }}>{{ s.name }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</label>
|
||
{% endif %}
|
||
<label>
|
||
<span>Label <span class="form-opt-hint">(optional)</span></span>
|
||
<input type="text" name="label" value="{{ session_model.label or '' }}" placeholder="e.g. Main table">
|
||
</label>
|
||
<label>
|
||
<span>Notes <span class="form-opt-hint">(optional)</span></span>
|
||
<input type="text" name="notes" value="{{ session_model.notes or '' }}" placeholder="Optional context">
|
||
</label>
|
||
</div>
|
||
<div class="modal-card__actions">
|
||
<button class="btn btn--ghost btn--sm" type="button" data-close-modal>Cancel</button>
|
||
<button class="btn btn--primary btn--sm" type="submit">Save changes</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="modal-backdrop" id="modal-reopen-session" hidden>
|
||
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="modal-reopen-title">
|
||
<div class="modal-card__head">
|
||
<span class="kicker" style="margin:0;">Settled session</span>
|
||
<h2 class="panel__title" id="modal-reopen-title">Reopen this session?</h2>
|
||
</div>
|
||
<p class="modal-card__text">This session has been closed and books are assumed to be settled. Reopening will allow new ledger events to be appended and may affect settlement totals.</p>
|
||
<div class="modal-card__actions">
|
||
<button class="btn btn--ghost btn--sm" type="button" data-close-modal>Cancel</button>
|
||
<form method="post" action="{{ url_for('leagues.open_league_session', league_ref=league.url_ref, session_id=session_model.id) }}" style="margin:0;">
|
||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||
<button class="btn btn--primary btn--sm" type="submit">Reopen session</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// --- Custom player picker ---
|
||
(function () {
|
||
const picker = document.getElementById('playerPicker');
|
||
if (!picker) return;
|
||
const searchInput = document.getElementById('playerSearch');
|
||
const idInput = document.getElementById('playerIdInput');
|
||
const dropdown = document.getElementById('playerDropdown');
|
||
const opts = dropdown ? Array.from(dropdown.querySelectorAll('.player-picker__opt')) : [];
|
||
const eventTypeSelect = document.getElementById('eventTypeSelect');
|
||
|
||
function openDropdown() {
|
||
if (!dropdown) return;
|
||
dropdown.removeAttribute('hidden');
|
||
}
|
||
function closeDropdown() {
|
||
if (dropdown) dropdown.setAttribute('hidden', '');
|
||
}
|
||
function filterOpts(q) {
|
||
const lq = q.toLowerCase().trim();
|
||
opts.forEach(opt => {
|
||
opt.style.display = (!lq || opt.dataset.name.toLowerCase().includes(lq)) ? '' : 'none';
|
||
});
|
||
}
|
||
function selectPlayer(name, id) {
|
||
searchInput.value = name;
|
||
idInput.value = id;
|
||
filterOpts(name);
|
||
closeDropdown();
|
||
}
|
||
function clearPlayer() {
|
||
searchInput.value = '';
|
||
idInput.value = '';
|
||
filterOpts('');
|
||
}
|
||
|
||
searchInput.addEventListener('focus', () => { filterOpts(searchInput.value); openDropdown(); });
|
||
searchInput.addEventListener('input', function () {
|
||
filterOpts(this.value);
|
||
openDropdown();
|
||
const matched = opts.find(o => o.dataset.name.toLowerCase() === this.value.trim().toLowerCase());
|
||
idInput.value = matched ? matched.dataset.id : '';
|
||
});
|
||
searchInput.addEventListener('keydown', function (e) {
|
||
if (e.key === 'Escape') closeDropdown();
|
||
if (e.key === 'Enter') {
|
||
const visible = opts.filter(o => o.style.display !== 'none');
|
||
if (visible.length === 1) { e.preventDefault(); selectPlayer(visible[0].dataset.name, visible[0].dataset.id); }
|
||
}
|
||
});
|
||
opts.forEach(opt => {
|
||
opt.addEventListener('mousedown', e => { e.preventDefault(); selectPlayer(opt.dataset.name, opt.dataset.id); });
|
||
});
|
||
document.addEventListener('click', e => { if (!picker.contains(e.target)) closeDropdown(); });
|
||
|
||
if (eventTypeSelect) {
|
||
eventTypeSelect.addEventListener('change', function () {
|
||
if (this.value === 'note') {
|
||
clearPlayer();
|
||
searchInput.placeholder = 'No player (session note)';
|
||
searchInput.disabled = true;
|
||
} else {
|
||
searchInput.placeholder = 'Search or select a player…';
|
||
searchInput.disabled = false;
|
||
}
|
||
});
|
||
if (eventTypeSelect.value === 'note') {
|
||
searchInput.placeholder = 'No player (session note)';
|
||
searchInput.disabled = true;
|
||
}
|
||
}
|
||
})();
|
||
|
||
// --- Quick-set amount buttons (Buy-in / Rebuy) + 1x/2x multipliers ---
|
||
(function () {
|
||
const amountInput = document.getElementById('amountInput');
|
||
const eventTypeSelect = document.getElementById('eventTypeSelect');
|
||
let quickBase = null;
|
||
let currentMult = 1;
|
||
|
||
function setAmount(val) {
|
||
if (!amountInput) return;
|
||
amountInput.value = parseFloat(val).toFixed(2);
|
||
}
|
||
|
||
function syncQuicksetActive(activeBtn) {
|
||
document.querySelectorAll('.amount-quickset__btn').forEach(b => {
|
||
b.classList.toggle('is-active', b === activeBtn);
|
||
});
|
||
}
|
||
|
||
function syncMultActive(mult) {
|
||
currentMult = mult;
|
||
document.querySelectorAll('.amount-quickpick__btn').forEach(b => {
|
||
b.classList.toggle('is-active', parseInt(b.dataset.mult) === mult);
|
||
});
|
||
}
|
||
|
||
document.querySelectorAll('.amount-quickset__btn').forEach(btn => {
|
||
btn.addEventListener('click', function () {
|
||
quickBase = parseFloat(this.dataset.base);
|
||
if (eventTypeSelect && this.dataset.eventType) {
|
||
eventTypeSelect.value = this.dataset.eventType;
|
||
}
|
||
setAmount(quickBase * currentMult);
|
||
syncQuicksetActive(this);
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('.amount-quickpick__btn').forEach(btn => {
|
||
btn.addEventListener('click', function () {
|
||
const mult = parseInt(this.dataset.mult);
|
||
syncMultActive(mult);
|
||
if (quickBase !== null) setAmount(quickBase * mult);
|
||
});
|
||
});
|
||
|
||
// Reset multiplier to 1× when user types manually
|
||
if (amountInput) {
|
||
amountInput.addEventListener('input', function () {
|
||
syncMultActive(1);
|
||
syncQuicksetActive(null);
|
||
quickBase = parseFloat(this.value) || null;
|
||
});
|
||
}
|
||
|
||
// Auto-select the Buy-in quickset button on load if buyin type and no amount
|
||
if (!amountInput || amountInput.value) return;
|
||
const buyinBtn = document.querySelector('.amount-quickset__btn[data-event-type="buyin"]');
|
||
if (buyinBtn && eventTypeSelect && eventTypeSelect.value === 'buyin') {
|
||
quickBase = parseFloat(buyinBtn.dataset.base);
|
||
setAmount(quickBase);
|
||
syncQuicksetActive(buyinBtn);
|
||
}
|
||
})();
|
||
|
||
// --- Per-player quick-action pre-fill ---
|
||
document.querySelectorAll('.player-row-prefill').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
var playerId = this.dataset.playerId;
|
||
var playerName = this.dataset.playerName;
|
||
var eventType = this.dataset.eventType;
|
||
var idInput = document.getElementById('playerIdInput');
|
||
var searchInput = document.getElementById('playerSearch');
|
||
var typeSelect = document.getElementById('eventTypeSelect');
|
||
var amountInput = document.getElementById('amountInput');
|
||
if (idInput) idInput.value = playerId;
|
||
if (searchInput) searchInput.value = playerName;
|
||
if (typeSelect) typeSelect.value = eventType;
|
||
if (amountInput) { amountInput.value = ''; amountInput.focus(); }
|
||
var form = document.getElementById('appendForm');
|
||
if (form) form.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('[data-modal]').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
var target = document.getElementById(this.dataset.modal);
|
||
if (target) target.removeAttribute('hidden');
|
||
});
|
||
});
|
||
document.querySelectorAll('[data-close-modal]').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
this.closest('.modal-backdrop').setAttribute('hidden', '');
|
||
});
|
||
});
|
||
document.querySelectorAll('.modal-backdrop').forEach(function(backdrop) {
|
||
backdrop.addEventListener('click', function(e) {
|
||
if (e.target === this) this.setAttribute('hidden', '');
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('[data-void-id]').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
var player = this.dataset.voidPlayer;
|
||
var type = this.dataset.voidType;
|
||
var amount = this.dataset.voidAmount;
|
||
document.getElementById('modal-void-title').textContent = 'Void ' + type + ' — ' + player;
|
||
document.getElementById('modal-void-desc').textContent =
|
||
'The ' + amount + ' ' + type + ' event for ' + player +
|
||
' will be marked voided and excluded from all calculations. It stays visible in the audit trail.';
|
||
var form = document.getElementById('modal-void-form');
|
||
form.action = this.dataset.voidAction;
|
||
form.querySelector('[name=void_reason]').value = '';
|
||
document.getElementById('modal-void-event').removeAttribute('hidden');
|
||
});
|
||
});
|
||
</script>
|
||
|
||
{% endblock %}
|