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'); }); });
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.