DIS_IPL / templates /admin.html
Jay-Rajput's picture
Admin panel imprvmnts
47370ee
{% extends 'base.html' %}
{% block title %}Admin – {{ app_brand }}{% endblock %}
{% block head %}
<style>
@media (max-width: 860px) {
.admin-result-form {
min-width: 0 !important;
width: 100%;
}
.admin-header-logout {
width: 100%;
}
}
</style>
{% endblock %}
{% block content %}
<div class="page">
<div class="page-header" style="display:flex; flex-wrap:wrap; align-items:flex-start; justify-content:space-between; gap:1rem;">
<div>
<div class="page-title">⚙️ ADMIN PANEL</div>
<div class="page-subtitle">Manage matches, users, results and points</div>
</div>
<a href="{{ url_for('admin_logout') }}" class="btn btn-ghost btn-sm admin-header-logout" style="align-self:center;">🔒 Log out admin</a>
</div>
<!-- Tab Nav -->
<div style="display:flex; gap:0.5rem; margin-bottom:1.5rem; border-bottom:1px solid var(--border); padding-bottom:0.5rem; flex-wrap:wrap;">
{% for tab in [('matches','📅 Matches'),('add_match','➕ Add Match'),('results','✅ Set Results'),('users','👥 Users')] %}
<button class="btn btn-ghost btn-sm tab-btn" data-tab="{{ tab[0] }}" onclick="showTab('{{ tab[0] }}')">{{ tab[1] }}</button>
{% endfor %}
</div>
<!-- ── TAB: MATCHES ─────────────────────────────────────────────────────── -->
<div id="tab-matches" class="tab-panel">
<div style="display:flex; justify-content:flex-end; margin-bottom:0.75rem;">
<a href="{{ url_for('team_pool') }}" class="btn btn-secondary btn-sm">🃏 Open team Pool (everyone’s picks)</a>
</div>
<div class="card">
<div class="table-wrap">
<table>
<thead>
<tr>
<th>#</th><th>Teams</th><th>Date & Time</th><th>Venue</th>
<th>Status</th><th>Winner</th><th>Preds</th><th>Actions</th>
</tr>
</thead>
<tbody>
{% for match in matches %}
<tr>
<td style="color:var(--muted);">{{ match.match_number or '—' }}</td>
<td>
<div style="font-weight:700;">
<span style="color:{{ match.team1_color }};">{{ match.team1_abbr }}</span>
<span style="color:var(--muted);"> vs </span>
<span style="color:{{ match.team2_color }};">{{ match.team2_abbr }}</span>
</div>
<div style="font-size:0.75rem; color:var(--muted2);">{{ match.team1 }} vs {{ match.team2 }}</div>
</td>
<td style="font-size:0.85rem; white-space:nowrap;">
{{ match.match_date|format_date }}<br>
<span style="color:var(--muted2);">{{ match.match_time }}</span>
</td>
<td style="font-size:0.8rem; color:var(--muted2);">{{ match.venue or '—' }}</td>
<td>
<span class="badge badge-{{ match.status }}">{{ match.status }}</span>
</td>
<td style="font-size:0.85rem;">
{% if match.winner and match.winner != 'ABANDONED' %}
<div style="color:var(--green);">{{ match.winner }}</div>
{% if match.man_of_match %}<div style="color:var(--muted2); font-size:0.78rem;">⭐ {{ match.man_of_match }}</div>{% endif %}
{% elif match.status == 'abandoned' %}<span style="color:var(--muted);">Abandoned</span>
{% else %}—{% endif %}
</td>
<td>
<a href="{{ url_for('admin_match_predictions', match_id=match.id) }}" class="btn btn-ghost btn-sm">View</a>
</td>
<td>
<div style="display:flex; gap:0.4rem; flex-wrap:wrap;">
<!-- Status update -->
<form method="post" action="{{ url_for('admin_update_status', match_id=match.id) }}" style="display:flex; gap:0.3rem;">
<select name="status" style="width:auto; font-size:0.78rem; padding:0.3rem 0.5rem;">
{% for s in statuses %}
<option value="{{ s }}" {% if match.status == s %}selected{% endif %}>{{ s }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-secondary btn-sm" style="padding:0.3rem 0.5rem;">Set</button>
</form>
<!-- Delete -->
<form method="post" action="{{ url_for('admin_delete_match', match_id=match.id) }}"
onsubmit="return confirm('Delete this match? (Only possible if no predictions exist)')">
<button type="submit" class="btn btn-danger btn-sm" style="padding:0.3rem 0.5rem;">🗑</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- ── TAB: ADD MATCH ───────────────────────────────────────────────────── -->
<div id="tab-add_match" class="tab-panel" style="display:none;">
<div class="card" style="max-width:700px;">
<div class="card-title">➕ ADD NEW MATCH</div>
<form method="post" action="{{ url_for('admin_add_match') }}">
<div class="form-row cols-2">
<div class="form-group">
<label>Team 1 *</label>
<select name="team1" required>
<option value="">Select team…</option>
{% for t in teams %}<option value="{{ t }}">{{ t }}</option>{% endfor %}
</select>
</div>
<div class="form-group">
<label>Team 2 *</label>
<select name="team2" required>
<option value="">Select team…</option>
{% for t in teams %}<option value="{{ t }}">{{ t }}</option>{% endfor %}
</select>
</div>
</div>
<div class="form-row cols-3">
<div class="form-group">
<label>Match Date *</label>
<input type="date" name="match_date" required value="{{ today }}">
</div>
<div class="form-group">
<label>Start Time *</label>
<input type="time" name="match_time" required value="19:30">
<div class="form-hint">{% if points_config.lock_minutes_before %}Lock auto-triggers {{ points_config.lock_minutes_before }} min before start{% else %}Lock auto-triggers at scheduled match start{% endif %}</div>
</div>
<div class="form-group">
<label>Match Number</label>
<input type="number" name="match_number" placeholder="e.g. 1">
</div>
</div>
<div class="form-row cols-2">
<div class="form-group">
<label>Venue</label>
<input type="text" name="venue" placeholder="e.g. Wankhede Stadium">
</div>
<div class="form-group">
<label>City</label>
<input type="text" name="city" placeholder="e.g. Mumbai">
</div>
</div>
<button type="submit" class="btn btn-primary">➕ Add Match</button>
</form>
</div>
<!-- Bulk IPL 2025 schedule hint -->
<div class="card" style="max-width:700px; margin-top:1rem; padding:1rem; background:rgba(59,130,246,0.05); border-color:rgba(59,130,246,0.2);">
<div style="font-size:0.875rem; color:var(--muted2);">
<strong style="color:var(--blue);">💡 Tip:</strong>
IPL matches typically kick off at <strong>7:30 PM IST</strong> with afternoon matches at <strong>3:30 PM IST</strong> on weekends.
Add matches before the season starts so your team can predict early!
</div>
</div>
</div>
<!-- ── TAB: RESULTS ─────────────────────────────────────────────────────── -->
<div id="tab-results" class="tab-panel" style="display:none;">
{% set settable = matches|selectattr('status','in',['completed','live','locked','abandoned'])|list %}
{% if settable %}
<div style="display:grid; gap:1rem;">
{% for match in settable %}
<div class="card">
<div style="display:flex; justify-content:space-between; align-items:flex-start; flex-wrap:wrap; gap:1rem;">
<div>
<div style="font-family:var(--font-display); font-size:1.3rem;">
<span style="color:{{ match.team1_color }};">{{ match.team1_abbr }}</span>
<span style="color:var(--muted);"> vs </span>
<span style="color:{{ match.team2_color }};">{{ match.team2_abbr }}</span>
<span class="badge badge-{{ match.status }}" style="margin-left:0.5rem; vertical-align:middle;">{{ match.status }}</span>
</div>
<div style="font-size:0.85rem; color:var(--muted2);">
Match #{{ match.match_number or '?' }} · {{ match.match_date|format_date }} · {{ match.match_time }}
{% if match.venue %} · {{ match.venue }}{% endif %}
</div>
{% if match.winner %}
<div style="margin-top:0.5rem; font-size:0.85rem; color:var(--green);">
Current: 🏆 {{ match.winner }} {% if match.man_of_match %}· ⭐ {{ match.man_of_match }}{% endif %}
{% if match.is_result_final %}<span class="badge badge-completed" style="margin-left:0.4rem;">Finalised</span>{% endif %}
</div>
{% endif %}
</div>
<form method="post" action="{{ url_for('admin_set_result', match_id=match.id) }}" class="admin-result-form" style="display:flex; flex-direction:column; gap:0.75rem; min-width:320px;">
<div class="form-row cols-2">
<div class="form-group" style="margin:0;">
<label>Winner *</label>
<select name="winner" id="admin-winner-{{ match.id }}" required>
<option value="">Select…</option>
<option value="{{ match.team1 }}" {% if match.winner == match.team1 %}selected{% endif %}>{{ match.team1 }}</option>
<option value="{{ match.team2 }}" {% if match.winner == match.team2 %}selected{% endif %}>{{ match.team2 }}</option>
</select>
</div>
<div class="form-group" style="margin:0;">
<label>Man of the Match</label>
<select name="man_of_match" id="admin-motm-{{ match.id }}" data-initial-motm="{{ (match.man_of_match or '')|e }}">
<option value="">— Pick winner first —</option>
</select>
<div class="form-hint" style="font-size:0.72rem; margin-top:0.25rem;">Squad for the selected winning team only.</div>
</div>
</div>
<div class="form-group" style="margin:0;">
<label>Notes (optional)</label>
<input type="text" name="result_notes" placeholder="e.g. Won by 4 wickets" value="{{ match.result_notes or '' }}">
</div>
{% if match.is_result_final %}
<label style="display:flex; align-items:center; gap:0.5rem; cursor:pointer; color:var(--muted2); font-size:0.85rem;">
<input type="checkbox" name="recalculate" value="1" style="width:auto;">
Reverse previous settlement &amp; recalculate
</label>
{% endif %}
<button type="submit" class="btn btn-success btn-sm">
{% if match.is_result_final %}🔄 Update &amp; Recalculate{% else %}✅ Set Result &amp; Settle{% endif %}
</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state card">
<div class="icon"></div>
<div>No matches need results yet. Complete or lock a match first.</div>
</div>
{% endif %}
</div>
<!-- ── TAB: USERS ───────────────────────────────────────────────────────── -->
<div id="tab-users" class="tab-panel" style="display:none;">
<!-- Add User Form -->
<div class="card" style="max-width:600px; margin-bottom:1.5rem;">
<div class="card-title">👤 ADD NEW PLAYER</div>
<form method="post" action="{{ url_for('admin_add_user') }}">
<div class="form-row cols-2">
<div class="form-group">
<label>Username *</label>
<input type="text" name="username" placeholder="e.g. rahul_ipl" required>
</div>
<div class="form-group">
<label>Display Name</label>
<input type="text" name="display_name" placeholder="e.g. Rahul Sharma">
</div>
</div>
<div class="form-row cols-2">
<div class="form-group">
<label>Password</label>
<input type="password" name="password" placeholder="Leave blank for name-picker sign-in">
</div>
<div class="form-group">
<label>Starting Points</label>
<input type="number" name="initial_points" value="{{ points_config.initial }}" min="100">
</div>
</div>
<button type="submit" class="btn btn-primary">➕ Add Player</button>
</form>
</div>
<!-- Users List -->
<div class="card">
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Player</th><th>Username</th><th style="text-align:right;">Points</th>
<th>Status</th><th>Actions</th>
</tr>
</thead>
<tbody>
{% for u in users %}
<tr>
<td>
<div style="font-weight:600;">{{ u.display_name or u.username }}</div>
<div style="font-size:0.75rem; color:var(--muted);">Joined {{ u.created_at[:10]|format_date }}</div>
</td>
<td style="font-family:var(--font-mono); color:var(--muted2);">@{{ u.username }}</td>
<td style="text-align:right; font-family:var(--font-mono); font-weight:700;">{{ '%.0f'|format(u.points) }}</td>
<td>
{% if u.is_active %}<span class="badge badge-completed">Active</span>
{% else %}<span class="badge badge-abandoned">Inactive</span>{% endif %}
</td>
<td>
<div style="display:flex; gap:0.4rem; flex-wrap:wrap; align-items:center;">
<!-- Adjust points -->
<form method="post" action="{{ url_for('admin_adjust_points', user_id=u.id) }}" style="display:flex; gap:0.3rem;">
<input type="number" name="amount" placeholder="±pts" style="width:75px; font-size:0.8rem; padding:0.3rem 0.5rem;">
<input type="text" name="reason" placeholder="reason" style="width:100px; font-size:0.8rem; padding:0.3rem 0.5rem;">
<button type="submit" class="btn btn-secondary btn-sm" style="padding:0.3rem 0.5rem;">Adjust</button>
</form>
<!-- Toggle active -->
<form method="post" action="{{ url_for('admin_toggle_user', user_id=u.id) }}">
<button type="submit" class="btn btn-ghost btn-sm">{{ '🚫 Disable' if u.is_active else '✅ Enable' }}</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
const ACTIVE_TAB_KEY = 'admin_tab';
const ADMIN_MOTM_BY_MATCH = {{ squads_by_match_id | tojson }};
function adminFillMotmForWinner(matchId) {
const w = document.getElementById('admin-winner-' + matchId);
const s = document.getElementById('admin-motm-' + matchId);
if (!w || !s) return;
const key = String(matchId);
const byMatch = ADMIN_MOTM_BY_MATCH[key] || ADMIN_MOTM_BY_MATCH[matchId];
const saved = (s.getAttribute('data-initial-motm') || '').trim();
const team = w.value;
s.innerHTML = '';
const ph = document.createElement('option');
ph.value = '';
if (!team) {
ph.textContent = '— Pick winner first —';
s.appendChild(ph);
return;
}
const players = (byMatch && byMatch[team]) ? byMatch[team] : [];
ph.textContent = players.length ? '— Optional: choose MOTM —' : '— No squad for this team —';
s.appendChild(ph);
for (let i = 0; i < players.length; i++) {
const o = document.createElement('option');
o.value = players[i];
o.textContent = players[i];
s.appendChild(o);
}
if (saved && players.indexOf(saved) !== -1) {
s.value = saved;
}
}
function showTab(name) {
document.querySelectorAll('.tab-panel').forEach(p => p.style.display = 'none');
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('btn-secondary'));
document.querySelectorAll('.tab-btn').forEach(b => b.classList.add('btn-ghost'));
const panel = document.getElementById('tab-' + name);
if (panel) panel.style.display = 'block';
const btn = document.querySelector(`[data-tab="${name}"]`);
if (btn) { btn.classList.remove('btn-ghost'); btn.classList.add('btn-secondary'); }
localStorage.setItem(ACTIVE_TAB_KEY, name);
}
// Restore last tab or default
const last = localStorage.getItem(ACTIVE_TAB_KEY) || 'matches';
showTab(last);
document.querySelectorAll('[id^="admin-winner-"]').forEach(function (el) {
const id = el.id.replace('admin-winner-', '');
el.addEventListener('change', function () { adminFillMotmForWinner(id); });
if (el.value) {
adminFillMotmForWinner(id);
}
});
</script>
{% endblock %}