filled('search')) { $search = $request->search; $query->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('username', 'like', "%{$search}%") ->orWhere('email', 'like', "%{$search}%"); }); } if ($request->filled('role')) { $role = $request->role; $query->where(function ($q) use ($role) { $q->where('role', $role) ->orWhereJsonContains('roles', $role); }); } $users = $query->orderBy('name')->paginate(20); return view('users.index', compact('users')); } public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'username' => ['required', 'string', 'max:255', 'unique:users,username'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email'], 'phone' => ['nullable', 'string', 'max:20', 'regex:/^[0-9+\-\s()]*$/'], 'password' => ['required', 'string', 'min:6'], 'roles' => ['required', 'array', 'min:1'], 'roles.*' => ['in:admin,bk,wali_kelas,staff,other,kepala_sekolah,waka_kesiswaan,ketua_tim'], 'is_active' => ['boolean'], ]); $roles = array_values(array_unique($validated['roles'])); $validated['roles'] = $roles; $validated['role'] = $roles[0]; $validated['password'] = Hash::make($validated['password']); $validated['is_active'] = $request->boolean('is_active', true); User::create($validated); return redirect()->route('users.index') ->with('success', 'User berhasil ditambahkan.'); } public function update(Request $request, User $user): RedirectResponse { $validated = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'username' => ['required', 'string', 'max:255', 'unique:users,username,' . $user->id], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,' . $user->id], 'phone' => ['nullable', 'string', 'max:20', 'regex:/^[0-9+\-\s()]*$/'], 'password' => ['nullable', 'string', 'min:6'], 'roles' => ['required', 'array', 'min:1'], 'roles.*' => ['in:admin,bk,wali_kelas,staff,other,kepala_sekolah,waka_kesiswaan,ketua_tim'], 'is_active' => ['boolean'], ]); $roles = array_values(array_unique($validated['roles'])); $validated['roles'] = $roles; $validated['role'] = $roles[0]; if (!empty($validated['password'])) { $validated['password'] = Hash::make($validated['password']); } else { unset($validated['password']); } $validated['is_active'] = $request->boolean('is_active', true); $user->update($validated); return redirect()->route('users.index') ->with('success', 'User berhasil diperbarui.'); } public function destroy(User $user): RedirectResponse { if ($user->id === auth()->id()) { return back()->with('error', 'Tidak bisa menghapus akun sendiri.'); } $user->delete(); return redirect()->route('users.index') ->with('success', 'User berhasil dihapus.'); } public function resetPassword(Request $request, User $user): RedirectResponse { if ($user->id === auth()->id() && $user->isAdmin()) { return back()->with('error', 'Ganti password admin lewat profil.'); } $validated = $request->validate([ 'new_password' => ['required', 'string', 'min:6'], ]); $user->update([ 'password' => Hash::make($validated['new_password']), ]); return redirect()->route('users.index') ->with('success', 'Password untuk ' . $user->name . ' berhasil direset.'); } /** * Akhiri sesi aktif user (single session) — user harus login ulang. */ public function forceLogout(User $user): RedirectResponse { if ($user->id === auth()->id()) { return back()->with('error', 'Tidak bisa memaksa logout diri sendiri.'); } $token = $user->active_session_token; $user->forceFill(['active_session_token' => null])->save(); // Hapus session file lama agar perangkat lama benar-benar terlempar if ($token && config('session.driver') === 'file') { @unlink(storage_path('framework/sessions/'.$token)); } return back()->with('success', 'Sesi '.$user->name.' diakhiri — akun bisa login kembali.'); } /** * Kirim akun (link + username + password baru) ke nomor HP user via WhatsApp. * Password lama tidak bisa dikirim (tersimpan hash) — jadi dibuatkan password baru. * * Jika request AJAX/JSON → kembalikan URL wa.me (aplikasi tetap terbuka, * WhatsApp dibuka di tab baru). Jika tidak → redirect away (fallback). */ public function sendWa(Request $request, User $user): RedirectResponse|JsonResponse { if ($user->id === auth()->id()) { return $this->waResponse($request, null, 'Tidak perlu mengirim akun ke diri sendiri.'); } if (blank($user->phone)) { return $this->waResponse($request, null, 'Nomor HP belum diisi untuk ' . $user->name . ' — isi dulu di form edit user.'); } // Generate password baru yang mudah dibaca (8 karakter, tanpa karakter ambigu) $password = Str::upper(Str::random(4)).rand(10, 99).Str::lower(Str::random(2)); $user->update([ 'password' => Hash::make($password), ]); $appName = Setting::getValue('app_name', 'Aplikasi'); $appUrl = Setting::getValue('app_url', config('app.url', '')); $message = implode("\n", [ '📲 *Akun Aplikasi '.$appName.'*', '', 'Yth. '.$user->name.',', 'Berikut akun Anda untuk masuk ke aplikasi:', '', '🔗 Link: '.$appUrl, '👤 Username: '.$user->username, '🔑 Password: '.$password, '', 'Mohon segera login dan ganti password Anda. Terima kasih.', ]); $waUrl = 'https://wa.me/'.$user->phone.'?text='.rawurlencode($message); return $this->waResponse($request, $waUrl, null, [ 'name' => $user->name, 'username' => $user->username, 'password' => $password, 'app_name' => $appName, 'app_url' => $appUrl, ]); } protected function waResponse(Request $request, ?string $url, ?string $error, array $extra = []): RedirectResponse|JsonResponse { if ($request->expectsJson() || $request->ajax()) { return response()->json(array_merge( $extra, ['url' => $url, 'error' => $error] ), $error ? 422 : 200); } if ($error) { return back()->with('error', $error); } return redirect()->away($url); } }