From 3e6188bfaac63c805a8a9e2fb6d5179a2d08b0fb Mon Sep 17 00:00:00 2001 From: micko samawa Date: Sun, 14 Jun 2026 21:15:56 +0700 Subject: [PATCH] refine dashboard and admin access --- .../Http/Controllers/BookingController.php | 44 +++--- .../Http/Controllers/DashboardController.php | 16 +- .../Http/Controllers/KontrakanController.php | 39 +++-- .../resources/views/Kriteria/create.blade.php | 10 +- .../resources/views/Kriteria/edit.blade.php | 10 +- .../resources/views/Kriteria/index.blade.php | 12 +- .../resources/views/Kriteria/show.blade.php | 6 +- .../resources/views/Layouts/admin.blade.php | 26 +++- .../resources/views/SAW/hasil.blade.php | 8 +- .../resources/views/SAW/index.blade.php | 2 +- .../resources/views/dashboard/index.blade.php | 140 ++++++++++++++++++ 11 files changed, 235 insertions(+), 78 deletions(-) diff --git a/spk_kontrakan/app/Http/Controllers/BookingController.php b/spk_kontrakan/app/Http/Controllers/BookingController.php index 81777df..20b55b6 100644 --- a/spk_kontrakan/app/Http/Controllers/BookingController.php +++ b/spk_kontrakan/app/Http/Controllers/BookingController.php @@ -26,13 +26,11 @@ public function index(Request $request) // ========== FILTER: ADMIN (Hanya booking dari kontrakan milik admin) ========== $admin = auth()->guard('admin')->user(); if ($admin) { - if ($admin->role !== 'super_admin') { - // Admin biasa hanya bisa lihat booking dari kontrakan milik mereka - $query->whereHas('kontrakan', function($q) use ($admin) { - $q->where('admin_id', $admin->id); - }); - } - // Super admin bisa lihat semua booking + // Semua admin, termasuk super admin, hanya melihat booking + // dari kontrakan yang mereka input sendiri. + $query->whereHas('kontrakan', function($q) use ($admin) { + $q->where('admin_id', $admin->id); + }); } // Filter by status @@ -50,7 +48,7 @@ public function index(Request $request) // ========== GET KONTRAKANS (sesuai filter admin) ========== $kontrakanQuery = Kontrakan::orderBy('nama'); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { $kontrakanQuery->where('admin_id', $admin->id); } @@ -68,7 +66,7 @@ public function create(Request $request) // ========== FILTER: ADMIN (Hanya kontrakan milik admin) ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { $kontrakanQuery->where('admin_id', $admin->id); } @@ -113,7 +111,7 @@ public function store(Request $request) // ========== AUTHORIZATION: Cek apakah admin punya akses ke kontrakan ini ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { if ($kontrakan->admin_id !== $admin->id) { throw new Exception('Anda tidak memiliki akses ke kontrakan ini.'); } @@ -238,7 +236,7 @@ public function show(Booking $booking) { // ========== AUTHORIZATION: Cek apakah admin punya akses ke booking ini ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { // Admin biasa hanya bisa lihat booking dari kontrakan milik mereka if ($booking->kontrakan->admin_id !== $admin->id) { abort(403, 'Anda tidak memiliki akses ke booking ini.'); @@ -257,7 +255,7 @@ public function paymentProof(Booking $booking) { // ========== AUTHORIZATION: Cek apakah admin punya akses ke booking ini ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { if ($booking->kontrakan->admin_id !== $admin->id) { abort(403, 'Anda tidak memiliki akses ke booking ini.'); } @@ -309,7 +307,7 @@ public function edit(Booking $booking) { // ========== AUTHORIZATION: Cek apakah admin punya akses ke booking ini ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { // Admin biasa hanya bisa edit booking dari kontrakan milik mereka if ($booking->kontrakan->admin_id !== $admin->id) { abort(403, 'Anda tidak memiliki akses ke booking ini.'); @@ -322,7 +320,7 @@ public function edit(Booking $booking) $kontrakanQuery = Kontrakan::orderBy('nama'); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { $kontrakanQuery->where('admin_id', $admin->id); } @@ -337,7 +335,7 @@ public function update(Request $request, Booking $booking) { // ========== AUTHORIZATION: Cek apakah admin punya akses ke booking ini ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { if ($booking->kontrakan->admin_id !== $admin->id) { abort(403, 'Anda tidak memiliki akses ke booking ini.'); } @@ -401,7 +399,7 @@ public function confirm(Booking $booking) { // ========== AUTHORIZATION ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { if ($booking->kontrakan->admin_id !== $admin->id) { abort(403, 'Anda tidak memiliki akses ke booking ini.'); } @@ -421,7 +419,7 @@ public function checkIn(Booking $booking) { // ========== AUTHORIZATION ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { if ($booking->kontrakan->admin_id !== $admin->id) { abort(403, 'Anda tidak memiliki akses ke booking ini.'); } @@ -441,7 +439,7 @@ public function checkOut(Booking $booking) { // ========== AUTHORIZATION ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { if ($booking->kontrakan->admin_id !== $admin->id) { abort(403, 'Anda tidak memiliki akses ke booking ini.'); } @@ -461,7 +459,7 @@ public function cancel(Request $request, Booking $booking) { // ========== AUTHORIZATION ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { if ($booking->kontrakan->admin_id !== $admin->id) { abort(403, 'Anda tidak memiliki akses ke booking ini.'); } @@ -483,7 +481,7 @@ public function markPaid(Request $request, Booking $booking) { // ========== AUTHORIZATION ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { if ($booking->kontrakan->admin_id !== $admin->id) { abort(403, 'Anda tidak memiliki akses ke booking ini.'); } @@ -505,7 +503,7 @@ public function togglePaymentStatus(Request $request, Booking $booking) { // ========== AUTHORIZATION ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { if ($booking->kontrakan->admin_id !== $admin->id) { abort(403, 'Anda tidak memiliki akses ke booking ini.'); } @@ -537,7 +535,7 @@ public function destroy(Booking $booking) { // ========== AUTHORIZATION ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { if ($booking->kontrakan->admin_id !== $admin->id) { abort(403, 'Anda tidak memiliki akses ke booking ini.'); } @@ -566,7 +564,7 @@ public function bulkDestroy(Request $request) // ========== AUTHORIZATION: Filter berdasarkan admin ========== $admin = auth()->guard('admin')->user(); - if ($admin && $admin->role !== 'super_admin') { + if ($admin) { // Admin biasa hanya bisa hapus booking dari kontrakan milik mereka $query->whereHas('kontrakan', function($q) use ($admin) { $q->where('admin_id', $admin->id); diff --git a/spk_kontrakan/app/Http/Controllers/DashboardController.php b/spk_kontrakan/app/Http/Controllers/DashboardController.php index 9273e4a..88be038 100644 --- a/spk_kontrakan/app/Http/Controllers/DashboardController.php +++ b/spk_kontrakan/app/Http/Controllers/DashboardController.php @@ -27,14 +27,14 @@ public function index() // ========== CACHE STATISTIK (5 menit) ========== $stats = Cache::remember('dashboard_stats_admin_' . $adminId, 300, function () use ($adminId, $isSuperAdmin) { return [ - 'jumlahKontrakan' => Kontrakan::where('admin_id', $adminId)->count(), + 'jumlahKontrakan' => $isSuperAdmin ? Kontrakan::count() : Kontrakan::where('admin_id', $adminId)->count(), 'jumlahLaundry' => $isSuperAdmin ? Laundry::count() : 0, 'jumlahKriteria' => $isSuperAdmin ? Kriteria::count() : 0, ]; }); // ========== DATA TERBARU (No Cache) ========== - $recentKontrakan = Kontrakan::where('admin_id', $adminId) + $recentKontrakan = ($isSuperAdmin ? Kontrakan::query() : Kontrakan::where('admin_id', $adminId)) ->latest() ->select('id', 'nama', 'alamat', 'harga', 'jarak', 'jumlah_kamar', 'created_at') // Only needed columns ->take(5) @@ -54,7 +54,7 @@ public function index() $chartData = Cache::remember('dashboard_charts_admin_' . $adminId, 600, function () use ($adminId, $isSuperAdmin) { // 1. Harga Kontrakan (Top 5) - $hargaKontrakan = Kontrakan::where('admin_id', $adminId)->select('nama', 'harga') + $hargaKontrakan = ($isSuperAdmin ? Kontrakan::query() : Kontrakan::where('admin_id', $adminId))->select('nama', 'harga') ->orderBy('harga', 'desc') ->take(5) ->get(); @@ -73,7 +73,7 @@ public function index() // 3. Distribusi Jarak (Single Query dengan CASE) $jarakKontrakan = DB::table('kontrakans') - ->where('admin_id', $adminId) + ->when(!$isSuperAdmin, function ($q) use ($adminId) { return $q->where('admin_id', $adminId); }) ->selectRaw(" SUM(CASE WHEN jarak <= 500 THEN 1 ELSE 0 END) as dekat, SUM(CASE WHEN jarak > 500 AND jarak <= 1000 THEN 1 ELSE 0 END) as sedang, @@ -91,7 +91,7 @@ public function index() // 4. Statistik Aggregate (Single Query) $kontrakanStats = DB::table('kontrakans') - ->where('admin_id', $adminId) + ->when(!$isSuperAdmin, function ($q) use ($adminId) { return $q->where('admin_id', $adminId); }) ->selectRaw(' AVG(harga) as avg_harga, AVG(jarak) as avg_jarak, @@ -102,7 +102,7 @@ public function index() ->first(); // 5. Top Kontrakan by Harga - $topKontrakan = Kontrakan::where('admin_id', $adminId)->select('nama', 'harga', 'jumlah_kamar', 'jarak') + $topKontrakan = ($isSuperAdmin ? Kontrakan::query() : Kontrakan::where('admin_id', $adminId))->select('nama', 'harga', 'jumlah_kamar', 'jarak') ->orderBy('harga', 'desc') ->take(5) ->get(); @@ -155,7 +155,7 @@ public function index() // ========== INSIGHT REAL-TIME (No Cache) ========== $realtimeJarakKontrakan = DB::table('kontrakans') - ->where('admin_id', $adminId) + ->when(!$isSuperAdmin, function ($q) use ($adminId) { return $q->where('admin_id', $adminId); }) ->selectRaw(" SUM(CASE WHEN jarak <= 500 THEN 1 ELSE 0 END) as dekat, SUM(CASE WHEN jarak > 500 AND jarak <= 1000 THEN 1 ELSE 0 END) as sedang, @@ -164,7 +164,7 @@ public function index() ->first(); $realtimeKontrakanStats = DB::table('kontrakans') - ->where('admin_id', $adminId) + ->when(!$isSuperAdmin, function ($q) use ($adminId) { return $q->where('admin_id', $adminId); }) ->selectRaw(' AVG(harga) as avg_harga, AVG(jarak) as avg_jarak, diff --git a/spk_kontrakan/app/Http/Controllers/KontrakanController.php b/spk_kontrakan/app/Http/Controllers/KontrakanController.php index 3eccb45..677ddb9 100644 --- a/spk_kontrakan/app/Http/Controllers/KontrakanController.php +++ b/spk_kontrakan/app/Http/Controllers/KontrakanController.php @@ -114,24 +114,31 @@ public function index(Request $request) // ========== STATISTIK & DATA UNTUK FILTER UI ========== - // Total kontrakan (semua data, tanpa filter) - $totalKontrakan = Kontrakan::count(); - + // Statistik hanya berdasarkan kontrakan milik admin yang sedang login + $statQuery = Kontrakan::query(); + + if ($admin && $admin->role !== 'super_admin') { + $statQuery->where('admin_id', $admin->id); + } + + $totalKontrakan = $statQuery->count(); + // Jumlah hasil setelah filter $filteredCount = $kontrakan->total(); - - // Range harga untuk slider (min & max dari database) - $hasData = Kontrakan::count() > 0; - $hargaMin = $hasData ? (Kontrakan::min('harga') ?? 0) : 0; - $hargaMax = $hasData ? (Kontrakan::max('harga') ?? 0) : 10000000; - - // Range jarak untuk slider (dalam km) - $jarakMaxDb = $hasData ? (Kontrakan::max('jarak') ?? 0) : 0; // dalam meter - $jarakMaxKm = $jarakMaxDb > 0 ? ceil($jarakMaxDb / 1000) : 0; // konversi ke km, bulatkan ke atas - - // Range jumlah_kamar untuk input - $jumlah_kamarMin = $hasData ? (Kontrakan::min('jumlah_kamar') ?? 0) : 0; - $jumlah_kamarMax = $hasData ? (Kontrakan::max('jumlah_kamar') ?? 0) : 0; + + $hasData = $totalKontrakan > 0; + + // Range harga untuk slider/statistik + $hargaMin = $hasData ? ($statQuery->min('harga') ?? 0) : 0; + $hargaMax = $hasData ? ($statQuery->max('harga') ?? 0) : 0; + + // Range jarak untuk slider/statistik + $jarakMaxDb = $hasData ? ($statQuery->max('jarak') ?? 0) : 0; + $jarakMaxKm = $jarakMaxDb > 0 ? ceil($jarakMaxDb / 1000) : 0; + + // Range jumlah kamar untuk statistik + $jumlah_kamarMin = $hasData ? ($statQuery->min('jumlah_kamar') ?? 0) : 0; + $jumlah_kamarMax = $hasData ? ($statQuery->max('jumlah_kamar') ?? 0) : 0; // Daftar fasilitas unik untuk checkbox (extract dari semua kontrakan) // Daftar fasilitas unik untuk checkbox (extract dari semua kontrakan) diff --git a/spk_kontrakan/resources/views/Kriteria/create.blade.php b/spk_kontrakan/resources/views/Kriteria/create.blade.php index 80e63ff..5428800 100644 --- a/spk_kontrakan/resources/views/Kriteria/create.blade.php +++ b/spk_kontrakan/resources/views/Kriteria/create.blade.php @@ -1,6 +1,6 @@ @extends('layouts.admin') -@section('title', 'Tambah Kriteria') +@section('title', 'Tambah Master Kriteria') @section('content')
@@ -79,7 +79,7 @@ @@ -87,7 +87,7 @@ @@ -165,7 +165,7 @@ class="form-control border-start-0 @error('nama_kriteria') is-invalid @enderror"
@@ -187,7 +187,7 @@ class="form-control border-start-0 @error('bobot') is-invalid @enderror"
{{ $message }}
@enderror
- Nilai bobot antara 0 - 1 (contoh: 0.25 atau 0.3) + Bobot ini hanya digunakan sebagai bobot default/simulasi untuk validasi perhitungan di web. Bobot utama rekomendasi tetap dipilih oleh user melalui aplikasi mobile.
diff --git a/spk_kontrakan/resources/views/Kriteria/edit.blade.php b/spk_kontrakan/resources/views/Kriteria/edit.blade.php index 7b205da..0a9852c 100644 --- a/spk_kontrakan/resources/views/Kriteria/edit.blade.php +++ b/spk_kontrakan/resources/views/Kriteria/edit.blade.php @@ -1,6 +1,6 @@ @extends('layouts.admin') -@section('title', 'Edit Kriteria') +@section('title', 'Edit Master Kriteria') @section('content')
@@ -79,7 +79,7 @@ @@ -87,7 +87,7 @@ @@ -166,7 +166,7 @@ class="form-control border-start-0 @error('nama_kriteria') is-invalid @enderror"
@@ -188,7 +188,7 @@ class="form-control border-start-0 @error('bobot') is-invalid @enderror"
{{ $message }}
@enderror
- Nilai bobot antara 0 - 1 (contoh: 0.25 atau 0.3) + Bobot ini hanya digunakan sebagai bobot default/simulasi untuk validasi perhitungan di web. Bobot utama rekomendasi tetap dipilih oleh user melalui aplikasi mobile.
diff --git a/spk_kontrakan/resources/views/Kriteria/index.blade.php b/spk_kontrakan/resources/views/Kriteria/index.blade.php index d1f66cf..4e35c09 100644 --- a/spk_kontrakan/resources/views/Kriteria/index.blade.php +++ b/spk_kontrakan/resources/views/Kriteria/index.blade.php @@ -1,6 +1,6 @@ @extends('layouts.admin') -@section('title', 'Kelola Kriteria SAW') +@section('title', 'Master Kriteria SAW') @section('content')
@@ -309,7 +309,7 @@

