route('admin.login'); })->name('welcome'); // Shortcut URL - lebih mudah diingat Route::get('/pemilik', function () { return redirect()->route('admin.login'); }); // ------------------------------------------------- // AUTH ADMIN (PUBLIC - TIDAK PERLU LOGIN) // ------------------------------------------------- Route::get('/admin/login', [AdminAuthController::class, 'loginPage'])->name('admin.login'); Route::post('/admin/login', [AdminAuthController::class, 'login'])->middleware('throttle:10,1')->name('admin.login.post'); // Admin register: // - Default: disabled in production (security) // - Enabled for local dev or when ENABLE_ADMIN_SELF_REGISTER=true if (app()->environment('local') || env('ENABLE_ADMIN_SELF_REGISTER', false)) { Route::get('/admin/register', [AdminAuthController::class, 'registerPage'])->name('admin.register'); Route::post('/admin/register', [AdminAuthController::class, 'register'])->middleware('throttle:3,1')->name('admin.register.post'); } // Local-only helper: reset demo admin passwords Route::get('/admin/reset-demo-passwords', function () { if (!app()->environment('local')) { abort(404); } $updated = 0; foreach (['superadmin@gmail.com', 'admin@gmail.com'] as $email) { $admin = Admin::where('email', $email)->first(); if ($admin) { $admin->password = Hash::make('password'); $admin->save(); $updated++; } } return "OK: reset $updated admin password(s)"; }); // ------------------------------------------------- // ROUTE ADMIN TERPROTEKSI LOGIN // ------------------------------------------------- Route::middleware('auth:admin')->group(function () { // Dashboard Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard'); // Admin Profile Route::get('/admin/profile', [\App\Http\Controllers\AdminProfileController::class, 'edit'])->name('admin.profile'); Route::put('/admin/profile', [\App\Http\Controllers\AdminProfileController::class, 'update'])->name('admin.profile.update'); // ========== 🆕 CLEAR DASHBOARD CACHE (BARU!) ========== Route::post('/dashboard/clear-cache', [DashboardController::class, 'clearCache'])->name('dashboard.clear-cache'); // ========== 🆕 EXPORT ROUTES ========== Route::prefix('export')->name('export.')->group(function () { // Kontrakan Route::get('/kontrakan/excel', [ExportController::class, 'kontrakanExcel'])->name('kontrakan.excel'); Route::get('/kontrakan/pdf', [ExportController::class, 'kontrakanPDF'])->name('kontrakan.pdf'); // Laundry Route::get('/laundry/excel', [ExportController::class, 'laundryExcel'])->name('laundry.excel'); Route::get('/laundry/pdf', [ExportController::class, 'laundryPDF'])->name('laundry.pdf'); // SAW Results Route::post('/saw/pdf', [ExportController::class, 'sawResultsPDF'])->name('saw.pdf'); Route::post('/saw/excel', [ExportController::class, 'sawResultsExcel'])->name('saw.excel'); }); // ========== 🆕 ACTIVITY LOG ROUTES ========== Route::prefix('admin')->name('admin.')->middleware('role:super_admin')->group(function () { Route::get('/activity-logs/dashboard', [ActivityLogController::class, 'dashboard'])->name('activity-logs.dashboard'); Route::get('/activity-logs/security', [ActivityLogController::class, 'security'])->name('activity-logs.security'); Route::get('/activity-logs/api-stats', [ActivityLogController::class, 'apiStats'])->name('activity-logs.api-stats'); Route::post('/activity-logs/archive', [ActivityLogController::class, 'archive'])->name('activity-logs.archive'); Route::get('/activity-logs/export', [ActivityLogController::class, 'export'])->name('activity-logs.export'); Route::post('/activity-logs/clear', [ActivityLogController::class, 'clear'])->name('activity-logs.clear'); Route::resource('activity-logs', ActivityLogController::class)->only(['index', 'show']); }); // ========== 🆕 USER MANAGEMENT ROUTES ========== Route::prefix('admin')->name('admin.')->middleware('role:super_admin')->group(function () { Route::resource('users', UserManagementController::class); Route::post('/users/{user}/restore', [UserManagementController::class, 'restore'])->name('users.restore')->withTrashed(); Route::post('/users/bulk-delete', [UserManagementController::class, 'bulkDelete'])->name('users.bulk-delete'); }); // ========== 🆕 BACKUP & RESTORE ROUTES ========== Route::prefix('admin/backup')->name('admin.backup.')->middleware('role:super_admin')->group(function () { Route::get('/', [BackupController::class, 'index'])->name('index'); Route::post('/create', [BackupController::class, 'create'])->name('create'); Route::get('/download/{backup}', [BackupController::class, 'download'])->name('download'); Route::delete('/delete/{backup}', [BackupController::class, 'delete'])->name('delete'); Route::post('/restore/{backup}', [BackupController::class, 'restore'])->name('restore'); }); // ========== BULK DELETE KONTRAKAN (HARUS SEBELUM RESOURCE!) ========== Route::post('/kontrakan/bulk-destroy', [KontrakanController::class, 'bulkDestroy'])->name('kontrakan.bulk-destroy'); // ========== 🆕 BOOKING SYSTEM ROUTES ========== Route::prefix('admin/bookings')->name('admin.bookings.')->group(function () { Route::get('/', [BookingController::class, 'index'])->name('index'); Route::get('/create', [BookingController::class, 'create'])->name('create'); Route::post('/', [BookingController::class, 'store'])->name('store'); Route::post('/bulk-destroy', [BookingController::class, 'bulkDestroy'])->name('bulk-destroy'); Route::get('/{booking}', [BookingController::class, 'show'])->name('show'); // ✅ Secure payment proof view (not public storage link) Route::get('/{booking}/payment-proof', [BookingController::class, 'paymentProof'])->name('payment-proof'); // ✅ Secure KTP photo view Route::get('/{booking}/ktp-photo', [BookingController::class, 'ktpPhoto'])->name('ktp-photo'); Route::get('/{booking}/edit', [BookingController::class, 'edit'])->name('edit'); Route::put('/{booking}', [BookingController::class, 'update'])->name('update'); Route::delete('/{booking}', [BookingController::class, 'destroy'])->name('destroy'); // Actions Route::post('/{booking}/confirm', [BookingController::class, 'confirm'])->name('confirm'); Route::post('/{booking}/check-in', [BookingController::class, 'checkIn'])->name('check-in'); Route::post('/{booking}/check-out', [BookingController::class, 'checkOut'])->name('check-out'); Route::post('/{booking}/cancel', [BookingController::class, 'cancel'])->name('cancel'); Route::post('/{booking}/mark-paid', [BookingController::class, 'markPaid'])->name('mark-paid'); Route::post('/{booking}/toggle-payment', [BookingController::class, 'togglePaymentStatus'])->name('toggle-payment'); Route::post('/{booking}/verify-payment', [BookingController::class, 'verifyPayment'])->name('verify-payment'); Route::post('/{booking}/reject-payment', [BookingController::class, 'rejectPayment'])->name('reject-payment'); // API & History Route::post('/check-availability', [BookingController::class, 'checkAvailability'])->name('check-availability'); Route::get('/kontrakan/{kontrakan}/history', [BookingController::class, 'kontrakanHistory'])->name('kontrakan-history'); }); // ========== UPDATE STATUS KONTRAKAN (QUICK) ========== Route::post('/kontrakan/{kontrakan}/update-status', [KontrakanController::class, 'updateStatus'])->name('kontrakan.update-status'); // CRUD Kontrakan Route::resource('kontrakan', KontrakanController::class); // ========== 🗺️ MAP VIEW LAUNDRY (BARU - HARUS SEBELUM RESOURCE!) ========== Route::get('/laundry/map', [LaundryController::class, 'map'])->name('laundry.map'); // ========== BULK DELETE LAUNDRY (HARUS SEBELUM RESOURCE!) ========== Route::post('/laundry/bulk-destroy', [LaundryController::class, 'bulkDestroy'])->name('laundry.bulk-destroy'); // CRUD Laundry Route::resource('laundry', LaundryController::class); // CRUD Kriteria SAW Route::resource('kriteria', KriteriaController::class); // ------------------------------------------------- // SAW (2 halaman) // ------------------------------------------------- Route::get('/saw', [SAWController::class, 'index'])->name('saw.index'); Route::get('/saw/bobot', [SAWController::class, 'bobot'])->name('saw.bobot'); Route::post('/saw/proses', [SAWController::class, 'proses'])->name('saw.proses'); // ------------------------------------------------- // 🆕 GALERI ROUTES (UNTUK MULTIPLE FOTO) // ------------------------------------------------- Route::prefix('galeri')->name('galeri.')->group(function () { // Kontrakan Route::post('/kontrakan/{kontrakan}/upload', [GaleriController::class, 'uploadKontrakan'])->name('upload'); Route::post('/kontrakan/{galeri}/set-primary', [GaleriController::class, 'setPrimaryKontrakan'])->name('set-primary'); Route::delete('/kontrakan/{galeri}', [GaleriController::class, 'deleteKontrakan'])->name('delete'); // Laundry Route::post('/laundry/{laundry}/upload', [GaleriController::class, 'uploadLaundry'])->name('upload-laundry'); Route::post('/laundry/{galeri}/set-primary', [GaleriController::class, 'setPrimaryLaundry'])->name('set-primary-laundry'); Route::delete('/laundry/{galeri}', [GaleriController::class, 'deleteLaundry'])->name('delete-laundry'); }); // Review Routes Route::prefix('reviews')->name('reviews.')->group(function () { Route::get('/{type}/{id}/create', [ReviewController::class, 'create']) ->where('type', 'kontrakan|laundry') ->name('create'); Route::post('/kontrakan/{kontrakan}', [ReviewController::class, 'storeKontrakan'])->name('kontrakan.store'); Route::post('/laundry/{laundry}', [ReviewController::class, 'storeLaundry'])->name('laundry.store'); Route::delete('/{review}', [ReviewController::class, 'destroy'])->name('destroy'); }); // Favorite Routes Route::prefix('favorites')->name('favorites.')->group(function () { Route::post('/kontrakan/{kontrakan}', [FavoriteController::class, 'toggleKontrakan'])->name('kontrakan.toggle'); Route::post('/laundry/{laundry}', [FavoriteController::class, 'toggleLaundry'])->name('laundry.toggle'); }); // Legacy favorite routes (untuk kompatibilitas) Route::post('/favorite/{type}/{id}', [FavoriteController::class, 'toggle'])->name('favorite.toggle'); Route::post('/admin/logout', [AdminAuthController::class, 'logout'])->name('admin.logout'); }); // ------------------------------------------------- // EMAIL VERIFICATION LANDING PAGE (PUBLIC - SIGNED) // ------------------------------------------------- Route::get('/email/verify/{id}/{hash}', function ($id, $hash, \Illuminate\Http\Request $request) { // Temukan user berdasarkan ID $user = \App\Models\User::findOrFail($id); // Verifikasi hash email if (! hash_equals((string) $hash, sha1($user->getEmailForVerification()))) { abort(403, 'Tautan verifikasi tidak valid.'); } // Tandai email sebagai terverifikasi if (! $user->hasVerifiedEmail()) { $user->markEmailAsVerified(); event(new \Illuminate\Auth\Events\Verified($user)); } // Kembalikan view sukses yang indah return view('auth.verified_success', [ 'name' => $user->name, 'email' => $user->email ]); })->middleware(['signed', 'throttle:6,1'])->name('verification.verify');