use Illuminate\Support\Facades\Route; use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Hash; use App\Http\Controllers\AuthController; use App\Http\Controllers\SuperAdminController; use App\Http\Controllers\MembershipController; use App\Http\Controllers\CustomerController; use App\Http\Controllers\TransactionController; use App\Http\Controllers\AdminController; use App\Rules\NotDisposableEmail; Route::get('/', function () { return view('welcome'); })->name('home')->middleware('guest'); // Authentication Routes Route::get('/login', [AuthController::class, 'showLogin'])->name('login')->middleware('guest'); Route::post('/login', [AuthController::class, 'login']); Route::get('/signup', [AuthController::class, 'showSignup'])->name('signup')->middleware('guest'); Route::post('/signup', [AuthController::class, 'signup']); Route::post('/logout', [AuthController::class, 'logout'])->name('logout'); // Change password route for first-time users Route::post('/change-password', function (Request $request) { $request->validate([ 'current_password' => 'required', 'new_password' => 'required|string|min:8|confirmed', ]); $user = Auth::user(); // Check if current password is the default password if (!Hash::check($request->current_password, $user->password)) { return back()->withErrors(['current_password' => 'Current password is incorrect.']); } // Update password $user->update([ 'password' => Hash::make($request->new_password) ]); return back()->with('success', 'Password changed successfully!'); })->name('change-password')->middleware('auth'); // Password Reset Routes Route::get('/forgot-password', [AuthController::class, 'showForgotPassword'])->name('password.request')->middleware('guest'); Route::post('/forgot-password', [AuthController::class, 'sendResetLinkEmail'])->name('password.email'); Route::get('/reset-password/{token}', [AuthController::class, 'showResetPassword'])->name('password.reset')->middleware('guest'); Route::post('/reset-password', [AuthController::class, 'resetPassword'])->name('password.update'); // Public RFID Routes (No authentication required) Route::post('/rfid/entry', function (Request $request) { // Log the request for debugging \Log::info('RFID Entry Request', [ 'rfid_number' => $request->rfid_number, 'headers' => $request->headers->all(), 'ip' => $request->ip() ]); $request->validate([ 'rfid_number' => 'required|string|max:255' ]); $user = \App\Models\User::where('rfid_number', $request->rfid_number)->first(); if (!$user) { \Log::warning('RFID card not found', ['rfid_number' => $request->rfid_number]); return response()->json([ 'success' => false, 'message' => 'RFID card not found' ], 404); } \Log::info('User found for RFID', [ 'user_id' => $user->id, 'user_name' => $user->full_name, 'has_active_membership' => $user->hasActiveMembership() ]); // Check if user has active membership if (!$user->hasActiveMembership()) { \Log::warning('No active membership found', ['user_id' => $user->id]); return response()->json([ 'success' => false, 'message' => 'No active membership found' ], 403); } // Check if user is already checked in today $existingAttendance = \App\Models\Attendance::where('user_id', $user->id) ->whereDate('entry_time', today()) ->where('status', 'present') ->first(); if ($existingAttendance) { \Log::info('User already checked in', [ 'user_id' => $user->id, 'attendance_id' => $existingAttendance->id ]); return response()->json([ 'success' => true, 'message' => 'User is already checked in', 'user_name' => $user->full_name, 'entry_time' => $existingAttendance->entry_time->format('H:i:s'), 'already_checked_in' => true ]); } // Create entry record try { $attendance = \App\Models\Attendance::create([ 'user_id' => $user->id, 'entry_time' => now(), 'status' => 'present' ]); \Log::info('Attendance record created successfully', [ 'attendance_id' => $attendance->id, 'user_id' => $user->id, 'entry_time' => $attendance->entry_time ]); return response()->json([ 'success' => true, 'message' => 'Entry recorded successfully', 'user_name' => $user->full_name, 'entry_time' => $attendance->entry_time->format('H:i:s') ]); } catch (\Exception $e) { \Log::error('Error creating attendance record', [ 'user_id' => $user->id, 'error' => $e->getMessage() ]); return response()->json([ 'success' => false, 'message' => 'Error creating attendance record: ' . $e->getMessage() ], 500); } })->name('rfid.entry'); // Automatic RFID entry only Route::post('/rfid/automatic', function (Request $request) { $request->validate([ 'rfid_number' => 'required|string|max:255' ]); $user = \App\Models\User::where('rfid_number', $request->rfid_number)->first(); if (!$user) { return response()->json([ 'success' => false, 'message' => 'RFID card not found' ], 404); } // Check if user has active membership if (!$user->hasActiveMembership()) { return response()->json([ 'success' => false, 'message' => 'No active membership found' ], 403); } // Get Manila timezone $manilaTime = now()->setTimezone('Asia/Manila'); $today = $manilaTime->format('Y-m-d'); // Check for existing attendance record today $existingAttendance = \App\Models\Attendance::where('user_id', $user->id) ->whereDate('entry_time', $today) ->first(); if ($existingAttendance) { // User already has attendance record today return response()->json([ 'success' => true, 'message' => 'Already checked in today', 'user_name' => $user->full_name, 'time' => $existingAttendance->entry_time->format('H:i:s') ]); } else { // Create new entry record $attendance = \App\Models\Attendance::create([ 'user_id' => $user->id, 'entry_time' => $manilaTime, 'status' => 'present' ]); return response()->json([ 'success' => true, 'message' => 'Entry recorded successfully', 'user_name' => $user->full_name, 'time' => $manilaTime->format('H:i:s') ]); } })->name('rfid.automatic'); // RFID Reader Interface Route (Public - No login required) Route::get('/rfid-reader', function () { return view('rfid-reader'); })->name('rfid.reader'); // Secure RFID Reader Route (with basic auth) Route::get('/rfid-kiosk', function () { return view('rfid-reader'); })->name('rfid.kiosk')->middleware('auth'); // Face Recognition Kiosk Route (Public - No login required) Route::get('/face-recognition-kiosk', function () { return view('face-recognition-kiosk'); })->name('face.kiosk'); // Public Face Recognition Check-in (for kiosk) Route::post('/face/check-in', [App\Http\Controllers\FaceRecognitionController::class, 'checkIn'])->name('face.check-in'); // Membership Selection Routes (after signup) Route::middleware('auth')->group(function () { Route::get('/membership/select', [MembershipController::class, 'showMembershipSelection'])->name('membership.select'); Route::post('/membership/select', [MembershipController::class, 'processMembershipSelection'])->name('membership.select.process'); Route::get('/membership/payment', [MembershipController::class, 'showPayment'])->name('membership.payment'); Route::post('/membership/payment', [MembershipController::class, 'processPayment'])->name('membership.payment.process'); Route::get('/membership/success', [MembershipController::class, 'showSuccess'])->name('membership.success'); Route::get('/membership/qr-payment', [MembershipController::class, 'showQRPayment'])->name('membership.qr-payment'); Route::post('/membership/verify-gcash', [MembershipController::class, 'verifyGCashPayment'])->name('membership.verify-gcash'); Route::get('/membership/cash-payment', [MembershipController::class, 'showCashPayment'])->name('membership.cash-payment'); Route::post('/membership/complete-cash', [MembershipController::class, 'completeCashPayment'])->name('membership.complete-cash'); Route::get('/membership/receipt/{id}', [MembershipController::class, 'showReceipt'])->name('membership.receipt'); Route::get('/membership/receipt/{id}/pdf', [MembershipController::class, 'downloadReceiptPDF'])->name('membership.receipt.pdf'); }); // Protected Routes Route::middleware('auth')->group(function () { // Super Admin Routes Route::middleware('role:super_admin')->group(function () { Route::get('/super-admin/dashboard', [SuperAdminController::class, 'dashboard'])->name('superadmin.dashboard'); Route::post('/super-admin/admins', [SuperAdminController::class, 'storeAdmin'])->name('superadmin.admins.store'); }); // Admin Routes Route::middleware('role:admin')->group(function () { Route::get('/admin/dashboard', function () { return view('admin.dashboard'); })->name('admin.dashboard'); // Admin Profile and Settings Routes Route::get('/admin/ajax/profile', [AdminController::class, 'showProfile'])->name('admin.ajax.profile'); Route::put('/admin/profile', [AdminController::class, 'updateProfile'])->name('admin.profile.update'); Route::get('/admin/ajax/settings', [AdminController::class, 'showSettings'])->name('admin.ajax.settings'); Route::put('/admin/password', [AdminController::class, 'updatePassword'])->name('admin.password.update'); // AJAX routes for dynamic content loading Route::get('/admin/ajax/dashboard', [AdminController::class, 'showDashboard'])->name('admin.ajax.dashboard'); Route::get('/admin/ajax/transactions', function () { $memberships = \App\Models\Membership::with('user')->orderBy('created_at', 'desc')->get(); // Calculate statistics $stats = [ 'total' => $memberships->count(), 'confirmed' => $memberships->where('status', 'active')->count(), 'pending' => $memberships->where('status', 'pending')->count(), 'totalRevenue' => $memberships->sum('amount_paid'), ]; return view('admin.ajax.transactions', compact('memberships', 'stats')); })->name('admin.ajax.transactions'); // Walk-in management (walk-in memberships only) Route::get('/admin/ajax/walkin', function () { $memberships = \App\Models\Membership::with('user') ->whereIn('membership_type', ['student_walkin', 'regular_walkin']) ->orderBy('created_at', 'desc') ->get(); $stats = [ 'total' => $memberships->count(), 'confirmed' => $memberships->where('status', 'active')->count(), 'pending' => $memberships->where('status', 'pending')->count(), 'totalRevenue' => $memberships->sum('amount_paid'), ]; return view('admin.ajax.walkin', compact('memberships', 'stats')); })->name('admin.ajax.walkin'); // RFID Management Routes Route::post('/admin/assign-rfid', function (Request $request) { $request->validate([ 'user_id' => 'required|exists:users,id', 'rfid_number' => 'required|string|max:255|unique:users,rfid_number,' . $request->user_id ]); $user = \App\Models\User::find($request->user_id); $user->rfid_number = $request->rfid_number; $user->save(); return response()->json([ 'success' => true, 'message' => 'RFID card assigned successfully' ]); })->name('admin.assign-rfid'); Route::post('/admin/update-rfid', function (Request $request) { $request->validate([ 'user_id' => 'required|exists:users,id', 'new_rfid_number' => 'required|string|max:255|unique:users,rfid_number,' . $request->user_id, 'current_rfid_number' => 'required|string|max:255' ]); $user = \App\Models\User::find($request->user_id); // Check if user has an RFID card to update if (!$user->rfid_number) { return response()->json([ 'success' => false, 'message' => 'User does not have an RFID card assigned' ], 400); } // Verify current RFID matches if ($user->rfid_number !== $request->current_rfid_number) { return response()->json([ 'success' => false, 'message' => 'Current RFID number does not match' ], 400); } // Check if new RFID is the same as current if ($user->rfid_number === $request->new_rfid_number) { return response()->json([ 'success' => false, 'message' => 'New RFID number is the same as current RFID number' ], 400); } // Update the RFID number $user->rfid_number = $request->new_rfid_number; $user->save(); return response()->json([ 'success' => true, 'message' => 'RFID card updated successfully' ]); })->name('admin.update-rfid'); // SMS notification route Route::post('/admin/send-sms-notification', function (Request $request) { $request->validate([ 'user_id' => 'required|exists:users,id', 'user_name' => 'required|string|max:255', 'user_phone' => 'required|string|max:255', 'membership_type' => 'required|string|max:255', 'expiry_date' => 'required|string|max:255' ]); $user = \App\Models\User::find($request->user_id); // Check if user has a phone number if (!$user->phone) { return response()->json([ 'success' => false, 'message' => 'User does not have a phone number registered' ], 400); } // Use Twilio service to send SMS $twilioService = new \App\Services\TwilioService(); $result = $twilioService->sendMembershipExpiryNotification( $request->user_name, $request->user_phone, $request->membership_type, $request->expiry_date ); if ($result['success']) { return response()->json([ 'success' => true, 'message' => 'SMS notification sent successfully', 'message_sid' => $result['message_sid'] ]); } else { return response()->json([ 'success' => false, 'message' => 'Failed to send SMS: ' . $result['error'] ], 500); } })->name('admin.send-sms-notification'); // Add new member route Route::post('/admin/add-new-member', function (Request $request) { // Normalize phone to digits-only (so pasted formats like 09xx-xxx-xxxx become 09xxxxxxxxx) if ($request->has('phone')) { $request->merge([ 'phone' => preg_replace('/\D+/', '', (string) $request->phone), ]); } $isWalkin = in_array($request->input('membershipType'), ['student_walkin', 'regular_walkin'], true); $rules = [ 'firstName' => 'required|string|max:255', 'lastName' => 'required|string|max:255', 'membershipType' => 'required|in:student_walkin,regular_walkin,student_monthly,regular_monthly', 'amountPaid' => 'required|numeric|min:0', 'paymentMethod' => 'required|in:cash,gcash', 'paymentReference' => 'nullable|string|max:255', ]; // Walk-ins are daily and can re-register on different days. // Members (monthly) keep existing uniqueness behavior. if ($isWalkin) { $rules['email'] = ['required', 'email', new NotDisposableEmail]; $rules['phone'] = ['required', 'string', 'size:11', 'regex:/^09\d{9}$/']; } else { $rules['email'] = ['required', 'email', 'unique:users,email', new NotDisposableEmail]; // PH mobile: 11 digits, starts with 09 (ex: 09123456789), must be unique $rules['phone'] = ['required', 'string', 'size:11', 'regex:/^09\d{9}$/', 'unique:users,phone']; } $request->validate($rules, [ 'email.unique' => 'Member already used the email.', 'phone.unique' => 'Member already used the phone number.', ]); // Members only: keep original "name must be unique" restriction if (!$isWalkin) { $firstName = trim($request->firstName); $lastName = trim($request->lastName); if (\App\Models\User::where('first_name', $firstName)->where('last_name', $lastName)->exists()) { return response()->json([ 'message' => 'A member with this first name and last name is already registered.', 'errors' => ['first_name' => ['A member with this first name and last name is already registered.']] ], 422); } } // Validate payment reference based on payment method if ($request->paymentMethod === 'gcash' && empty($request->paymentReference)) { return response()->json([ 'success' => false, 'message' => 'Payment reference is required for GCash payments' ], 400); } try { \DB::beginTransaction(); $user = null; if ($isWalkin) { // For walk-ins, first try to re-use existing person by phone $user = \App\Models\User::where('phone', $request->phone)->first(); // If phone is different but first+last name match an existing user, treat as same person if (!$user) { $firstName = trim($request->firstName); $lastName = trim($request->lastName); $user = \App\Models\User::where('first_name', $firstName) ->where('last_name', $lastName) ->first(); } } if (!$user) { // Create new user (members and first-time walk-ins) $createData = [ 'first_name' => $request->firstName, 'last_name' => $request->lastName, 'email' => $request->email, 'phone' => $request->phone, 'role' => 'customer', 'password' => bcrypt('password123'), // Default password ]; if ($isWalkin) { // Auto-verify for walk-ins only $createData['email_verified_at'] = now(); } $user = \App\Models\User::create($createData); } // Generate payment reference for cash if not provided $paymentReference = $request->paymentReference; if ($request->paymentMethod === 'cash' && empty($paymentReference)) { $date = now()->format('m-d-y'); // MM-DD-YY format $uniqueRef = substr(uniqid(), -6); // Get last 6 characters $paymentReference = $date . '-' . $uniqueRef; } // Calculate end date based on membership type $startDate = now(); $endDate = null; if (in_array($request->membershipType, ['student_walkin', 'regular_walkin'])) { // Walk-in: 1 day duration $endDate = $startDate->copy()->addDay(); } elseif (in_array($request->membershipType, ['student_monthly', 'regular_monthly'])) { // Monthly: 1 month duration $endDate = $startDate->copy()->addMonth(); } // Walk-ins: prevent duplicate registration for the same day (but allow on different days) if ($isWalkin) { $alreadyRegisteredToday = \App\Models\Membership::where('user_id', $user->id) ->whereIn('membership_type', ['student_walkin', 'regular_walkin']) ->whereDate('start_date', $startDate->toDateString()) ->exists(); if ($alreadyRegisteredToday) { \DB::rollBack(); return response()->json([ 'message' => 'This walk-in member is already registered today.', 'errors' => ['walkin' => ['This walk-in member is already registered today.']] ], 422); } } // Create membership $membership = \App\Models\Membership::create([ 'user_id' => $user->id, 'membership_type' => $request->membershipType, 'amount_paid' => $request->amountPaid, 'payment_method' => $request->paymentMethod, 'payment_reference' => $paymentReference, 'status' => 'active', // Auto-confirm for walk-ins 'payment_status' => 'completed', // Payment is completed for walk-ins 'start_date' => $startDate, 'end_date' => $endDate ]); \DB::commit(); return response()->json([ 'success' => true, 'message' => 'Member added successfully', 'user_id' => $user->id, 'membership_id' => $membership->id ]); } catch (\Exception $e) { \DB::rollback(); return response()->json([ 'success' => false, 'message' => 'Failed to add member: ' . $e->getMessage() ], 500); } })->name('admin.add-new-member'); // Renew expired membership route Route::post('/admin/renew-membership', function (Request $request) { $request->validate([ 'user_id' => 'required|exists:users,id', 'membershipType' => 'required|in:student_walkin,regular_walkin,student_monthly,regular_monthly', 'amountPaid' => 'required|numeric|min:0', 'paymentMethod' => 'required|in:cash,gcash', 'paymentReference' => 'nullable|string|max:255' ]); // Validate payment reference based on payment method if ($request->paymentMethod === 'gcash' && empty($request->paymentReference)) { return response()->json([ 'success' => false, 'message' => 'Payment reference is required for GCash payments' ], 400); } try { \DB::beginTransaction(); // Get the user $user = \App\Models\User::findOrFail($request->user_id); // Generate payment reference for cash if not provided $paymentReference = $request->paymentReference; if ($request->paymentMethod === 'cash' && empty($paymentReference)) { $date = now()->format('m-d-y'); // MM-DD-YY format $uniqueRef = substr(uniqid(), -6); // Get last 6 characters $paymentReference = $date . '-' . $uniqueRef; } // Calculate end date based on membership type $startDate = now(); $endDate = null; if (in_array($request->membershipType, ['student_walkin', 'regular_walkin'])) { // Walk-in: 1 day duration $endDate = $startDate->copy()->addDay(); } elseif (in_array($request->membershipType, ['student_monthly', 'regular_monthly'])) { // Monthly: 1 month duration $endDate = $startDate->copy()->addMonth(); } // Create new membership (renewal creates a new membership record) $membership = \App\Models\Membership::create([ 'user_id' => $user->id, 'membership_type' => $request->membershipType, 'amount_paid' => $request->amountPaid, 'payment_method' => $request->paymentMethod, 'payment_reference' => $paymentReference, 'status' => 'active', // Auto-activate for admin renewals 'payment_status' => 'completed', // Payment is completed for admin renewals 'start_date' => $startDate, 'end_date' => $endDate, 'notes' => 'Membership renewed by admin' ]); \DB::commit(); return response()->json([ 'success' => true, 'message' => 'Membership renewed successfully', 'user_id' => $user->id, 'membership_id' => $membership->id ]); } catch (\Exception $e) { \DB::rollback(); return response()->json([ 'success' => false, 'message' => 'Failed to renew membership: ' . $e->getMessage() ], 500); } })->name('admin.renew-membership'); Route::get('/admin/ajax/members', function () { $users = \App\Models\User::with(['memberships' => function($query) { $query->orderBy('created_at', 'desc'); }, 'latestMembership'])->where('role', 'customer')->orderBy('created_at', 'desc')->get(); // Filter out users with only pending memberships (show active, expired, or no membership) $filteredUsers = $users->filter(function($user) { if ($user->hasActiveMembership()) { return true; } if ($user->memberships->isEmpty()) { return true; } if ($user->hasPendingMembership() && !$user->hasActiveMembership()) { return false; } return true; }); // Exclude walk-in only: show only members (monthly) and expired monthly (no face recognition for walk-ins) $filteredUsers = $filteredUsers->filter(function($user) { $hasMonthly = $user->memberships->contains(function($m) { return in_array($m->membership_type, ['student_monthly', 'regular_monthly']); }); return $hasMonthly; }); // Calculate statistics (members and expired only) $stats = [ 'total' => $filteredUsers->count(), 'active' => $filteredUsers->filter(function($user) { return $user->hasActiveMembership(); })->count(), 'monthly' => $filteredUsers->filter(function($user) { return $user->activeMembership && $user->activeMembership->isMonthly(); })->count(), 'expired' => $filteredUsers->filter(function($user) { $latest = $user->latestMembership; return $latest && ($latest->isExpired() || $latest->status === 'expired'); })->count(), ]; return view('admin.ajax.members', compact('filteredUsers', 'stats')); })->name('admin.ajax.members'); Route::get('/admin/ajax/attendance', function () { return view('admin.ajax.attendance'); })->name('admin.ajax.attendance'); // Attendance data endpoint Route::get('/admin/attendance/data', [AdminController::class, 'getAttendanceData'])->name('admin.attendance.data'); // Members data endpoint Route::get('/admin/members/data', [AdminController::class, 'getMembersData'])->name('admin.members.data'); Route::get('/admin/ajax/staff', function () { $staff = \App\Models\User::where('role', 'staff')->orderBy('created_at', 'desc')->get(); // Calculate statistics $stats = [ 'total' => $staff->count(), 'active' => $staff->count(), // All staff are considered active 'recent' => $staff->where('created_at', '>=', now()->subDays(30))->count(), ]; return view('admin.ajax.staff', compact('staff', 'stats')); })->name('admin.ajax.staff'); // Add staff route Route::post('/admin/staff/add', function (Request $request) { $validator = Validator::make($request->all(), [ 'first_name' => 'required|string|max:255', 'last_name' => 'required|string|max:255', 'email' => 'required|email|unique:users,email', 'phone' => 'nullable|string|max:20', 'password' => 'required|string|min:6', 'confirm_password' => 'required|same:password', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation failed', 'errors' => $validator->errors() ], 422); } try { $user = \App\Models\User::create([ 'first_name' => $request->first_name, 'last_name' => $request->last_name, 'email' => $request->email, 'phone' => $request->phone, 'password' => Hash::make($request->password), 'role' => 'staff', ]); return response()->json([ 'success' => true, 'message' => 'Staff member added successfully!', 'user' => $user ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'Failed to add staff member: ' . $e->getMessage() ], 500); } })->name('admin.staff.add'); // Standalone transactions page for debugging Route::get('/admin/transactions', function () { $memberships = \App\Models\Membership::with('user')->orderBy('created_at', 'desc')->get(); // Calculate statistics $stats = [ 'total' => $memberships->count(), 'confirmed' => $memberships->where('status', 'active')->count(), 'pending' => $memberships->where('status', 'pending')->count(), 'totalRevenue' => $memberships->sum('amount_paid'), ]; return view('admin.transactions-standalone', compact('memberships', 'stats')); })->name('admin.transactions.standalone'); // Receipt print page (full page with Tailwind for exact design match) Route::get('/admin/print/receipt/{id}', [TransactionController::class, 'receiptPrintPage'])->name('admin.receipt.print'); }); // Staff Routes Route::middleware('role:staff')->group(function () { Route::get('/staff/dashboard', function () { return view('staff.dashboard'); })->name('staff.dashboard'); // AJAX routes for dynamic content loading Route::get('/staff/ajax/dashboard', function () { return view('staff.ajax.dashboard'); })->name('staff.ajax.dashboard'); Route::get('/staff/ajax/members', function () { return view('staff.ajax.members'); })->name('staff.ajax.members'); Route::get('/staff/ajax/transactions', function () { $memberships = \App\Models\Membership::with('user')->orderBy('created_at', 'desc')->get(); // Calculate statistics $stats = [ 'total' => $memberships->count(), 'confirmed' => $memberships->where('status', 'active')->count(), 'pending' => $memberships->where('status', 'pending')->count(), 'totalRevenue' => $memberships->sum('amount_paid'), ]; return view('staff.ajax.transactions', compact('memberships', 'stats')); })->name('staff.ajax.transactions'); Route::get('/staff/ajax/attendance', function () { return view('staff.ajax.attendance'); })->name('staff.ajax.attendance'); }); // Customer Routes Route::middleware('role:customer')->group(function () { Route::get('/customer/dashboard', [CustomerController::class, 'dashboard'])->name('customer.dashboard'); // Customer AJAX routes for dynamic content loading Route::get('/customer/ajax/dashboard', function () { $memberships = \App\Models\Membership::where('user_id', auth()->id())->orderBy('created_at', 'desc')->get(); $latestMembership = $memberships->first(); $pendingMembership = $memberships->where('status', 'pending')->first(); return view('customer.ajax.dashboard', compact('memberships', 'latestMembership', 'pendingMembership')); })->name('customer.ajax.dashboard'); Route::get('/customer/ajax/attendance', function () { $user = auth()->user(); $todayAttendance = null; $monthlyStats = []; if ($user->hasActiveMembership()) { $todayAttendance = \App\Models\Attendance::where('user_id', $user->id) ->whereDate('entry_time', today()) ->first(); // Calculate monthly statistics $startOfMonth = now()->startOfMonth(); $endOfMonth = now()->endOfMonth(); $monthlyAttendances = \App\Models\Attendance::where('user_id', $user->id) ->whereBetween('entry_time', [$startOfMonth, $endOfMonth]) ->where('status', 'absent') ->get(); $monthlyStats = [ 'total_visits' => $monthlyAttendances->count(), 'total_hours' => 0, // No longer tracking duration 'average_duration' => 0, // No longer tracking duration 'longest_session' => 0 // No longer tracking duration ]; } return view('customer.ajax.attendance', compact('todayAttendance', 'monthlyStats')); })->name('customer.ajax.attendance'); Route::get('/customer/ajax/rules', function () { return view('customer.ajax.rules'); })->name('customer.ajax.rules'); Route::get('/customer/ajax/payment-history', function () { $memberships = \App\Models\Membership::where('user_id', auth()->id()) ->orderBy('created_at', 'desc') ->get(); return view('customer.ajax.payment-history', compact('memberships')); })->name('customer.ajax.payment-history'); // Customer Profile Routes Route::get('/customer/ajax/profile', [App\Http\Controllers\CustomerController::class, 'showProfile'])->name('customer.ajax.profile'); Route::put('/customer/profile/update', [App\Http\Controllers\CustomerController::class, 'updateProfile'])->name('customer.profile.update')->middleware('role:customer'); // Customer Settings Routes Route::get('/customer/ajax/settings', [App\Http\Controllers\CustomerController::class, 'showSettings'])->name('customer.ajax.settings'); Route::put('/customer/password/update', [App\Http\Controllers\CustomerController::class, 'updatePassword'])->name('customer.password.update'); // Attendance Routes (protected by active membership) Route::middleware('auth')->group(function () { Route::get('/attendance', function() { return redirect()->route('customer.dashboard'); })->name('attendance.index'); Route::post('/attendance/check-in', [App\Http\Controllers\AttendanceController::class, 'checkIn'])->name('attendance.check-in'); Route::post('/attendance/check-out', [App\Http\Controllers\AttendanceController::class, 'checkOut'])->name('attendance.check-out'); Route::get('/attendance/history', [App\Http\Controllers\AttendanceController::class, 'history'])->name('attendance.history'); Route::get('/attendance/stats', [App\Http\Controllers\AttendanceController::class, 'stats'])->name('attendance.stats'); Route::get('/attendance/calendar-data', [App\Http\Controllers\AttendanceController::class, 'getCalendarData'])->name('attendance.calendar-data'); Route::get('/attendance/status', [App\Http\Controllers\AttendanceController::class, 'getStatus'])->name('attendance.status'); Route::post('/attendance/rfid-check-in', [App\Http\Controllers\AttendanceController::class, 'rfidCheckIn'])->name('attendance.rfid-check-in'); // Face Recognition Routes Route::post('/face/register', [App\Http\Controllers\FaceRecognitionController::class, 'registerFace'])->name('face.register'); Route::post('/face/verify', [App\Http\Controllers\FaceRecognitionController::class, 'verifyFace'])->name('face.verify'); Route::get('/face/status', [App\Http\Controllers\FaceRecognitionController::class, 'getStatus'])->name('face.status'); }); }); // Shared API routes for admin and staff Route::middleware('role:admin,staff')->group(function () { Route::get('/api/transactions', [TransactionController::class, 'getTransactions'])->name('api.transactions'); Route::post('/api/transactions/{id}/confirm', [TransactionController::class, 'confirmPayment'])->name('api.transactions.confirm'); Route::get('/api/transactions/statistics', [TransactionController::class, 'getStatistics'])->name('api.transactions.statistics'); Route::get('/api/transactions/{id}/receipt', [TransactionController::class, 'getReceipt'])->name('api.transactions.receipt'); Route::get('/api/transactions/{id}/receipt/pdf', [TransactionController::class, 'getReceiptPdf'])->name('api.transactions.receipt.pdf'); Route::get('/api/transactions/export/pdf', [TransactionController::class, 'exportAllTransactionsPdf'])->name('api.transactions.export.pdf'); }); // Admin-only face registration route Route::middleware('role:admin')->group(function () { Route::post('/admin/face/register/{userId}', [App\Http\Controllers\FaceRecognitionController::class, 'registerFaceForUser'])->name('admin.face.register'); }); }); An Error Occurred: Not Found

Oops! An Error Occurred

The server returned a "404 Not Found".

Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused.