MIF_E31230069/spk_kontrakan/app/Http/Controllers/DashboardController.php

238 lines
10 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Kontrakan;
use App\Models\Laundry;
use App\Models\Kriteria;
use App\Models\Booking;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Auth;
class DashboardController extends Controller
{
/**
* Display dashboard with statistics and recent data
* OPTIMIZED VERSION - Cache untuk 5 menit
*
* @return \Illuminate\View\View
*/
public function index()
{
$admin = auth('admin')->user() ?? Auth::user();
$adminId = $admin->id;
$isSuperAdmin = $admin->role === 'super_admin';
// ========== CACHE STATISTIK (5 menit) ==========
$stats = Cache::remember('dashboard_stats_admin_' . $adminId, 300, function () use ($adminId, $isSuperAdmin) {
return [
'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 = ($isSuperAdmin ? Kontrakan::query() : Kontrakan::where('admin_id', $adminId))
->latest()
->select('id', 'nama', 'alamat', 'harga', 'jarak', 'jumlah_kamar', 'created_at') // Only needed columns
->take(5)
->get();
$recentLaundry = Laundry::with(['layanan' => function($query) {
$query->select('laundry_id', 'jenis_layanan', 'harga')
->orderBy('harga', 'asc')
->limit(1); // Only cheapest service
}])
->select('id', 'nama', 'alamat', 'jarak', 'created_at')
->latest()
->take(5)
->get();
// ========== DATA CHART (Cache 10 menit) ==========
$chartData = Cache::remember('dashboard_charts_admin_' . $adminId, 600, function () use ($adminId, $isSuperAdmin) {
// 1. Harga Kontrakan (Top 5)
$hargaKontrakan = ($isSuperAdmin ? Kontrakan::query() : Kontrakan::where('admin_id', $adminId))->select('nama', 'harga')
->orderBy('harga', 'desc')
->take(5)
->get();
// 2. Harga Laundry (Top 5 dengan SUBQUERY - LEBIH CEPAT!)
$hargaLaundry = DB::table('laundry')
->join(DB::raw('(
SELECT laundry_id, MIN(harga) as min_harga
FROM layanan_laundry
GROUP BY laundry_id
) as layanan'), 'laundry.id', '=', 'layanan.laundry_id')
->select('laundry.nama', 'layanan.min_harga as harga')
->orderBy('layanan.min_harga', 'asc')
->take(5)
->get();
// 3. Distribusi Jarak (Single Query dengan CASE)
$jarakKontrakan = DB::table('kontrakans')
->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,
SUM(CASE WHEN jarak > 1000 THEN 1 ELSE 0 END) as jauh
")
->first();
$jarakLaundry = DB::table('laundry')
->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,
SUM(CASE WHEN jarak > 1000 THEN 1 ELSE 0 END) as jauh
")
->first();
// 4. Statistik Aggregate (Single Query)
$kontrakanStats = DB::table('kontrakans')
->when(!$isSuperAdmin, function ($q) use ($adminId) { return $q->where('admin_id', $adminId); })
->selectRaw('
AVG(harga) as avg_harga,
AVG(jarak) as avg_jarak,
AVG(jumlah_kamar) as avg_kamar,
MIN(harga) as min_harga,
MAX(harga) as max_harga
')
->first();
// 5. Top Kontrakan by Harga
$topKontrakan = ($isSuperAdmin ? Kontrakan::query() : Kontrakan::where('admin_id', $adminId))->select('nama', 'harga', 'jumlah_kamar', 'jarak')
->orderBy('harga', 'desc')
->take(5)
->get();
// 6. Monthly Data (Optimized dengan GROUP BY)
$monthlyData = DB::table(DB::raw('
(SELECT DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL n MONTH), "%Y-%m") as month
FROM (SELECT 0 as n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) as numbers) as months
'))
->leftJoin(DB::raw('kontrakans'), DB::raw('DATE_FORMAT(kontrakans.created_at, "%Y-%m")'), '=', 'months.month')
->leftJoin(DB::raw('laundry'), DB::raw('DATE_FORMAT(laundry.created_at, "%Y-%m")'), '=', 'months.month')
->selectRaw('
months.month,
COUNT(DISTINCT kontrakans.id) as kontrakan_count,
COUNT(DISTINCT laundry.id) as laundry_count
')
->groupBy('months.month')
->orderBy('months.month', 'asc')
->get()
->map(function($item) {
return [
'month' => date('M Y', strtotime($item->month . '-01')),
'kontrakan' => $item->kontrakan_count,
'laundry' => $item->laundry_count
];
});
return [
'hargaKontrakan' => $hargaKontrakan,
'hargaLaundry' => $hargaLaundry,
'jarakKontrakan' => [
'dekat' => $jarakKontrakan->dekat ?? 0,
'sedang' => $jarakKontrakan->sedang ?? 0,
'jauh' => $jarakKontrakan->jauh ?? 0,
],
'jarakLaundry' => [
'dekat' => $jarakLaundry->dekat ?? 0,
'sedang' => $jarakLaundry->sedang ?? 0,
'jauh' => $jarakLaundry->jauh ?? 0,
],
'avgHargaKontrakan' => $kontrakanStats->avg_harga ?? 0,
'avgJarakKontrakan' => $kontrakanStats->avg_jarak ?? 0,
'avgKamarKontrakan' => $kontrakanStats->avg_kamar ?? 0,
'minHargaKontrakan' => $kontrakanStats->min_harga ?? 0,
'maxHargaKontrakan' => $kontrakanStats->max_harga ?? 0,
'topKontrakan' => $topKontrakan,
'monthlyData' => $monthlyData,
];
});
// ========== INSIGHT REAL-TIME (No Cache) ==========
$realtimeJarakKontrakan = DB::table('kontrakans')
->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,
SUM(CASE WHEN jarak > 1000 THEN 1 ELSE 0 END) as jauh
")
->first();
$realtimeKontrakanStats = DB::table('kontrakans')
->when(!$isSuperAdmin, function ($q) use ($adminId) { return $q->where('admin_id', $adminId); })
->selectRaw('
AVG(harga) as avg_harga,
AVG(jarak) as avg_jarak,
AVG(jumlah_kamar) as avg_kamar,
MIN(harga) as min_harga,
MAX(harga) as max_harga
')
->first();
// ========== TAMBAHAN DATA YANG HILANG ==========
$additionalData = [
// Data booking
'totalBookings' => Booking::whereHas('kontrakan', function ($query) use ($adminId) {
$query->where('admin_id', $adminId);
})->count(),
'bookingMenunggu' => Booking::whereHas('kontrakan', function ($query) use ($adminId) {
$query->where('admin_id', $adminId);
})->whereRaw('LOWER(status) = ?', ['menunggu'])->count(),
'bookingSelesai' => Booking::whereHas('kontrakan', function ($query) use ($adminId) {
$query->where('admin_id', $adminId);
})->whereRaw('LOWER(status) = ?', ['selesai'])->count(),
// Data admin (dari tabel admins)
'totalAdmins' => \App\Models\Admin::where('role', 'admin')->count() ?? 1,
// Average kecepatan laundry (dari estimasi_selesai dalam jam)
'avgKecepatan' => round(DB::table('layanan_laundry')
->where('estimasi_selesai', '>', 0)
->avg('estimasi_selesai') ?? 24, 1),
];
// Merge semua data
$data = array_merge($stats, $chartData, $additionalData, [
'jarakKontrakan' => [
'dekat' => $realtimeJarakKontrakan->dekat ?? 0,
'sedang' => $realtimeJarakKontrakan->sedang ?? 0,
'jauh' => $realtimeJarakKontrakan->jauh ?? 0,
],
'avgHargaKontrakan' => $realtimeKontrakanStats->avg_harga ?? 0,
'avgJarakKontrakan' => $realtimeKontrakanStats->avg_jarak ?? 0,
'avgKamarKontrakan' => $realtimeKontrakanStats->avg_kamar ?? 0,
'minHargaKontrakan' => $realtimeKontrakanStats->min_harga ?? 0,
'maxHargaKontrakan' => $realtimeKontrakanStats->max_harga ?? 0,
'recentBookings' => Booking::whereHas('kontrakan', function ($query) use ($adminId) {
$query->where('admin_id', $adminId);
})
->with('kontrakan:id,nama,admin_id')
->latest()
->take(5)
->get(),
'recentKontrakan' => $recentKontrakan,
'recentLaundry' => $recentLaundry,
]);
return view('dashboard.index', $data);
}
/**
* Clear dashboard cache (optional - bisa dipanggil manual)
*/
public function clearCache()
{
Cache::forget('dashboard_stats');
Cache::forget('dashboard_charts');
return redirect()->route('dashboard')
->with('success', 'Cache dashboard berhasil dibersihkan!');
}
}