first commit
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Auth\LoginRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class AuthenticatedSessionController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the login view.
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
return view('auth.login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming authentication request.
|
||||
*/
|
||||
public function store(LoginRequest $request): RedirectResponse
|
||||
{
|
||||
$request->authenticate();
|
||||
|
||||
$request->session()->regenerate();
|
||||
|
||||
return redirect()->intended(route('home', absolute: false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an authenticated session.
|
||||
*/
|
||||
public function destroy(Request $request): RedirectResponse
|
||||
{
|
||||
Auth::guard('web')->logout();
|
||||
|
||||
$request->session()->invalidate();
|
||||
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ConfirmablePasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the confirm password view.
|
||||
*/
|
||||
public function show(): View
|
||||
{
|
||||
return view('auth.confirm-password');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the user's password.
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
if (! Auth::guard('web')->validate([
|
||||
'email' => $request->user()->email,
|
||||
'password' => $request->password,
|
||||
])) {
|
||||
throw ValidationException::withMessages([
|
||||
'password' => __('auth.password'),
|
||||
]);
|
||||
}
|
||||
|
||||
$request->session()->put('auth.password_confirmed_at', time());
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class EmailVerificationNotificationController extends Controller
|
||||
{
|
||||
/**
|
||||
* Send a new email verification notification.
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
if ($request->user()->hasVerifiedEmail()) {
|
||||
return redirect()->intended(route('dashboard', absolute: false));
|
||||
}
|
||||
|
||||
$request->user()->sendEmailVerificationNotification();
|
||||
|
||||
return back()->with('status', 'verification-link-sent');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class EmailVerificationPromptController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the email verification prompt.
|
||||
*/
|
||||
public function __invoke(Request $request): RedirectResponse|View
|
||||
{
|
||||
return $request->user()->hasVerifiedEmail()
|
||||
? redirect()->intended(route('dashboard', absolute: false))
|
||||
: view('auth.verify-email');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rules;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class NewPasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the password reset view.
|
||||
*/
|
||||
public function create(Request $request): View
|
||||
{
|
||||
return view('auth.reset-password', ['request' => $request]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming new password request.
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'token' => ['required'],
|
||||
'email' => ['required', 'email'],
|
||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||
]);
|
||||
|
||||
// Here we will attempt to reset the user's password. If it is successful we
|
||||
// will update the password on an actual user model and persist it to the
|
||||
// database. Otherwise we will parse the error and return the response.
|
||||
$status = Password::reset(
|
||||
$request->only('email', 'password', 'password_confirmation', 'token'),
|
||||
function (User $user) use ($request) {
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($request->password),
|
||||
'remember_token' => Str::random(60),
|
||||
])->save();
|
||||
|
||||
event(new PasswordReset($user));
|
||||
}
|
||||
);
|
||||
|
||||
// If the password was successfully reset, we will redirect the user back to
|
||||
// the application's home authenticated view. If there is an error we can
|
||||
// redirect them back to where they came from with their error message.
|
||||
return $status == Password::PASSWORD_RESET
|
||||
? redirect()->route('login')->with('status', __($status))
|
||||
: back()->withInput($request->only('email'))
|
||||
->withErrors(['email' => __($status)]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
class PasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Update the user's password.
|
||||
*/
|
||||
public function update(Request $request): RedirectResponse
|
||||
{
|
||||
$validated = $request->validateWithBag('updatePassword', [
|
||||
'current_password' => ['required', 'current_password'],
|
||||
'password' => ['required', Password::defaults(), 'confirmed'],
|
||||
]);
|
||||
|
||||
$request->user()->update([
|
||||
'password' => Hash::make($validated['password']),
|
||||
]);
|
||||
|
||||
return back()->with('status', 'password-updated');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class PasswordResetLinkController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the password reset link request view.
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
return view('auth.forgot-password');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming password reset link request.
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'email' => ['required', 'email'],
|
||||
]);
|
||||
|
||||
// We will send the password reset link to this user. Once we have attempted
|
||||
// to send the link, we will examine the response then see the message we
|
||||
// need to show to the user. Finally, we'll send out a proper response.
|
||||
$status = Password::sendResetLink(
|
||||
$request->only('email')
|
||||
);
|
||||
|
||||
return $status == Password::RESET_LINK_SENT
|
||||
? back()->with('status', __($status))
|
||||
: back()->withInput($request->only('email'))
|
||||
->withErrors(['email' => __($status)]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class RegisteredUserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the registration view.
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
return view('auth.register');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming registration request.
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
|
||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
]);
|
||||
|
||||
event(new Registered($user));
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
return redirect(route('dashboard', absolute: false));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Auth\Events\Verified;
|
||||
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class VerifyEmailController extends Controller
|
||||
{
|
||||
/**
|
||||
* Mark the authenticated user's email address as verified.
|
||||
*/
|
||||
public function __invoke(EmailVerificationRequest $request): RedirectResponse
|
||||
{
|
||||
if ($request->user()->hasVerifiedEmail()) {
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
}
|
||||
|
||||
if ($request->user()->markEmailAsVerified()) {
|
||||
event(new Verified($request->user()));
|
||||
}
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Comment;
|
||||
use App\Models\Registration;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CommentController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$request->validate(['registration_id' => 'required|exists:registrations,id']);
|
||||
|
||||
$comments = Comment::where('registration_id', $request->registration_id)
|
||||
->orderBy('created_at', 'asc')
|
||||
->get()
|
||||
->map(fn($c) => [
|
||||
'comment' => $c->comment,
|
||||
'author' => auth()->user()->name,
|
||||
'created_at_human' => $c->created_at->diffForHumans(),
|
||||
]);
|
||||
|
||||
return response()->json(['comments' => $comments]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'registration_id' => 'required|exists:registrations,id',
|
||||
'comment' => 'required|string|max:1000',
|
||||
]);
|
||||
|
||||
Registration::findOrFail($request->registration_id);
|
||||
|
||||
$comment = Comment::create([
|
||||
'registration_id' => $request->registration_id,
|
||||
'comment' => $request->comment,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'comment' => [
|
||||
'comment' => $comment->comment,
|
||||
'author' => auth()->user()->name,
|
||||
'created_at_human' => $comment->created_at->diffForHumans(),
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Registration;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
return match ($user->role) {
|
||||
'admin' => $this->adminDashboard(),
|
||||
'counselor' => $this->counselorDashboard(),
|
||||
default => abort(403, 'Unauthorized action.'),
|
||||
};
|
||||
}
|
||||
|
||||
public function adminDashboard()
|
||||
{
|
||||
$today = Carbon::today();
|
||||
|
||||
$query = Registration::with([
|
||||
'sessions' => function ($q) use ($today) {
|
||||
$q->whereDate('play_date', $today)->with('shots');
|
||||
}
|
||||
])
|
||||
->whereHas('sessions', function ($q) use ($today) {
|
||||
$q->whereDate('play_date', $today);
|
||||
})
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(10);
|
||||
|
||||
$registrations = $query->getCollection()->map(function ($student) {
|
||||
|
||||
$todaySession = $student->sessions->first();
|
||||
|
||||
$todayGoals = null;
|
||||
|
||||
if ($todaySession) {
|
||||
$todayGoals = $todaySession->shots
|
||||
->sortBy('shot_number')
|
||||
->map(fn($shot) => (bool) $shot->result)
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $student->id,
|
||||
'name' => $student->name,
|
||||
'phone' => $student->phone,
|
||||
'email' => $student->email,
|
||||
'today_goals' => $todayGoals,
|
||||
'total_score' => $student->total_score,
|
||||
'session_id' => $todaySession?->id,
|
||||
];
|
||||
});
|
||||
|
||||
$query->setCollection($registrations);
|
||||
|
||||
$data['registrations'] = $query;
|
||||
$data['total'] = Registration::count();
|
||||
$data['played'] = $registrations->filter(fn($r) => !empty($r['today_goals']) && count($r['today_goals']) >= 3)->count();
|
||||
$data['topScore'] = $registrations->max('total_score') ?? 0;
|
||||
|
||||
return view('dashboard.admin', $data);
|
||||
}
|
||||
|
||||
public function counselorDashboard()
|
||||
{
|
||||
$today = Carbon::today();
|
||||
|
||||
$query = Registration::with([
|
||||
'sessions' => function ($q) use ($today) {
|
||||
$q->whereDate('play_date', $today)->with('shots');
|
||||
},
|
||||
'comments'
|
||||
])
|
||||
->whereHas('sessions', function ($q) use ($today) {
|
||||
$q->whereDate('play_date', $today);
|
||||
})
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(10);
|
||||
|
||||
$registrations = $query->getCollection()->map(function ($student) {
|
||||
|
||||
$todaySession = $student->sessions->first();
|
||||
|
||||
$todayGoals = null;
|
||||
|
||||
if ($todaySession) {
|
||||
$todayGoals = $todaySession->shots
|
||||
->sortBy('shot_number')
|
||||
->map(fn($shot) => (bool) $shot->result)
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $student->id,
|
||||
'name' => $student->name,
|
||||
'phone' => $student->phone,
|
||||
'email' => $student->email,
|
||||
'today_goals' => $todayGoals,
|
||||
'total_score' => $student->total_score,
|
||||
'session_id' => $todaySession?->id,
|
||||
'comment_count' => $student->comments->count(),
|
||||
];
|
||||
});
|
||||
|
||||
$query->setCollection($registrations);
|
||||
|
||||
$data['registrations'] = $query;
|
||||
$data['total'] = Registration::count();
|
||||
$data['played'] = $registrations->filter(fn($r) => !empty($r['today_goals']) && count($r['today_goals']) >= 3)->count();
|
||||
$data['topScore'] = $registrations->max('total_score') ?? 0;
|
||||
|
||||
return view('dashboard.counselor', $data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ProfileUpdateRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the user's profile form.
|
||||
*/
|
||||
public function edit(Request $request): View
|
||||
{
|
||||
return view('profile.edit', [
|
||||
'user' => $request->user(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user's profile information.
|
||||
*/
|
||||
public function update(ProfileUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$request->user()->fill($request->validated());
|
||||
|
||||
if ($request->user()->isDirty('email')) {
|
||||
$request->user()->email_verified_at = null;
|
||||
}
|
||||
|
||||
$request->user()->save();
|
||||
|
||||
return Redirect::route('profile.edit')->with('status', 'profile-updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the user's account.
|
||||
*/
|
||||
public function destroy(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validateWithBag('userDeletion', [
|
||||
'password' => ['required', 'current_password'],
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
Auth::logout();
|
||||
|
||||
$user->delete();
|
||||
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return Redirect::to('/');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\GameSession;
|
||||
use App\Models\GameShot;
|
||||
use App\Models\Registration;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class RegistrationController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$registrations = Registration::orderBy('id', 'desc')->paginate(20);
|
||||
|
||||
return view('registrations', compact('registrations'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return view('registrations.create');
|
||||
}
|
||||
|
||||
/*
|
||||
|-------------------------------------------------
|
||||
| STORE (ONLY USED AFTER OTP FOR NEW USERS)
|
||||
|-------------------------------------------------
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'phone' => 'required',
|
||||
'name' => 'required',
|
||||
'email' => 'nullable|email'
|
||||
]);
|
||||
|
||||
$registration = Registration::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'phone' => $request->phone,
|
||||
'total_score' => 0
|
||||
]);
|
||||
|
||||
$session = GameSession::create([
|
||||
'registration_id' => $registration->id,
|
||||
'play_date' => today(),
|
||||
'score' => 0,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'session_created',
|
||||
'registration' => [
|
||||
'id' => $registration->id,
|
||||
'name' => $registration->name,
|
||||
'email' => $registration->email,
|
||||
'phone' => $registration->phone,
|
||||
'total_score' => 0,
|
||||
'today_goals' => [],
|
||||
'session_id' => $session->id,
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/*
|
||||
|-------------------------------------------------
|
||||
| SEND OTP
|
||||
|-------------------------------------------------
|
||||
*/
|
||||
public function sendOtp(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'phone' => 'required'
|
||||
]);
|
||||
|
||||
$phone = $request->phone;
|
||||
|
||||
$otp = rand(100000, 999999);
|
||||
|
||||
Cache::put("otp_$phone", $otp, now()->addMinutes(2));
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'otp' => $otp // remove in production
|
||||
]);
|
||||
}
|
||||
|
||||
/*
|
||||
|-------------------------------------------------
|
||||
| VERIFY OTP (CORE LOGIC)
|
||||
|-------------------------------------------------
|
||||
*/
|
||||
public function verifyOtp(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'phone' => 'required',
|
||||
'otp' => 'required'
|
||||
]);
|
||||
|
||||
$phone = $request->phone;
|
||||
|
||||
$cachedOtp = Cache::get("otp_$phone");
|
||||
|
||||
if (!$cachedOtp || $cachedOtp != $request->otp) {
|
||||
return response()->json([
|
||||
'status' => 'invalid_otp',
|
||||
'message' => 'Invalid OTP'
|
||||
], 422);
|
||||
}
|
||||
|
||||
Cache::forget("otp_$phone");
|
||||
|
||||
$registration = Registration::where('phone', $phone)->first();
|
||||
|
||||
/*
|
||||
| CASE 1: NEW USER
|
||||
*/
|
||||
if (!$registration) {
|
||||
return response()->json([
|
||||
'status' => 'needs_registration',
|
||||
'phone' => $phone
|
||||
]);
|
||||
}
|
||||
|
||||
/*
|
||||
| CASE 2: ALREADY PLAYED TODAY
|
||||
*/
|
||||
$todaySession = $registration->sessions()
|
||||
->whereDate('play_date', today())
|
||||
->first();
|
||||
|
||||
if ($todaySession && $todaySession->isComplete()) {
|
||||
return response()->json([
|
||||
'status' => 'blocked',
|
||||
'message' => 'This student has already completed today\'s session.',
|
||||
], 409);
|
||||
}
|
||||
|
||||
// Session exists but shots not recorded yet — return it
|
||||
if ($todaySession) {
|
||||
return response()->json([
|
||||
'status' => 'session_created',
|
||||
'registration' => [
|
||||
'id' => $registration->id,
|
||||
'name' => $registration->name,
|
||||
'email' => $registration->email,
|
||||
'phone' => $registration->phone,
|
||||
'total_score' => $registration->total_score,
|
||||
'today_goals' => $todaySession->getShotArray(),
|
||||
'session_id' => $todaySession->id,
|
||||
]
|
||||
]);
|
||||
}
|
||||
/*
|
||||
| CASE 3: CREATE SESSION IMMEDIATELY
|
||||
*/
|
||||
$session = GameSession::create([
|
||||
'registration_id' => $registration->id,
|
||||
'play_date' => today(),
|
||||
'score' => 0,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'session_created',
|
||||
'registration' => [
|
||||
'id' => $registration->id,
|
||||
'name' => $registration->name,
|
||||
'email' => $registration->email,
|
||||
'phone' => $registration->phone,
|
||||
'total_score' => $registration->total_score,
|
||||
'today_goals' => [],
|
||||
'session_id' => $session->id,
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function resendOtp(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'phone' => 'required'
|
||||
]);
|
||||
|
||||
$phone = $request->phone;
|
||||
|
||||
$cooldownKey = 'otp_cooldown_' . $phone;
|
||||
|
||||
if (Cache::has($cooldownKey)) {
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => 'Please wait before resending OTP'
|
||||
], 429);
|
||||
}
|
||||
|
||||
$otp = rand(100000, 999999);
|
||||
|
||||
Cache::put("otp_$phone", $otp, now()->addMinutes(2));
|
||||
Cache::put($cooldownKey, true, now()->addSeconds(30));
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'message' => 'OTP resent successfully',
|
||||
'otp' => $otp
|
||||
]);
|
||||
}
|
||||
|
||||
public function getRegistrationJson($id)
|
||||
{
|
||||
$registration = Registration::with('sessions.shots')->findOrFail($id);
|
||||
|
||||
return response()->json([
|
||||
'registration' => [
|
||||
'id' => $registration->id,
|
||||
'name' => $registration->name,
|
||||
'email' => $registration->email,
|
||||
'phone' => $registration->phone,
|
||||
'total_score' => $registration->total_score,
|
||||
'today_goals' => optional(
|
||||
$registration->sessions()->whereDate('play_date', today())->first()
|
||||
)?->getShotArray()
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function recordShot(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);
|
||||
|
||||
if ($session->shots()->where('shot_number', $request->shot_number)->exists()) {
|
||||
return response()->json(['status' => 'error', 'message' => 'Shot already recorded.'], 409);
|
||||
}
|
||||
|
||||
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,
|
||||
]);
|
||||
}
|
||||
|
||||
public function recordShots(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'session_id' => 'required|exists:game_sessions,id',
|
||||
'shots' => 'required|array|size:3',
|
||||
'shots.*' => 'boolean',
|
||||
]);
|
||||
|
||||
$session = GameSession::with('registration')->findOrFail($request->session_id);
|
||||
|
||||
// Prevent re-recording
|
||||
if ($session->isComplete()) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => 'Shots already recorded for this session.'
|
||||
], 409);
|
||||
}
|
||||
|
||||
// Store the 3 shots
|
||||
foreach ($request->shots as $index => $result) {
|
||||
GameShot::create([
|
||||
'game_session_id' => $session->id,
|
||||
'shot_number' => $index + 1,
|
||||
'result' => $result,
|
||||
]);
|
||||
}
|
||||
|
||||
// Recalculate and update scores
|
||||
$sessionScore = $session->calculateScore();
|
||||
$session->update(['score' => $sessionScore]);
|
||||
|
||||
$registration = $session->registration;
|
||||
$registration->increment('total_score', $sessionScore);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'ok',
|
||||
'session_score' => $sessionScore,
|
||||
'total_score' => $registration->fresh()->total_score,
|
||||
]);
|
||||
}
|
||||
|
||||
//Leaderboard
|
||||
public function leaderboard()
|
||||
{
|
||||
$students = Registration::withCount('sessions')
|
||||
->orderByDesc('total_score')
|
||||
->orderBy('name')
|
||||
->paginate(20);
|
||||
|
||||
$maxScore = Registration::max('total_score') ?? 0;
|
||||
$total = Registration::count();
|
||||
|
||||
$data['students'] = $students;
|
||||
$data['maxScore'] = $maxScore;
|
||||
$data['total'] = $total;
|
||||
|
||||
return view('leaderboard', $data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Registration;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ScoreboardController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return view('scoreboard');
|
||||
}
|
||||
|
||||
public function select(Request $request)
|
||||
{
|
||||
$request->validate(['registration_id' => 'required|exists:registrations,id']);
|
||||
session(['scoreboard_player_id' => $request->registration_id]);
|
||||
return response()->json(['status' => 'ok']);
|
||||
}
|
||||
|
||||
public function state()
|
||||
{
|
||||
$id = session('scoreboard_player_id');
|
||||
|
||||
if (!$id) {
|
||||
return response()->json(['player' => null, 'leaderboard' => $this->leaderboard()]);
|
||||
}
|
||||
|
||||
$reg = Registration::with([
|
||||
'sessions' => fn($q) => $q->whereDate('play_date', today())->with('shots')
|
||||
])->findOrFail($id);
|
||||
|
||||
$todaySession = $reg->sessions->first();
|
||||
$shots = $todaySession
|
||||
? $todaySession->shots->sortBy('shot_number')->map(fn($s) => (bool)$s->result)->values()->toArray()
|
||||
: [];
|
||||
|
||||
return response()->json([
|
||||
'player' => [
|
||||
'id' => $reg->id,
|
||||
'name' => $reg->name,
|
||||
'total_score' => $reg->total_score,
|
||||
'shots' => $shots,
|
||||
'session_score' => $todaySession?->calculateScore() ?? 0,
|
||||
],
|
||||
'leaderboard' => $this->leaderboard(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function leaderboard(): array
|
||||
{
|
||||
return Registration::orderByDesc('total_score')
|
||||
->limit(5)
|
||||
->get(['id', 'name', 'total_score'])
|
||||
->map(fn($r, $i) => [
|
||||
'rank' => $i + 1,
|
||||
'name' => $r->name,
|
||||
'total_score' => $r->total_score,
|
||||
])
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user