- 📊 Kelola Kriteria SAW + 📊 Master Kriteria SAW 📊 Kriteria

@@ -320,7 +320,7 @@

@@ -444,7 +444,7 @@ class="form-control border-start-0"
Belum ada kriteria

Mulai tambahkan kriteria untuk penilaian SAW (Simple Additive Weighting) agar dapat melakukan perhitungan rekomendasi yang akurat.

- Tambah Kriteria Pertama + Tambah Master Kriteria Pertama
@else @@ -629,7 +629,7 @@ class="form-control border-start-0" @@ -749,7 +749,7 @@ class="btn btn-sm rounded-3"
-
📋 Informasi Kriteria SAW
+
📋 Informasi Master Kriteria SAW
- +
@@ -359,8 +359,8 @@
- Kriteria dan Bobot yang Digunakan - Kriteria & Bobot + Kriteria dan Bobot Simulasi yang Digunakan + Kriteria & Bobot Simulasi
@@ -663,7 +663,7 @@ class="progress-bar bg-{{ $barColor }}" @if($index == 0)
- ✨ Rekomendasi Terbaik Berdasarkan Kriteria Anda + ✨ Rekomendasi Terbaik Berdasarkan Bobot Simulasi
@elseif($index == 1)
diff --git a/spk_kontrakan/resources/views/SAW/index.blade.php b/spk_kontrakan/resources/views/SAW/index.blade.php index 80e5c1d..de1d8ed 100644 --- a/spk_kontrakan/resources/views/SAW/index.blade.php +++ b/spk_kontrakan/resources/views/SAW/index.blade.php @@ -266,7 +266,7 @@ - + diff --git a/spk_kontrakan/resources/views/dashboard/index.blade.php b/spk_kontrakan/resources/views/dashboard/index.blade.php index eab0653..cc1d639 100644 --- a/spk_kontrakan/resources/views/dashboard/index.blade.php +++ b/spk_kontrakan/resources/views/dashboard/index.blade.php @@ -391,6 +391,146 @@ .border-admin { border-color: #667eea !important; } + + .modern-section-card { + border: none; + border-radius: 18px; + box-shadow: 0 14px 34px rgba(15, 23, 42, 0.08); + overflow: hidden; + background: #ffffff; + height: 100%; + } + + .modern-section-header { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; + padding: 18px 22px; + border-bottom: 1px solid rgba(226, 232, 240, 0.9); + background: linear-gradient(135deg, rgba(102, 126, 234, 0.08), rgba(255, 255, 255, 0.95)); + } + + .modern-section-title { + display: flex; + align-items: center; + gap: 10px; + font-weight: 800; + font-size: 1.05rem; + color: #4f46e5; + margin: 0; + } + + .modern-count-badge { + background: rgba(99, 102, 241, 0.12); + color: #4f46e5; + font-size: 0.75rem; + font-weight: 700; + padding: 6px 12px; + border-radius: 999px; + white-space: nowrap; + } + + .modern-list { + padding: 16px 18px 8px 18px; + } + + .modern-item { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 14px; + padding: 14px 12px; + border-radius: 14px; + transition: all 0.25s ease; + border: 1px solid rgba(226, 232, 240, 0.8); + margin-bottom: 10px; + background: #fafbff; + } + + .modern-item:hover { + background: #f4f6ff; + border-color: rgba(99, 102, 241, 0.28); + transform: translateY(-2px); + box-shadow: 0 10px 20px rgba(15, 23, 42, 0.06); + } + + .modern-item-left { + flex: 1; + min-width: 0; + } + + .modern-item-title { + font-weight: 800; + font-size: 0.96rem; + color: #111827; + margin-bottom: 6px; + } + + .modern-item-subtitle { + font-size: 0.82rem; + color: #64748b; + line-height: 1.5; + margin-bottom: 8px; + } + + .modern-meta { + display: flex; + flex-wrap: wrap; + gap: 8px; + } + + .modern-meta-badge { + font-size: 0.72rem; + padding: 5px 10px; + border-radius: 999px; + font-weight: 700; + background: #eef2ff; + color: #4f46e5; + } + + .modern-price { + min-width: 115px; + text-align: center; + padding: 9px 12px; + border-radius: 12px; + font-size: 0.82rem; + font-weight: 800; + white-space: nowrap; + background: #dcfce7; + color: #15803d; + } + + .modern-price.kontrakan { + background: #eef2ff; + color: #4f46e5; + } + + .modern-empty { + text-align: center; + padding: 36px 20px; + color: #94a3b8; + } + + .modern-footer { + padding: 4px 18px 18px 18px; + } + + .modern-btn { + border-radius: 12px; + font-weight: 700; + padding: 9px 15px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border: none; + color: #fff; + } + + .modern-btn:hover { + color: #fff; + transform: translateY(-1px); + box-shadow: 0 10px 20px rgba(102, 126, 234, 0.25); + } +
KriteriaMaster Kriteria SAW Bobot Tipe