user(); $profile = Profile::where('user_id', $user->id)->first(); $profileId = $profile?->id; $jumlahLahan = $profileId ? Lahan::where('profile_id', $profileId)->count() : 0; $totalLuasLahan = $profileId ? Lahan::where('profile_id', $profileId)->sum('luas_lahan') : 0; $jumlahTanaman = $profileId ? Tanaman::where('profile_id', $profileId)->count() : 0; $jumlahKomoditas = $profileId ? Tanaman::where('profile_id', $profileId)->whereNotNull('komoditas_id')->distinct('komoditas_id')->count('komoditas_id') : 0; $problemSummary = $this->getProblemSummary(); $activities = $this->getProfileActivities($user, $profile); $title = 'Profile'; return view('konfigurasi.profile.index', compact('title', 'profile', 'jumlahKomoditas', 'jumlahLahan', 'totalLuasLahan', 'jumlahTanaman', 'activities', 'problemSummary')); } public function security() { $user = auth()->user(); $profile = Profile::where('user_id', $user->id)->first(); $loginSessions = $this->getLoginSessions($user); $title = 'Keamanan Akun'; return view('konfigurasi.profile.security', compact('title', 'profile', 'loginSessions')); } public function activities() { $user = auth()->user(); $profile = Profile::where('user_id', $user->id)->first(); return response()->json([ 'activities' => $this->getProfileActivities($user, $profile)->values(), ]); } public function updatePassword(Request $request) { $request->validate([ 'current_password' => ['required', 'current_password'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ], [ 'current_password.current_password' => 'Password saat ini tidak sesuai.', 'password.confirmed' => 'Konfirmasi password baru tidak cocok.', 'password.min' => 'Password baru minimal 8 karakter.', ]); $request->user()->update([ 'password' => Hash::make($request->password), ]); return back()->with('security_success', 'Password berhasil diperbarui.'); } public function logoutDevices(Request $request) { if (Schema::hasTable('sessions')) { DB::table('sessions') ->where('user_id', $request->user()->id) ->delete(); } Auth::logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); return redirect()->route('login')->with('success', 'Berhasil logout dari semua perangkat.'); } private function getLoginSessions(User $user) { if (!Schema::hasTable('sessions')) { return collect(); } return DB::table('sessions') ->where('user_id', $user->id) ->orderByDesc('last_activity') ->limit(5) ->get() ->map(function ($session) { return (object) [ 'id' => $session->id, 'ip_address' => $session->ip_address, 'user_agent' => $session->user_agent, 'last_activity' => \Carbon\Carbon::createFromTimestamp($session->last_activity), 'is_current' => $session->id === session()->getId(), ]; }); } private function getProblemSummary(): array { return [ [ 'label' => 'User belum diverifikasi', 'count' => User::whereNull('email_verified_at')->count(), 'icon' => 'tabler-user-exclamation', 'color' => 'warning', 'url' => route('users.index', ['tab' => 'pending']), ], [ 'label' => 'Profile tanpa user', 'count' => Profile::whereNull('user_id') ->orWhereDoesntHave('user') ->count(), 'icon' => 'tabler-id-off', 'color' => 'danger', 'url' => route('profile.index'), ], [ 'label' => 'Lahan tanpa luas', 'count' => Lahan::whereNull('luas_lahan') ->orWhere('luas_lahan', '<=', 0) ->count(), 'icon' => 'tabler-map-off', 'color' => 'warning', 'url' => route('lahan.index'), ], [ 'label' => 'Tanaman tanpa komoditas', 'count' => Tanaman::whereNull('komoditas_id')->count(), 'icon' => 'tabler-plant-off', 'color' => 'warning', 'url' => route('tanaman.index'), ], [ 'label' => 'Wilayah/desa tidak lengkap', 'count' => DesaKelurahan::withoutGlobalScopes() ->where(function (Builder $query) { $query->whereNull('kode') ->orWhere('kode', '') ->orWhereNull('nama') ->orWhere('nama', '') ->orWhereNull('kecamatan_id') ->orWhereNull('path'); }) ->count(), 'icon' => 'tabler-map-pin-exclamation', 'color' => 'info', 'url' => route('desakelurahan.index'), ], ]; } private function getProfileActivities(User $user, ?Profile $profile, int $limit = 20) { $sources = [ ['model' => Profile::class, 'label' => 'Profil', 'name' => 'nama', 'profile_field' => null], ['model' => Lahan::class, 'label' => 'Lahan', 'name' => 'nama', 'profile_field' => 'profile_id'], ['model' => Tanaman::class, 'label' => 'Tanaman', 'name' => 'nama', 'profile_field' => 'profile_id'], ['model' => Komoditas::class, 'label' => 'Komoditas', 'name' => 'nama', 'profile_field' => null], ]; $activities = collect(); foreach ($sources as $source) { $query = $source['model']::withTrashed(); $query->where(function ($query) use ($user) { $query->where('created_by', $user->id) ->orWhere('updated_by', $user->id) ->orWhere('deleted_by', $user->id); }); $query->where(function (Builder $query) { $query->where('created_at', '>=', now()->subWeeks(2)) ->orWhere('updated_at', '>=', now()->subWeeks(2)) ->orWhere('deleted_at', '>=', now()->subWeeks(2)); }); $query->latest('updated_at') ->limit($limit) ->get() ->each(function ($record) use ($activities, $source) { $activities->push($this->formatActivity($record, $source)); }); } return $activities ->sortByDesc('sort_time') ->take($limit) ->values() ->map(function ($activity) { unset($activity['sort_time']); return $activity; }); } private function formatActivity($record, array $source): array { $name = $record->{$source['name']} ?? 'Tanpa nama'; $timestamp = $record->created_at; $action = 'Menambahkan'; $color = 'success'; if ($record->deleted_at) { $timestamp = $record->deleted_at; $action = 'Menghapus'; $color = 'danger'; } elseif ($record->updated_at && $record->created_at && $record->updated_at->gt($record->created_at)) { $timestamp = $record->updated_at; $action = 'Memperbarui'; $color = 'primary'; } return [ 'title' => "{$action} {$source['label']}", 'description' => "{$source['label']} {$name}", 'time' => $timestamp ? $timestamp->diffForHumans() : '-', 'color' => $color, 'sort_time' => $timestamp, ]; } }