changes
This commit is contained in:
@@ -310,6 +310,42 @@ class RegistrationController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function correctShot(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'session_id' => 'required|exists:game_sessions,id',
|
||||||
|
'shot_number' => 'required|integer|in:1,2,3',
|
||||||
|
'result' => 'required|boolean',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$session = GameSession::with('registration')->findOrFail($request->session_id);
|
||||||
|
|
||||||
|
$shot = $session->shots()->where('shot_number', $request->shot_number)->first();
|
||||||
|
|
||||||
|
if ($shot) {
|
||||||
|
$shot->update(['result' => $request->result]);
|
||||||
|
} else {
|
||||||
|
GameShot::create([
|
||||||
|
'game_session_id' => $session->id,
|
||||||
|
'shot_number' => $request->shot_number,
|
||||||
|
'result' => $request->result,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessionScore = $session->calculateScore();
|
||||||
|
$session->update(['score' => $sessionScore]);
|
||||||
|
|
||||||
|
$registration = $session->registration;
|
||||||
|
$registration->update([
|
||||||
|
'total_score' => $registration->sessions()->sum('score')
|
||||||
|
]);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'ok',
|
||||||
|
'total_score' => $registration->fresh()->total_score,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
//Leaderboard
|
//Leaderboard
|
||||||
public function leaderboard()
|
public function leaderboard()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<div class="flex items-center justify-between mb-7">
|
<div class="flex items-center justify-between mb-7">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-slate-900 text-xl font-bold tracking-tight">Student Registrations</h1>
|
<h1 class="text-slate-900 text-xl font-bold tracking-tight">Student Registrations</h1>
|
||||||
<p class="text-slate-500 text-sm mt-0.5">View registrations and manage student comments.</p>
|
<p class="text-slate-500 text-sm mt-0.5">Track registrations and goal scores for today's session.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2.5">
|
<div class="flex items-center gap-2.5">
|
||||||
<!-- Search -->
|
<!-- Search -->
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||||
</svg>
|
</svg>
|
||||||
<input type="text" name="search" value="{{ $search ?? '' }}" placeholder="Search name, phone, email…"
|
<input type="text" name="search" value="{{ $search ?? '' }}" placeholder="Search name, phone, email…"
|
||||||
class="text-sm text-slate-700 placeholder-slate-400 outline-none bg-transparent w-52">
|
class="text-sm text-slate-700 placeholder-slate-400 outline-none bg-transparent w-48">
|
||||||
@if(!empty($search))
|
@if(!empty($search))
|
||||||
<a href="{{ route('home') }}" class="text-slate-400 hover:text-slate-600 transition-colors">
|
<a href="{{ route('home') }}" class="text-slate-400 hover:text-slate-600 transition-colors">
|
||||||
<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>
|
<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>
|
||||||
@@ -26,14 +26,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<a href="{{ route('registrations.index') }}"
|
<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">
|
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">
|
<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 d="M3 12h18"/><path d="M3 18h18"/>
|
<path d="M3 6h18"/><path d="M3 12h18"/><path d="M3 18h18"/>
|
||||||
</svg>
|
</svg>
|
||||||
All Registrations
|
All Registrations
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ route('leaderboard') }}"
|
<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">
|
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">
|
<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"/>
|
<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>
|
</svg>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-2xl font-bold text-slate-900 leading-none">{{ $total }}</p>
|
<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>
|
<p class="text-xs text-slate-500 mt-1 font-medium">Total Students</p>
|
||||||
</div>
|
</div>
|
||||||
</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="bg-white rounded-xl border border-slate-200 px-5 py-4 flex items-center gap-4 shadow-sm">
|
||||||
@@ -95,7 +95,7 @@
|
|||||||
<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="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"/>
|
<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>
|
</svg>
|
||||||
Today's Sessions
|
All Registrations
|
||||||
</div>
|
</div>
|
||||||
<span class="text-xs font-medium text-slate-400 bg-slate-100 px-2 py-0.5 rounded-full">{{ $total }} students</span>
|
<span class="text-xs font-medium text-slate-400 bg-slate-100 px-2 py-0.5 rounded-full">{{ $total }} students</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -106,7 +106,6 @@
|
|||||||
<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">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">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-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Email</th>
|
||||||
<th class="px-5 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Country</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">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-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>
|
<th class="px-5 py-3 text-right text-xs font-semibold text-slate-500 uppercase tracking-wider">Actions</th>
|
||||||
@@ -134,19 +133,6 @@
|
|||||||
</td>
|
</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['phone'] }}</td>
|
||||||
<td class="px-5 py-3.5 text-slate-500">{{ $reg['email'] }}</td>
|
<td class="px-5 py-3.5 text-slate-500">{{ $reg['email'] }}</td>
|
||||||
<td class="px-5 py-3.5" onclick="event.stopPropagation()">
|
|
||||||
<select class="country-select text-xs border border-slate-200 rounded-lg px-2 py-1.5 bg-white
|
|
||||||
text-slate-700 focus:outline-none focus:ring-2 focus:ring-indigo-400 cursor-pointer max-w-[140px]"
|
|
||||||
data-reg-id="{{ $reg['id'] }}">
|
|
||||||
<option value="">— Select —</option>
|
|
||||||
@foreach($countries as $country)
|
|
||||||
<option value="{{ $country->id }}"
|
|
||||||
{{ ($reg['country_id'] ?? null) == $country->id ? 'selected' : '' }}>
|
|
||||||
{{ $country->title }}
|
|
||||||
</option>
|
|
||||||
@endforeach
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="px-5 py-3.5">
|
<td class="px-5 py-3.5">
|
||||||
<div class="shots-cell flex items-center justify-center gap-1.5">
|
<div class="shots-cell flex items-center justify-center gap-1.5">
|
||||||
@if(is_null($reg['today_goals']))
|
@if(is_null($reg['today_goals']))
|
||||||
@@ -280,6 +266,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<input type="tel" id="phone" placeholder="+977 98XXXXXXXX"
|
<input type="tel" id="phone" placeholder="+977 98XXXXXXXX"
|
||||||
|
inputmode="numeric"
|
||||||
class="flex-1 px-3 py-2.5 bg-transparent text-sm text-slate-800 placeholder-slate-400 outline-none">
|
class="flex-1 px-3 py-2.5 bg-transparent text-sm text-slate-800 placeholder-slate-400 outline-none">
|
||||||
</div>
|
</div>
|
||||||
<button id="sendOtpBtn"
|
<button id="sendOtpBtn"
|
||||||
@@ -446,7 +433,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Done state (all 3 recorded) -->
|
<!-- Done state (all 3 recorded) -->
|
||||||
<div id="shotDoneState" class="hidden text-center">
|
<div id="shotDoneState" class="hidden text-center px-2">
|
||||||
<div class="w-16 h-16 rounded-full bg-emerald-100 flex items-center justify-center mx-auto mb-3">
|
<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">
|
<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"/>
|
<polyline points="20 6 9 17 4 12"/>
|
||||||
@@ -454,6 +441,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="text-slate-900 font-bold text-lg">All shots recorded!</p>
|
<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>
|
<p class="text-slate-500 text-sm mt-1">Session score: <span id="sessionScoreDisplay" class="font-bold text-indigo-600"></span> pts</p>
|
||||||
|
<button id="correctShotBtn"
|
||||||
|
class="mt-4 w-full flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl
|
||||||
|
border-2 border-amber-300 bg-amber-50 hover:bg-amber-100 text-amber-700
|
||||||
|
text-sm font-semibold transition-all">
|
||||||
|
<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="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/>
|
||||||
|
<path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
||||||
|
</svg>
|
||||||
|
Correct a Shot
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -596,6 +593,117 @@ function recordShot(result) {
|
|||||||
.fail(() => alert('Failed to record shot. Please try again.'));
|
.fail(() => alert('Failed to record shot. Please try again.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Phone: allow only digits, +, -, spaces ───────────────────
|
||||||
|
document.getElementById('phone').addEventListener('input', function () {
|
||||||
|
this.value = this.value.replace(/[^0-9+\-\s]/g, '');
|
||||||
|
});
|
||||||
|
document.getElementById('phone').addEventListener('keydown', function (e) {
|
||||||
|
const allowed = ['Backspace','Delete','ArrowLeft','ArrowRight','Tab','Home','End'];
|
||||||
|
if (allowed.includes(e.key)) return;
|
||||||
|
if (!/^[0-9+\-\s]$/.test(e.key)) e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Shot correction — click an already-recorded indicator ────
|
||||||
|
for (let i = 1; i <= 3; i++) {
|
||||||
|
document.getElementById('shotIndicator' + i).addEventListener('click', function () {
|
||||||
|
const shotIndex = i - 1;
|
||||||
|
// Only allow editing shots that have been recorded
|
||||||
|
if (shotResults[shotIndex] === undefined) return;
|
||||||
|
enterCorrectionMode(shotIndex);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('correctShotBtn').addEventListener('click', function () {
|
||||||
|
// Default to correcting the last shot
|
||||||
|
enterCorrectionMode(shotResults.length - 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
function enterCorrectionMode(shotIndex) {
|
||||||
|
const shotNum = shotIndex + 1;
|
||||||
|
|
||||||
|
// Show ready state with correction context
|
||||||
|
document.getElementById('shotReadyState').classList.remove('hidden');
|
||||||
|
document.getElementById('shotDoneState').classList.add('hidden');
|
||||||
|
document.getElementById('currentShotLabel').textContent = 'Correct Shot ' + shotNum + '?';
|
||||||
|
document.getElementById('currentShotLabel').classList.add('text-amber-600');
|
||||||
|
|
||||||
|
// Highlight the indicator being corrected
|
||||||
|
for (let j = 1; j <= 3; j++) {
|
||||||
|
const el = document.getElementById('shotIndicator' + j);
|
||||||
|
if (j === shotNum) {
|
||||||
|
el.className = 'flex-1 h-10 rounded-lg border-2 border-amber-400 bg-amber-50 flex items-center justify-center text-xs font-bold text-amber-600 animate-pulse transition-all';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override goal/miss to correct this specific shot
|
||||||
|
const goalBtn = document.getElementById('goalBtn');
|
||||||
|
const missBtn = document.getElementById('missBtn');
|
||||||
|
|
||||||
|
// Remove old listeners by cloning
|
||||||
|
const newGoalBtn = goalBtn.cloneNode(true);
|
||||||
|
const newMissBtn = missBtn.cloneNode(true);
|
||||||
|
goalBtn.parentNode.replaceChild(newGoalBtn, goalBtn);
|
||||||
|
missBtn.parentNode.replaceChild(newMissBtn, missBtn);
|
||||||
|
|
||||||
|
newGoalBtn.addEventListener('click', () => correctShot(shotIndex, true));
|
||||||
|
newMissBtn.addEventListener('click', () => correctShot(shotIndex, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
function correctShot(shotIndex, result) {
|
||||||
|
if (!selectedSessionId) return;
|
||||||
|
const shotNumber = shotIndex + 1;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '{{ route("registrations.correct-shot") }}',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
_token: '{{ csrf_token() }}',
|
||||||
|
session_id: selectedSessionId,
|
||||||
|
shot_number: shotNumber,
|
||||||
|
result: result ? 1 : 0,
|
||||||
|
},
|
||||||
|
success: function (res) {
|
||||||
|
// Update local state
|
||||||
|
shotResults[shotIndex] = result;
|
||||||
|
|
||||||
|
// Reset button listeners back to normal
|
||||||
|
const goalBtn = document.getElementById('goalBtn');
|
||||||
|
const missBtn = document.getElementById('missBtn');
|
||||||
|
const newGoalBtn = goalBtn.cloneNode(true);
|
||||||
|
const newMissBtn = missBtn.cloneNode(true);
|
||||||
|
goalBtn.parentNode.replaceChild(newGoalBtn, goalBtn);
|
||||||
|
missBtn.parentNode.replaceChild(newMissBtn, missBtn);
|
||||||
|
newGoalBtn.addEventListener('click', () => recordShot(true));
|
||||||
|
newMissBtn.addEventListener('click', () => recordShot(false));
|
||||||
|
|
||||||
|
document.getElementById('currentShotLabel').classList.remove('text-amber-600');
|
||||||
|
currentShot = shotResults.length + 1;
|
||||||
|
refreshPanel();
|
||||||
|
|
||||||
|
// Update scores
|
||||||
|
document.getElementById('panelTotalScore').textContent = res.total_score;
|
||||||
|
if (selectedRow) {
|
||||||
|
selectedRow.dataset.totalScore = res.total_score;
|
||||||
|
selectedRow.dataset.shots = JSON.stringify(shotResults);
|
||||||
|
const scoreCell = selectedRow.querySelector('.score-cell');
|
||||||
|
if (scoreCell) scoreCell.textContent = res.total_score;
|
||||||
|
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('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () { alert('Failed to correct shot. Please try again.'); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('goalBtn').addEventListener('click', () => recordShot(true));
|
document.getElementById('goalBtn').addEventListener('click', () => recordShot(true));
|
||||||
document.getElementById('missBtn').addEventListener('click', () => recordShot(false));
|
document.getElementById('missBtn').addEventListener('click', () => recordShot(false));
|
||||||
document.getElementById('closeShotPanel').addEventListener('click', closeShotPanel);
|
document.getElementById('closeShotPanel').addEventListener('click', closeShotPanel);
|
||||||
@@ -799,33 +907,4 @@ document.getElementById('submitRegistrationBtn').addEventListener('click', funct
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
|
||||||
document.querySelectorAll('.country-select').forEach(sel => {
|
|
||||||
sel.addEventListener('change', function () {
|
|
||||||
const regId = this.dataset.regId;
|
|
||||||
const countryId = this.value;
|
|
||||||
const flagTarget = this.dataset.flagTarget;
|
|
||||||
|
|
||||||
if (!countryId) return;
|
|
||||||
|
|
||||||
// Update flag preview if present
|
|
||||||
if (flagTarget) {
|
|
||||||
const selectedOption = this.options[this.selectedIndex];
|
|
||||||
const flagUrl = selectedOption.dataset.flag;
|
|
||||||
const flagEl = document.getElementById(flagTarget);
|
|
||||||
if (flagEl && flagUrl) {
|
|
||||||
flagEl.src = flagUrl;
|
|
||||||
flagEl.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '{{ route("registrations.update-country", ":id") }}'.replace(':id', regId),
|
|
||||||
method: 'POST',
|
|
||||||
data: { _token: '{{ csrf_token() }}', country_id: countryId },
|
|
||||||
error: function () { alert('Failed to update country.'); }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@endpush
|
@endpush
|
||||||
@@ -34,11 +34,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- TABLE -->
|
<!-- TABLE -->
|
||||||
<div class="bg-white border rounded-xl overflow-hidden">
|
<div class="bg-white border rounded-xl overflow-x-auto">
|
||||||
<table class="w-full text-sm">
|
<table class="min-w-[1200px] w-full text-sm">
|
||||||
<thead class="bg-slate-50 border-b">
|
<thead class="bg-slate-50 border-b">
|
||||||
<tr>
|
<tr>
|
||||||
<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">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">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-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Email</th>
|
||||||
@@ -76,9 +75,6 @@
|
|||||||
data-country-flag="{{ $reg->country?->country_flag ?? '' }}"
|
data-country-flag="{{ $reg->country?->country_flag ?? '' }}"
|
||||||
data-status="{{ $reg->status ?? 'warm' }}"
|
data-status="{{ $reg->status ?? 'warm' }}"
|
||||||
data-comments="{{ json_encode($commentData) }}">
|
data-comments="{{ json_encode($commentData) }}">
|
||||||
<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">
|
<td class="px-5 py-3.5">
|
||||||
<div class="flex items-center gap-3">
|
<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">
|
<div class="w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center shrink-0">
|
||||||
@@ -96,34 +92,64 @@
|
|||||||
src="{{ $reg->country?->country_flag ?? '' }}"
|
src="{{ $reg->country?->country_flag ?? '' }}"
|
||||||
class="w-5 h-3.5 object-cover rounded-sm {{ $reg->country_id ? '' : 'hidden' }}"
|
class="w-5 h-3.5 object-cover rounded-sm {{ $reg->country_id ? '' : 'hidden' }}"
|
||||||
alt="">
|
alt="">
|
||||||
<select class="country-select text-xs border border-slate-200 rounded-lg px-2 py-1.5 bg-white
|
|
||||||
|
@if(auth()->user()->role === 'admin')
|
||||||
|
<select class="country-select text-xs border border-slate-200 rounded-lg px-2 py-1.5 bg-white
|
||||||
text-slate-700 focus:outline-none focus:ring-2 focus:ring-indigo-400 cursor-pointer max-w-[140px]"
|
text-slate-700 focus:outline-none focus:ring-2 focus:ring-indigo-400 cursor-pointer max-w-[140px]"
|
||||||
data-reg-id="{{ $reg->id }}"
|
data-reg-id="{{ $reg->id }}"
|
||||||
data-flag-target="flag-{{ $reg->id }}">
|
data-flag-target="flag-{{ $reg->id }}">
|
||||||
<option value="">— Select —</option>
|
<option value="">— Select —</option>
|
||||||
@foreach($countries as $country)
|
@foreach($countries as $country)
|
||||||
<option value="{{ $country->id }}"
|
<option value="{{ $country->id }}"
|
||||||
data-flag="{{ $country->country_flag }}"
|
data-flag="{{ $country->country_flag }}"
|
||||||
{{ $reg->country_id == $country->id ? 'selected' : '' }}>
|
{{ $reg->country_id == $country->id ? 'selected' : '' }}>
|
||||||
{{ $country->title }}
|
{{ $country->title }}
|
||||||
</option>
|
</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
|
@else
|
||||||
|
|
||||||
|
<span class="text-sm text-slate-600">
|
||||||
|
{{ $reg->country?->title ?? '—' }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{{-- Status TD (read-only badge) --}}
|
{{-- Status TD (read-only badge) --}}
|
||||||
<td class="px-5 py-3.5 text-center" onclick="event.stopPropagation()">
|
<td class="px-5 py-3.5 text-center" onclick="event.stopPropagation()">
|
||||||
|
|
||||||
@php $status = $reg->status ?? 'warm'; @endphp
|
@php $status = $reg->status ?? 'warm'; @endphp
|
||||||
<span class="text-xs font-semibold rounded-full px-2.5 py-1
|
|
||||||
{{ match($status) {
|
@if(auth()->user()->role === 'counselor')
|
||||||
'hot' => 'bg-red-50 text-red-600',
|
|
||||||
'warm' => 'bg-amber-50 text-amber-600',
|
<select class="status-select text-xs px-2 py-1 rounded border cursor-pointer"
|
||||||
'cold' => 'bg-blue-50 text-blue-600',
|
data-reg-id="{{ $reg->id }}">
|
||||||
default => 'bg-amber-50 text-amber-600',
|
<option value="hot" {{ $status=='hot'?'selected':'' }}>🔥 Hot</option>
|
||||||
} }}">
|
<option value="warm" {{ $status=='warm'?'selected':'' }}>☀️ Warm</option>
|
||||||
{{ match($status) { 'hot' => '🔥 Hot', 'warm' => '☀️ Warm', 'cold' => '❄️ Cold', default => '☀️ Warm' } }}
|
<option value="cold" {{ $status=='cold'?'selected':'' }}>❄️ Cold</option>
|
||||||
</span>
|
</select>
|
||||||
|
|
||||||
|
@else
|
||||||
|
|
||||||
|
<span class="text-xs font-semibold rounded-full px-2.5 py-1
|
||||||
|
{{ match($status) {
|
||||||
|
'hot' => 'bg-red-50 text-red-600',
|
||||||
|
'warm' => 'bg-amber-50 text-amber-600',
|
||||||
|
'cold' => 'bg-blue-50 text-blue-600',
|
||||||
|
default => 'bg-amber-50 text-amber-600'
|
||||||
|
} }}">
|
||||||
|
{{ match($status) {
|
||||||
|
'hot' => '🔥 Hot',
|
||||||
|
'warm' => '☀️ Warm',
|
||||||
|
'cold' => '❄️ Cold',
|
||||||
|
default => '☀️ Warm'
|
||||||
|
} }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
@endif
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="px-5 py-3.5 text-center">
|
<td class="px-5 py-3.5 text-center">
|
||||||
@@ -433,4 +459,46 @@ document.querySelectorAll('.country-select').forEach(sel => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
const statusColors = {
|
||||||
|
hot: 'bg-red-50 text-red-600 border-red-200',
|
||||||
|
warm: 'bg-amber-50 text-amber-600 border-amber-200',
|
||||||
|
cold: 'bg-blue-50 text-blue-600 border-blue-200',
|
||||||
|
};
|
||||||
|
|
||||||
|
document.querySelectorAll('.status-select').forEach(sel => {
|
||||||
|
|
||||||
|
const current = sel.value;
|
||||||
|
|
||||||
|
if(statusColors[current]){
|
||||||
|
statusColors[current].split(' ').forEach(cls => sel.classList.add(cls));
|
||||||
|
}
|
||||||
|
|
||||||
|
sel.addEventListener('change', function () {
|
||||||
|
|
||||||
|
const regId = this.dataset.regId;
|
||||||
|
const status = this.value;
|
||||||
|
|
||||||
|
Object.values(statusColors).forEach(c =>
|
||||||
|
c.split(' ').forEach(cls => this.classList.remove(cls))
|
||||||
|
);
|
||||||
|
|
||||||
|
statusColors[status].split(' ').forEach(cls =>
|
||||||
|
this.classList.add(cls)
|
||||||
|
);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '{{ route("registrations.update-status", ":id") }}'.replace(':id', regId),
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
_token: '{{ csrf_token() }}',
|
||||||
|
status: status
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
alert('Failed to update status.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@endpush
|
@endpush
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
<div class="logo-wrap"><img src="assets/rosetta-logo.png" alt="Rosetta Education International" /></div>
|
<div class="logo-wrap"><img src="assets/rosetta-logo.png" alt="Rosetta Education International" /></div>
|
||||||
<div class="title-badge">
|
<div class="title-badge">
|
||||||
<div class="title">SC⚽RE <span>& WIN</span></div>
|
<div class="title">SC⚽RE <span>& WIN</span></div>
|
||||||
<div class="sub">CHALLENGE 2025</div>
|
<div class="sub">CHALLENGE 2026</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="passion">
|
<div class="passion">
|
||||||
<div class="cup">🏆</div>
|
<div class="cup">🏆</div>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ Route::middleware('auth')->group(function () {
|
|||||||
Route::post('/verify-otp', [RegistrationController::class, 'verifyOtp'])->name('verify-otp');
|
Route::post('/verify-otp', [RegistrationController::class, 'verifyOtp'])->name('verify-otp');
|
||||||
Route::get('/{id}/json', [RegistrationController::class, 'getRegistrationJson'])->name('get-json');
|
Route::get('/{id}/json', [RegistrationController::class, 'getRegistrationJson'])->name('get-json');
|
||||||
Route::post('/record-shot', [RegistrationController::class, 'recordShot'])->name('record-shot');
|
Route::post('/record-shot', [RegistrationController::class, 'recordShot'])->name('record-shot');
|
||||||
|
Route::post('/correct-shot', [RegistrationController::class, 'correctShot'])->name('correct-shot');
|
||||||
Route::post('/record-shots', [RegistrationController::class, 'recordShots'])->name('record-shots');
|
Route::post('/record-shots', [RegistrationController::class, 'recordShots'])->name('record-shots');
|
||||||
Route::get('/{id}/history', [RegistrationController::class, 'history'])->name('history');
|
Route::get('/{id}/history', [RegistrationController::class, 'history'])->name('history');
|
||||||
Route::post('/{id}/country', [RegistrationController::class, 'updateCountry'])->name('update-country');
|
Route::post('/{id}/country', [RegistrationController::class, 'updateCountry'])->name('update-country');
|
||||||
|
|||||||
Reference in New Issue
Block a user