Files
2026-06-10 10:46:22 +05:45

778 lines
44 KiB
PHP

@extends('layouts.master')
@section('content')
<div class="max-w-7xl mx-auto px-6 py-8">
<!-- HEADER -->
<div class="flex items-center justify-between mb-7">
<div>
<h1 class="text-slate-900 text-xl font-bold tracking-tight">Student Registrations</h1>
<p class="text-slate-500 text-sm mt-0.5">Track registrations and goal scores for today's session.</p>
</div>
<div class="flex items-center gap-2.5">
<a href="{{ route('registrations.index') }}"
class="flex items-center gap-2 bg-white hover:bg-slate-50 border border-slate-200 text-slate-700 text-sm font-medium px-4 py-2 rounded-lg transition-colors shadow-sm">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 6h18"></path>
<path d="M3 12h18"></path>
<path d="M3 18h18"></path>
</svg>
All Registrations
</a>
<a href="{{ route('leaderboard') }}"
class="flex items-center gap-2 bg-white hover:bg-slate-50 border border-slate-200 text-slate-700 text-sm font-medium px-4 py-2 rounded-lg transition-colors shadow-sm">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>
</svg>
Leaderboard
</a>
<button id="newRegistrationBtn"
class="flex items-center gap-2 bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-medium px-4 py-2 rounded-lg transition-colors shadow-sm">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
</svg>
New Registration
</button>
</div>
</div>
<!-- STATS -->
<div class="grid grid-cols-3 gap-4 mb-7">
<div class="bg-white rounded-xl border border-slate-200 px-5 py-4 flex items-center gap-4 shadow-sm">
<div class="w-9 h-9 rounded-lg bg-slate-100 flex items-center justify-center shrink-0">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#64748b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/>
<path d="M23 21v-2a4 4 0 00-3-3.87"/><path d="M16 3.13a4 4 0 010 7.75"/>
</svg>
</div>
<div>
<p class="text-2xl font-bold text-slate-900 leading-none">{{ $total }}</p>
<p class="text-xs text-slate-500 mt-1 font-medium">Total Registrations</p>
</div>
</div>
<div class="bg-white rounded-xl border border-slate-200 px-5 py-4 flex items-center gap-4 shadow-sm">
<div class="w-9 h-9 rounded-lg bg-emerald-50 flex items-center justify-center shrink-0">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 000 20M12 2a14.5 14.5 0 010 20M2 12h20"/>
</svg>
</div>
<div>
<p class="text-2xl font-bold text-slate-900 leading-none">{{ $played }}</p>
<p class="text-xs text-emerald-600 mt-1 font-medium">Played Today</p>
</div>
</div>
<div class="bg-white rounded-xl border border-slate-200 px-5 py-4 flex items-center gap-4 shadow-sm">
<div class="w-9 h-9 rounded-lg bg-indigo-50 flex items-center justify-center shrink-0">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#6366f1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
</svg>
</div>
<div>
<p class="text-2xl font-bold text-slate-900 leading-none">{{ $topScore }}</p>
<p class="text-xs text-indigo-600 mt-1 font-medium">Top Score</p>
</div>
</div>
</div>
<!-- TABLE -->
<div class="bg-white rounded-xl border border-slate-200 overflow-hidden shadow-sm">
<div class="px-5 py-3.5 border-b border-slate-100 flex items-center justify-between">
<div class="flex items-center gap-2 text-slate-700 font-semibold text-sm">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/>
<line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>
</svg>
Today's Sessions
</div>
<span class="text-xs font-medium text-slate-400 bg-slate-100 px-2 py-0.5 rounded-full">{{ $total }} students</span>
</div>
<table class="w-full text-sm">
<thead>
<tr class="bg-slate-50 border-b border-slate-100">
<th class="px-5 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider w-16">ID</th>
<th class="px-5 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Name</th>
<th class="px-5 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Phone</th>
<th class="px-5 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Email</th>
<th class="px-5 py-3 text-center text-xs font-semibold text-slate-500 uppercase tracking-wider">Today's Shots</th>
<th class="px-5 py-3 text-center text-xs font-semibold text-slate-500 uppercase tracking-wider">Total Score</th>
<th class="px-5 py-3 text-right text-xs font-semibold text-slate-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
@foreach ($registrations as $reg)
<tr class="hover:bg-slate-50/70 transition-colors group"
data-session-id="{{ $reg['session_id'] ?? '' }}"
data-name="{{ $reg['name'] }}"
data-total-score="{{ $reg['total_score'] }}"
data-shots="{{ json_encode($reg['today_goals'] ?? []) }}"
data-shots-recorded="{{ is_array($reg['today_goals']) ? count($reg['today_goals']) : 0 }}"
data-reg-id="{{ $reg['id'] }}">
<td class="px-5 py-3.5">
<span class="text-xs font-mono text-slate-400">#{{ str_pad($reg['id'], 4, '0', STR_PAD_LEFT) }}</span>
</td>
<td class="px-5 py-3.5">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center shrink-0">
<span class="text-xs font-bold text-indigo-600">{{ strtoupper(substr($reg['name'], 0, 1)) }}</span>
</div>
<span class="font-medium text-slate-800">{{ $reg['name'] }}</span>
</div>
</td>
<td class="px-5 py-3.5 text-slate-500">{{ $reg['phone'] }}</td>
<td class="px-5 py-3.5 text-slate-500">{{ $reg['email'] }}</td>
<td class="px-5 py-3.5">
<div class="shots-cell flex items-center justify-center gap-1.5">
@if(is_null($reg['today_goals']))
<span class="text-xs text-slate-400 italic">Not played</span>
@else
@foreach($reg['today_goals'] as $i => $goal)
@if($goal)
<div class="w-7 h-7 rounded-full bg-emerald-100 border-2 border-emerald-400 flex items-center justify-center" title="Shot {{ $i+1 }}: Goal">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</div>
@else
<div class="w-7 h-7 rounded-full bg-slate-100 border-2 border-slate-300 flex items-center justify-center" title="Shot {{ $i+1 }}: Miss">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</div>
@endif
@endforeach
@endif
</div>
</td>
<td class="px-5 py-3.5 text-center">
<div class="inline-flex items-center gap-1 font-bold text-slate-900">
<svg width="13" height="13" viewBox="0 0 24 24" fill="#6366f1" stroke="none"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
<span class="text-base score-cell">{{ $reg['total_score'] }}</span>
</div>
</td>
<td class="px-5 py-3.5">
<div class="flex items-center justify-end gap-1">
@if(is_null($reg['today_goals']))
<button class="flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-xs font-medium text-indigo-600 bg-indigo-50 hover:bg-indigo-100 border border-indigo-200 transition-colors">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
Record Score
</button>
@else
<span class="text-xs text-slate-400 italic px-1">Played ✓</span>
@endif
<button class="p-1.5 rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 transition-colors opacity-0 group-hover:opacity-100">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2"/></svg>
</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
<div class="px-5 py-3 border-t border-slate-100 flex items-center justify-between">
<p class="text-xs text-slate-400">Showing <span class="font-medium text-slate-600">{{ $total }}</span> students</p>
</div>
</div>
</div>
<!-- FLOATING FOOTBALL BTN -->
<style>
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-8px); }
}
#footballBtn { animation: float 2.8s ease-in-out infinite; }
#footballBtn:hover { animation: none; transform: translateY(-10px) rotate(12deg); }
#footballBtn:active { transform: scale(0.92); }
</style>
<button id="footballBtn" onclick="openScoreboard()" title="View Scoreboard"
class="fixed bottom-8 right-8 z-40 w-24 h-24 cursor-pointer bg-transparent border-none outline-none p-0">
<img src="{{ asset('assets/wcup.png') }}" alt="Scoreboard" class="w-full h-full object-contain drop-shadow-md">
</button>
<div class="fixed bottom-[7rem] right-8 z-40 pointer-events-none">
<span id="footballTooltip" class="bg-slate-900 text-white text-xs font-medium px-2.5 py-1 rounded-lg shadow opacity-0 transition-opacity duration-150 whitespace-nowrap">
Scoreboard
</span>
</div>
<iframe id="scoreboardFrame" src="" data-src="{{ route('scoreboard') }}"
allowfullscreen
style="display:none; position:fixed; inset:0; width:100vw; height:100vh; border:none; z-index:9999;"></iframe>
<!-- ============================================================ -->
<!-- REGISTRATION MODAL -->
<!-- ============================================================ -->
<div id="registrationModal" class="fixed inset-0 z-50 hidden items-center justify-center">
<!-- Backdrop -->
<div id="modalBackdrop" class="absolute inset-0 bg-slate-900/60 backdrop-blur-sm"></div>
<!-- Panel -->
<div class="relative bg-white rounded-2xl shadow-2xl w-full max-w-sm mx-4 overflow-hidden">
<!-- Top accent bar -->
<div class="h-1 bg-gradient-to-r from-indigo-500 via-purple-500 to-indigo-600"></div>
<div class="p-6">
<!-- Header -->
<div class="flex items-start justify-between mb-6">
<div>
<h3 class="text-slate-900 font-bold text-lg leading-tight">New Registration</h3>
<p id="modalSubtitle" class="text-slate-500 text-xs mt-0.5">Enter the student's phone number to begin.</p>
</div>
<button id="closeRegistrationModal"
class="ml-4 w-7 h-7 rounded-full bg-slate-100 hover:bg-slate-200 flex items-center justify-center text-slate-500 transition-colors shrink-0">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
</div>
<!-- Step indicators -->
<div class="flex items-center gap-2 mb-6">
<div id="step1dot" class="flex items-center gap-1.5">
<div class="w-6 h-6 rounded-full bg-indigo-600 flex items-center justify-center text-white text-xs font-bold">1</div>
<span class="text-xs font-medium text-indigo-600">Phone</span>
</div>
<div class="flex-1 h-px bg-slate-200" id="step1line"></div>
<div id="step2dot" class="flex items-center gap-1.5 opacity-40">
<div class="w-6 h-6 rounded-full bg-slate-300 flex items-center justify-center text-slate-600 text-xs font-bold">2</div>
<span class="text-xs font-medium text-slate-400">Verify</span>
</div>
<div class="flex-1 h-px bg-slate-200" id="step2line"></div>
<div id="step3dot" class="flex items-center gap-1.5 opacity-40">
<div class="w-6 h-6 rounded-full bg-slate-300 flex items-center justify-center text-slate-600 text-xs font-bold">3</div>
<span class="text-xs font-medium text-slate-400">Details</span>
</div>
</div>
<!-- STEP 1: Phone -->
<div id="stepPhone">
<label class="block text-xs font-semibold text-slate-600 mb-1.5 uppercase tracking-wide">Phone Number</label>
<div class="flex items-center border border-slate-200 rounded-xl overflow-hidden focus-within:ring-2 focus-within:ring-indigo-500 focus-within:border-indigo-500 transition-all bg-slate-50">
<div class="px-3 py-2.5 border-r border-slate-200 bg-white">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07A19.5 19.5 0 013.07 9.81 19.79 19.79 0 01.06 1.18 2 2 0 012.03 0h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L6.09 7.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 14.92z"/>
</svg>
</div>
<input type="tel" id="phone" placeholder="+977 98XXXXXXXX"
class="flex-1 px-3 py-2.5 bg-transparent text-sm text-slate-800 placeholder-slate-400 outline-none">
</div>
<button id="sendOtpBtn"
class="mt-4 w-full bg-indigo-600 hover:bg-indigo-700 active:bg-indigo-800 text-white text-sm font-semibold py-2.5 rounded-xl transition-colors flex items-center justify-center gap-2">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
</svg>
Send OTP
</button>
</div>
<!-- STEP 2: Verify OTP -->
<div id="stepOtp" class="hidden">
<div class="bg-indigo-50 border border-indigo-100 rounded-xl px-4 py-3 mb-4 flex items-center gap-3">
<div class="w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center shrink-0">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#6366f1" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07A19.5 19.5 0 013.07 9.81 19.79 19.79 0 01.06 1.18 2 2 0 012.03 0h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L6.09 7.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 14.92z"/>
</svg>
</div>
<div>
<p class="text-xs font-semibold text-indigo-700">OTP sent to <span id="phoneSentTo" class="font-bold"></span></p>
<p class="text-xs text-indigo-500 mt-0.5">Expires in 2 minutes</p>
</div>
</div>
<!-- Dev OTP preview -->
<div id="otpPreview" class="hidden bg-amber-50 border border-amber-200 rounded-xl px-4 py-2.5 mb-4 flex items-center gap-2">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#d97706" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
<p class="text-xs font-semibold text-amber-700">Dev OTP: <span id="otpValue" class="font-mono tracking-widest"></span></p>
</div>
<label class="block text-xs font-semibold text-slate-600 mb-1.5 uppercase tracking-wide">Enter OTP</label>
<input type="text" id="otp" maxlength="6" placeholder="— — — — — —"
class="w-full border border-slate-200 rounded-xl px-4 py-2.5 text-center text-xl font-mono tracking-[.5em] text-slate-800 bg-slate-50
focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 focus:bg-white transition-all placeholder-slate-300">
<button id="verifyOtpBtn"
class="mt-4 w-full bg-emerald-600 hover:bg-emerald-700 active:bg-emerald-800 text-white text-sm font-semibold py-2.5 rounded-xl transition-colors flex items-center justify-center gap-2">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"/>
</svg>
Verify OTP
</button>
<button id="backToPhone" class="mt-2.5 w-full text-xs text-slate-400 hover:text-slate-600 py-1.5 transition-colors">
Change phone number
</button>
</div>
<!-- STEP 3: Extra details (new user only) -->
<div id="stepDetails" class="hidden">
<div class="bg-emerald-50 border border-emerald-100 rounded-xl px-4 py-3 mb-4 flex items-center gap-3">
<div class="w-8 h-8 rounded-full bg-emerald-100 flex items-center justify-center shrink-0">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"/>
</svg>
</div>
<div>
<p class="text-xs font-semibold text-emerald-700">Phone verified!</p>
<p class="text-xs text-emerald-600 mt-0.5">New student please fill in their details.</p>
</div>
</div>
<div class="space-y-3">
<div>
<label class="block text-xs font-semibold text-slate-600 mb-1.5 uppercase tracking-wide">Full Name <span class="text-red-400">*</span></label>
<input type="text" id="regName" placeholder="Student's full name"
class="w-full border border-slate-200 rounded-xl px-3 py-2.5 text-sm text-slate-800 bg-slate-50
focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 focus:bg-white transition-all placeholder-slate-400">
</div>
<div>
<label class="block text-xs font-semibold text-slate-600 mb-1.5 uppercase tracking-wide">Email <span class="text-slate-400 font-normal normal-case">(optional)</span></label>
<input type="email" id="regEmail" placeholder="student@email.com"
class="w-full border border-slate-200 rounded-xl px-3 py-2.5 text-sm text-slate-800 bg-slate-50
focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 focus:bg-white transition-all placeholder-slate-400">
</div>
</div>
<button id="submitRegistrationBtn"
class="mt-5 w-full bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-semibold py-2.5 rounded-xl transition-colors flex items-center justify-center gap-2">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M16 21v-2a4 4 0 00-4-4H6a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="19" y1="8" x2="19" y2="14"/><line x1="22" y1="11" x2="16" y2="11"/>
</svg>
Register Student
</button>
</div>
<!-- Error message -->
<div id="modalError" class="hidden mt-3 bg-red-50 border border-red-200 rounded-xl px-4 py-3 flex items-center gap-2">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
<p id="modalErrorText" class="text-xs text-red-600 font-medium"></p>
</div>
</div>
</div>
</div>
<!-- ============================================================ -->
<!-- SHOT RECORDING SIDE PANEL -->
<!-- ============================================================ -->
<div id="shotPanel"
class="fixed top-0 right-0 h-full w-80 bg-white border-l border-slate-200 shadow-2xl z-40
transform translate-x-full transition-transform duration-300 ease-in-out
flex flex-col">
<!-- Panel Header -->
<div class="px-5 py-4 border-b border-slate-100 flex items-center justify-between bg-slate-50">
<div>
<p class="text-xs font-semibold text-slate-400 uppercase tracking-wider">Recording Shots For</p>
<p id="panelName" class="text-slate-900 font-bold text-base mt-0.5"></p>
</div>
<button id="closeShotPanel"
class="w-7 h-7 rounded-full bg-slate-200 hover:bg-slate-300 flex items-center justify-center text-slate-500 transition-colors">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
</div>
<!-- Shot Indicators -->
<div class="px-5 py-4 border-b border-slate-100">
<p class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Today's Shots</p>
<div class="flex items-center gap-3">
<div id="shotIndicator1" class="flex-1 h-10 rounded-lg border-2 border-slate-200 bg-slate-50 flex items-center justify-center text-xs font-bold text-slate-400">
Shot 1
</div>
<div id="shotIndicator2" class="flex-1 h-10 rounded-lg border-2 border-slate-200 bg-slate-50 flex items-center justify-center text-xs font-bold text-slate-400">
Shot 2
</div>
<div id="shotIndicator3" class="flex-1 h-10 rounded-lg border-2 border-slate-200 bg-slate-50 flex items-center justify-center text-xs font-bold text-slate-400">
Shot 3
</div>
</div>
</div>
<!-- Current Shot -->
<div class="flex-1 flex flex-col items-center justify-center px-5 gap-5">
<div id="shotReadyState">
<p class="text-center text-slate-400 text-xs font-semibold uppercase tracking-wider mb-1">Current Shot</p>
<p id="currentShotLabel" class="text-center text-slate-900 font-black text-4xl mb-6">Shot 1</p>
<div class="flex flex-col gap-3 w-full px-2">
<button id="goalBtn"
class="w-full py-8 flex flex-col items-center justify-center gap-3 rounded-2xl
bg-emerald-50 hover:bg-emerald-100 active:scale-95
border-2 border-emerald-300 hover:border-emerald-500
transition-all duration-150">
<span class="text-5xl">⚽</span>
<span class="text-emerald-700 font-black text-base uppercase tracking-widest">Goal</span>
<span class="text-emerald-500 text-xs font-semibold">+1 pt</span>
</button>
<button id="missBtn"
class="w-full py-8 flex flex-col items-center justify-center gap-3 rounded-2xl
bg-red-50 hover:bg-red-100 active:scale-95
border-2 border-red-300 hover:border-red-500
transition-all duration-150">
<span class="text-5xl font-black text-red-500">✕</span>
<span class="text-red-700 font-black text-base uppercase tracking-widest">Miss</span>
<span class="text-red-400 text-xs font-semibold">0 pts</span>
</button>
</div>
</div>
<!-- Done state (all 3 recorded) -->
<div id="shotDoneState" class="hidden text-center">
<div class="w-16 h-16 rounded-full bg-emerald-100 flex items-center justify-center mx-auto mb-3">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"/>
</svg>
</div>
<p class="text-slate-900 font-bold text-lg">All shots recorded!</p>
<p class="text-slate-500 text-sm mt-1">Session score: <span id="sessionScoreDisplay" class="font-bold text-indigo-600"></span> pts</p>
</div>
</div>
<!-- Total Score Footer -->
<div class="px-5 py-4 border-t border-slate-100 bg-slate-50">
<div class="flex items-center justify-between">
<p class="text-xs font-semibold text-slate-400 uppercase tracking-wider">Cumulative Total</p>
<div class="flex items-center gap-1">
<svg width="13" height="13" viewBox="0 0 24 24" fill="#6366f1" stroke="none">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
</svg>
<span id="panelTotalScore" class="text-slate-900 font-black text-xl">0</span>
<span class="text-slate-400 text-xs font-medium">pts</span>
</div>
</div>
</div>
</div>
<!-- Overlay behind panel -->
<div id="shotPanelOverlay"
class="fixed inset-0 bg-black/20 z-30 hidden"
id="shotPanelOverlay">
</div>
@endsection
@push('js')
<script>
// ── Row selection + Shot panel ───────────────────────────────
let selectedSessionId = null;
let currentShot = 1;
let shotResults = [];
let selectedRow = null;
function openShotPanel(row) {
// Deselect previous
if (selectedRow) selectedRow.classList.remove('ring-2', 'ring-inset', 'ring-indigo-400', 'bg-indigo-50/40');
selectedRow = row;
row.classList.add('ring-2', 'ring-inset', 'ring-indigo-400', 'bg-indigo-50/40');
selectedSessionId = row.dataset.sessionId;
currentShot = parseInt(row.dataset.shotsRecorded || 0) + 1;
shotResults = JSON.parse(row.dataset.shots || '[]');
document.getElementById('panelName').textContent = row.dataset.name;
document.getElementById('panelTotalScore').textContent = row.dataset.totalScore;
refreshPanel();
document.getElementById('shotPanel').classList.remove('translate-x-full');
document.getElementById('shotPanelOverlay').classList.remove('hidden');
}
function closeShotPanel() {
document.getElementById('shotPanel').classList.add('translate-x-full');
document.getElementById('shotPanelOverlay').classList.add('hidden');
if (selectedRow) selectedRow.classList.remove('ring-2', 'ring-inset', 'ring-indigo-400', 'bg-indigo-50/40');
selectedRow = null;
}
function refreshPanel() {
// Update indicators
for (let i = 1; i <= 3; i++) {
const el = document.getElementById('shotIndicator' + i);
el.className = 'flex-1 h-10 rounded-lg border-2 flex items-center justify-center text-xs font-bold transition-all';
if (i < currentShot) {
const goal = shotResults[i - 1];
if (goal) {
el.classList.add('border-emerald-400', 'bg-emerald-50', 'text-emerald-600');
el.innerHTML = ' Goal';
} else {
el.classList.add('border-red-300', 'bg-red-50', 'text-red-500');
el.innerHTML = ' Miss';
}
} else if (i === currentShot) {
el.classList.add('border-indigo-400', 'bg-indigo-50', 'text-indigo-600', 'animate-pulse');
el.innerHTML = 'Shot ' + i;
} else {
el.classList.add('border-slate-200', 'bg-slate-50', 'text-slate-400');
el.innerHTML = 'Shot ' + i;
}
}
if (currentShot > 3) {
document.getElementById('shotReadyState').classList.add('hidden');
document.getElementById('shotDoneState').classList.remove('hidden');
const goals = shotResults.filter(Boolean).length;
document.getElementById('sessionScoreDisplay').textContent = goals;
} else {
document.getElementById('shotReadyState').classList.remove('hidden');
document.getElementById('shotDoneState').classList.add('hidden');
document.getElementById('currentShotLabel').textContent = 'Shot ' + currentShot;
}
}
function recordShot(result) {
if (!selectedSessionId || currentShot > 3) return;
const shotNumber = currentShot;
$.post("{{ route('registrations.record-shot') }}", {
_token: "{{ csrf_token() }}",
session_id: selectedSessionId,
shot_number: shotNumber,
result: result ? 1 : 0,
})
.done(res => {
shotResults.push(result);
currentShot++;
// Update the indicator + panel
refreshPanel();
// Update total score in footer + in the row
document.getElementById('panelTotalScore').textContent = res.total_score;
if (selectedRow) {
selectedRow.dataset.totalScore = res.total_score;
selectedRow.dataset.shots = JSON.stringify(shotResults);
selectedRow.dataset.shotsRecorded = shotResults.length;
// Live-update score cell
const scoreCell = selectedRow.querySelector('.score-cell');
if (scoreCell) scoreCell.textContent = res.total_score;
// Live-update shot dots
const shotsCell = selectedRow.querySelector('.shots-cell');
if (shotsCell) {
shotsCell.innerHTML = shotResults.map((g, i) =>
g ? `<div class="w-7 h-7 rounded-full bg-emerald-100 border-2 border-emerald-400 flex items-center justify-center" title="Shot ${i+1}: Goal">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</div>`
: `<div class="w-7 h-7 rounded-full bg-slate-100 border-2 border-slate-300 flex items-center justify-center" title="Shot ${i+1}: Miss">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</div>`
).join('');
}
}
})
.fail(() => alert('Failed to record shot. Please try again.'));
}
document.getElementById('goalBtn').addEventListener('click', () => recordShot(true));
document.getElementById('missBtn').addEventListener('click', () => recordShot(false));
document.getElementById('closeShotPanel').addEventListener('click', closeShotPanel);
document.getElementById('shotPanelOverlay').addEventListener('click', closeShotPanel);
// Row click listeners
document.querySelectorAll('tr[data-session-id]').forEach(row => {
row.classList.add('cursor-pointer');
row.addEventListener('click', () => {
openShotPanel(row);
// Sync selected player to scoreboard
const regId = row.dataset.regId;
if (regId) {
$.post("{{ route('scoreboard.select') }}", {
_token: "{{ csrf_token() }}",
registration_id: regId,
});
}
});
});
// ── Scoreboard ──────────────────────────────────────────────
const footballBtn = document.getElementById('footballBtn');
const tooltip = document.getElementById('footballTooltip');
footballBtn.addEventListener('mouseenter', () => tooltip.style.opacity = 1);
footballBtn.addEventListener('mouseleave', () => tooltip.style.opacity = 0);
function openScoreboard() {
const frame = document.getElementById('scoreboardFrame');
if (!frame.src || frame.src === window.location.href) frame.src = frame.dataset.src;
const req = frame.requestFullscreen || frame.webkitRequestFullscreen || frame.mozRequestFullScreen || frame.msRequestFullscreen;
if (req) req.call(frame);
}
['fullscreenchange','webkitfullscreenchange','mozfullscreenchange','MSFullscreenChange'].forEach(e =>
document.addEventListener(e, () => {
const fs = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
document.getElementById('scoreboardFrame').style.display = fs ? 'block' : 'none';
})
);
// ── Modal state ──────────────────────────────────────────────
const modal = document.getElementById('registrationModal');
const stepPhone = document.getElementById('stepPhone');
const stepOtp = document.getElementById('stepOtp');
const stepDetails = document.getElementById('stepDetails');
function showModal() {
modal.classList.remove('hidden');
modal.classList.add('flex');
goToStep(1);
}
function hideModal() {
modal.classList.add('hidden');
modal.classList.remove('flex');
clearError();
document.getElementById('phone').value = '';
document.getElementById('otp').value = '';
document.getElementById('regName').value = '';
document.getElementById('regEmail').value = '';
}
function goToStep(step) {
stepPhone.classList.add('hidden');
stepOtp.classList.add('hidden');
stepDetails.classList.add('hidden');
clearError();
const subtitles = {
1: 'Enter the student\'s phone number to begin.',
2: 'Enter the 6-digit OTP sent to the student\'s phone.',
3: 'New student — fill in their details to complete registration.'
};
document.getElementById('modalSubtitle').textContent = subtitles[step];
// Step indicators
const s2 = document.getElementById('step2dot');
const s3 = document.getElementById('step3dot');
s2.classList.toggle('opacity-40', step < 2);
s3.classList.toggle('opacity-40', step < 3);
s2.querySelector('div').className = step >= 2
? 'w-6 h-6 rounded-full bg-indigo-600 flex items-center justify-center text-white text-xs font-bold'
: 'w-6 h-6 rounded-full bg-slate-300 flex items-center justify-center text-slate-600 text-xs font-bold';
s3.querySelector('div').className = step >= 3
? 'w-6 h-6 rounded-full bg-indigo-600 flex items-center justify-center text-white text-xs font-bold'
: 'w-6 h-6 rounded-full bg-slate-300 flex items-center justify-center text-slate-600 text-xs font-bold';
if (step === 1) stepPhone.classList.remove('hidden');
if (step === 2) stepOtp.classList.remove('hidden');
if (step === 3) stepDetails.classList.remove('hidden');
}
function showError(msg) {
document.getElementById('modalError').classList.remove('hidden');
document.getElementById('modalErrorText').textContent = msg;
}
function clearError() {
document.getElementById('modalError').classList.add('hidden');
}
// ── Open / Close ─────────────────────────────────────────────
document.getElementById('newRegistrationBtn').addEventListener('click', showModal);
document.getElementById('closeRegistrationModal').addEventListener('click', hideModal);
document.getElementById('modalBackdrop').addEventListener('click', hideModal);
document.getElementById('backToPhone').addEventListener('click', () => goToStep(1));
// ── Send OTP ─────────────────────────────────────────────────
document.getElementById('sendOtpBtn').addEventListener('click', function () {
const phone = document.getElementById('phone').value.trim();
if (!phone) { showError('Please enter a phone number.'); return; }
this.disabled = true;
this.textContent = 'Sending…';
$.post("{{ route('registrations.send-otp') }}", {
_token: "{{ csrf_token() }}",
phone: phone
})
.done(res => {
document.getElementById('phoneSentTo').textContent = phone;
if (res.otp) {
document.getElementById('otpPreview').classList.remove('hidden');
document.getElementById('otpValue').textContent = res.otp;
}
goToStep(2);
})
.fail(() => showError('Failed to send OTP. Please try again.'))
.always(() => {
this.disabled = false;
this.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg> Send OTP`;
});
});
// ── Verify OTP ───────────────────────────────────────────────
document.getElementById('verifyOtpBtn').addEventListener('click', function () {
const phone = document.getElementById('phone').value.trim();
const otp = document.getElementById('otp').value.trim();
if (!otp) { showError('Please enter the OTP.'); return; }
this.disabled = true;
this.textContent = 'Verifying…';
$.post("{{ route('registrations.verify-otp') }}", {
_token: "{{ csrf_token() }}",
phone: phone,
otp: otp
})
.done(res => {
if (res.status === 'session_created') {
hideModal();
location.reload();
} else if (res.status === 'needs_registration') {
goToStep(3);
}
})
.fail(xhr => {
const res = xhr.responseJSON;
if (res?.status === 'blocked') {
showError(res.message || 'This student has already played today.');
} else if (res?.status === 'invalid_otp') {
showError('Invalid OTP. Please try again.');
} else {
showError('Something went wrong. Please try again.');
}
})
.always(() => {
this.disabled = false;
this.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> Verify OTP`;
});
});
// ── Submit new registration (step 3) ─────────────────────────
document.getElementById('submitRegistrationBtn').addEventListener('click', function () {
const name = document.getElementById('regName').value.trim();
const email = document.getElementById('regEmail').value.trim();
const phone = document.getElementById('phone').value.trim();
if (!name) { showError('Please enter the student\'s name.'); return; }
this.disabled = true;
this.textContent = 'Registering…';
$.post("{{ route('registrations.store') }}", {
_token: "{{ csrf_token() }}",
phone: phone,
name: name,
email: email
})
.done(res => {
if (res.status === 'session_created') {
hideModal();
location.reload();
}
})
.fail(() => showError('Registration failed. Please try again.'))
.always(() => {
this.disabled = false;
this.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 00-4-4H6a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="19" y1="8" x2="19" y2="14"/><line x1="22" y1="11" x2="16" y2="11"/></svg> Register Student`;
});
});
</script>
@endpush