MIF_E31230069/spk_kontrakan/resources/views/dashboard/index.blade.php

1188 lines
56 KiB
PHP

@extends('layouts.admin')
@section('title', 'Dashboard Admin')
@section('content')
<!-- Load Skeleton Loader Script -->
@include('components.skeleton-script')
<div class="container-fluid px-2 px-md-4">
<style>
.header-dashboard {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16px;
padding: 2.5rem 2rem;
color: white;
margin-bottom: 2.5rem;
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.2);
position: relative;
overflow: hidden;
}
.header-dashboard::before {
content: '';
position: absolute;
top: -50%;
right: -10%;
width: 300px;
height: 300px;
background: rgba(255,255,255,0.1);
border-radius: 50%;
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-20px); }
}
.header-dashboard h2 {
font-size: 2.2rem;
font-weight: 800;
margin-bottom: 0.5rem;
position: relative;
z-index: 1;
}
.header-dashboard p {
opacity: 0.95;
margin-bottom: 0;
font-size: 1.1rem;
position: relative;
z-index: 1;
}
.stats-card {
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
border: none !important;
border-radius: 16px;
overflow: hidden;
position: relative;
}
.stats-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: inherit;
filter: brightness(1.1);
opacity: 0;
transition: opacity 0.4s ease;
pointer-events: none;
z-index: 0;
}
.stats-card:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 20px 40px rgba(0,0,0,0.15) !important;
}
.stats-card:hover::before {
opacity: 1;
}
.stats-card .btn {
pointer-events: auto;
cursor: pointer;
}
.chart-card {
border: none;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
overflow: hidden;
}
.chart-card .card-header {
border-bottom: 2px solid #667eea;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
padding: 1.5rem;
font-weight: 700;
}
.section-header {
color: #667eea;
font-size: 1.2rem;
font-weight: 800;
padding-bottom: 1rem;
border-bottom: 3px solid #667eea;
margin-bottom: 2rem;
position: relative;
}
.section-header::after {
content: '';
position: absolute;
bottom: -3px;
left: 0;
width: 60px;
height: 3px;
background: linear-gradient(90deg, #667eea, #764ba2);
}
/* Mobile Responsive Enhancements */
@media (max-width: 768px) {
.header-dashboard {
padding: 2rem 1.5rem;
margin-bottom: 2rem;
}
.header-dashboard h2 {
font-size: 1.8rem;
}
.header-dashboard p {
font-size: 1rem;
}
.stats-card {
margin-bottom: 1.5rem;
}
.chart-card .card-header {
padding: 1.25rem;
}
.section-header {
font-size: 1.1rem;
}
}
@media (max-width: 480px) {
.header-dashboard {
padding: 1.5rem 1rem;
}
.header-dashboard h2 {
font-size: 1.6rem;
}
.stats-card:hover {
transform: translateY(-4px) scale(1.01);
}
}
/* Skeleton Loader Styles */
.skeleton {
background: linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
border-radius: 4px;
}
html.dark-mode .skeleton {
background: linear-gradient(90deg, #3a3a3a 25%, #4a4a4a 50%, #3a3a3a 75%);
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* Konsistensi Warna Admin - Purple Theme */
.btn-admin-outline {
color: #667eea;
border: 2px solid #667eea;
background: transparent;
font-weight: 600;
border-radius: 10px;
transition: all 0.3s ease;
}
.btn-admin-outline:hover {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-color: #667eea;
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.3);
}
.btn-admin-solid {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
font-weight: 600;
border-radius: 10px;
transition: all 0.3s ease;
}
.btn-admin-solid:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
color: white;
}
.text-admin-primary {
color: #667eea !important;
}
.bg-admin-light {
background: rgba(102, 126, 234, 0.1) !important;
}
.border-admin {
border-color: #667eea !important;
}
</style>
<!-- Header Section -->
<div class="header-dashboard">
<h2 class="mb-2">📊 Dashboard Analytics</h2>
<p class="mb-0">Selamat datang, <strong>{{ Auth::user()->name }}!</strong> 👋</p>
</div>
<!-- Statistics Cards -->
<div class="row g-3 g-md-4 mb-4 mb-md-5" id="statsContainer">
<!-- Card Kontrakan - Purple Theme -->
<div class="col-6 col-lg-3">
<div class="card stats-card h-100" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none !important; box-shadow: 0 8px 25px rgba(102, 126, 234, 0.35); position: relative; z-index: 1;">
<div class="card-body text-white p-3 p-md-4" style="position: relative; z-index: 2;">
<div class="d-flex justify-content-between align-items-start mb-3">
<div class="flex-grow-1">
<p class="mb-2 opacity-75 small fw-semibold"><i class="bi bi-house-door me-1"></i>Total Kontrakan</p>
<h2 class="mb-2 mb-md-3 fw-bold display-6 counter" data-target="{{ $jumlahKontrakan }}">0</h2>
<p class="mb-0 opacity-85 small d-none d-md-block">
<i class="bi bi-currency-dollar me-1"></i>
Rp {{ number_format($avgHargaKontrakan, 0, ',', '.') }}
</p>
</div>
<div class="rounded-circle p-2 p-md-3 d-flex align-items-center justify-content-center d-none d-sm-flex" style="min-width: 50px; min-height: 50px; background: rgba(255,255,255,0.25);">
<i class="bi bi-building" style="font-size: 1.5rem;"></i>
</div>
</div>
<button type="button" class="btn btn-light btn-sm w-100 fw-semibold d-none d-md-block" style="position: relative; z-index: 3; color: #764ba2;" data-bs-toggle="modal" data-bs-target="#kontrakanModal">
<i class="bi bi-arrow-right me-2"></i>Lihat Detail
</button>
</div>
</div>
</div>
<!-- Card Laundry - Purple Theme -->
<div class="col-6 col-lg-3">
<div class="card stats-card h-100" style="background: linear-gradient(135deg, #818cf8 0%, #667eea 100%); border: none !important; box-shadow: 0 8px 25px rgba(102, 126, 234, 0.35); position: relative; z-index: 1;">
<div class="card-body text-white p-3 p-md-4" style="position: relative; z-index: 2;">
<div class="d-flex justify-content-between align-items-start mb-3">
<div class="flex-grow-1">
<p class="mb-2 opacity-75 small fw-semibold"><i class="bi bi-droplet me-1"></i>Total Laundry</p>
<h2 class="mb-2 mb-md-3 fw-bold display-6 counter" data-target="{{ $jumlahLaundry }}">0</h2>
<p class="mb-0 opacity-85 small d-none d-md-block">
<i class="bi bi-clock-history me-1"></i>
{{ $avgKecepatan ?? 'N/A' }} jam rata-rata
</p>
</div>
<div class="rounded-circle p-2 p-md-3 d-flex align-items-center justify-content-center d-none d-sm-flex" style="min-width: 50px; min-height: 50px; background: rgba(255,255,255,0.25);">
<i class="bi bi-water" style="font-size: 1.5rem;"></i>
</div>
</div>
<button type="button" class="btn btn-light btn-sm w-100 fw-semibold d-none d-md-block" style="position: relative; z-index: 3; color: #667eea;" data-bs-toggle="modal" data-bs-target="#laundryModal">
<i class="bi bi-arrow-right me-2"></i>Lihat Detail
</button>
</div>
</div>
</div>
<!-- Card Kriteria -->
<div class="col-6 col-lg-3">
<div class="card stats-card h-100" style="background: linear-gradient(135deg, #6366f1 0%, #4f46e5 50%, #4338ca 100%); border: none !important; box-shadow: 0 8px 25px rgba(99, 102, 241, 0.3); position: relative; z-index: 1;">
<div class="card-body text-white p-3 p-md-4" style="position: relative; z-index: 2;">
<div class="d-flex justify-content-between align-items-start mb-3">
<div class="flex-grow-1">
<p class="mb-2 opacity-75 small fw-semibold"><i class="bi bi-sliders me-1"></i>Total Kriteria</p>
<h2 class="mb-2 mb-md-3 fw-bold display-6 counter" data-target="{{ $jumlahKriteria }}">0</h2>
<p class="mb-0 opacity-85 small d-none d-md-block">
<i class="bi bi-star-fill me-1"></i>
Untuk perhitungan SAW
</p>
</div>
<div class="rounded-circle p-2 p-md-3 d-flex align-items-center justify-content-center d-none d-sm-flex" style="min-width: 50px; min-height: 50px; background: rgba(255,255,255,0.25);">
<i class="bi bi-list-check" style="font-size: 1.5rem;"></i>
</div>
</div>
<button type="button" class="btn btn-light btn-sm w-100 fw-semibold d-none d-md-block" style="position: relative; z-index: 3; color: #4338ca;" data-bs-toggle="modal" data-bs-target="#kriteriaModal">
<i class="bi bi-arrow-right me-2"></i>Lihat Detail
</button>
</div>
</div>
</div>
<!-- Card SAW - Amber/Gold Tone -->
<div class="col-6 col-lg-3">
<div class="card stats-card h-100" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 50%, #b45309 100%); border: none !important; box-shadow: 0 8px 25px rgba(217, 119, 6, 0.35); position: relative; z-index: 1;">
<div class="card-body text-white p-3 p-md-4" style="position: relative; z-index: 2;">
<div class="d-flex justify-content-between align-items-start mb-3">
<div class="flex-grow-1">
<p class="mb-2 opacity-75 small fw-semibold"><i class="bi bi-calculator me-1"></i>Sistem Rekomendasi</p>
<h2 class="mb-2 mb-md-3 fw-bold display-6">Analisis</h2>
<p class="mb-0 opacity-85 small d-none d-md-block">
<i class="bi bi-diagram-3 me-1"></i>
Keputusan Terpadu
</p>
</div>
<div class="rounded-circle p-2 p-md-3 d-flex align-items-center justify-content-center d-none d-sm-flex" style="min-width: 50px; min-height: 50px; background: rgba(255,255,255,0.25);">
<i class="bi bi-calculator" style="font-size: 1.5rem;"></i>
</div>
</div>
<button type="button" class="btn btn-light btn-sm w-100 fw-semibold d-none d-md-block" style="position: relative; z-index: 3; color: #b45309;" data-bs-toggle="modal" data-bs-target="#sawModal">
<i class="bi bi-arrow-right me-2"></i>Lihat Detail
</button>
</div>
</div>
</div>
</div>
<!-- Charts Section -->
<div class="row g-3 g-md-4 mb-4 mb-md-5">
<!-- Bar Chart: Perbandingan Harga -->
<div class="col-12 col-xl-8">
<div class="card chart-card h-100">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-bold" style="color: #667eea;">
<i class="bi bi-bar-chart-fill me-2"></i>
<span class="d-none d-md-inline">Perbandingan Harga (Top 5)</span>
<span class="d-md-none">Harga (Top 5)</span>
</h5>
</div>
<div class="card-body p-3 p-md-4">
<canvas id="hargaChart" height="80"></canvas>
</div>
</div>
</div>
<!-- Donut Charts: Distribusi Jarak -->
<div class="col-12 col-xl-4">
<div class="card chart-card h-100">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-bold" style="color: #667eea;">
<i class="bi bi-pie-chart-fill me-2"></i>
<span class="d-none d-md-inline">Distribusi Jarak</span>
<span class="d-md-none">Jarak</span>
</h5>
</div>
<div class="card-body p-3 p-md-4">
<canvas id="jarakChart" height="200"></canvas>
<div class="mt-3">
<div class="d-flex justify-content-between align-items-center mb-2 py-2 border-bottom">
<span class="small"><span class="badge" style="background-color: #198754;"></span> <span class="d-none d-sm-inline">Dekat (≤500m)</span><span class="d-sm-none">Dekat</span></span>
<strong style="color: #667eea;">{{ $jarakKontrakan['dekat'] + $jarakLaundry['dekat'] }}</strong>
</div>
<div class="d-flex justify-content-between align-items-center mb-2 py-2 border-bottom">
<span class="small"><span class="badge" style="background-color: #ffc107;"></span> <span class="d-none d-sm-inline">Sedang (501-1000m)</span><span class="d-sm-none">Sedang</span></span>
<strong style="color: #667eea;">{{ $jarakKontrakan['sedang'] + $jarakLaundry['sedang'] }}</strong>
</div>
<div class="d-flex justify-content-between align-items-center py-2">
<span class="small"><span class="badge" style="background-color: #dc3545;"></span> <span class="d-none d-sm-inline">Jauh (>1000m)</span><span class="d-sm-none">Jauh</span></span>
<strong style="color: #667eea;">{{ $jarakKontrakan['jauh'] + $jarakLaundry['jauh'] }}</strong>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Line Chart: Trend Data -->
<div class="row g-3 g-md-4 mb-4 mb-md-5">
<div class="col-12">
<div class="card chart-card">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-bold" style="color: #667eea;">
<i class="bi bi-graph-up me-2"></i>
<span class="d-none d-md-inline">Trend Data (6 Bulan Terakhir)</span>
<span class="d-md-none">Trend Data</span>
</h5>
</div>
<div class="card-body p-3 p-md-4">
<canvas id="trendChart" height="60"></canvas>
</div>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="row g-4 mb-4">
<div class="col-12">
<div class="card chart-card">
<div class="card-body">
<h5 class="card-title mb-4 fw-bold" style="color: #667eea;">
<i class="bi bi-lightning-charge me-2"></i>
Quick Actions
</h5>
<div class="row g-3">
<div class="col-md-3">
<a href="{{ route('kontrakan.create') }}" class="btn btn-admin-outline w-100 py-3">
<i class="bi bi-building me-2"></i> Tambah Kontrakan
</a>
</div>
<div class="col-md-3">
<a href="{{ route('laundry.create') }}" class="btn btn-admin-outline w-100 py-3">
<i class="bi bi-water me-2"></i> Tambah Laundry
</a>
</div>
<div class="col-md-3">
<a href="{{ route('kriteria.create') }}" class="btn btn-admin-outline w-100 py-3">
<i class="bi bi-list-check me-2"></i> Tambah Kriteria
</a>
</div>
<div class="col-md-3">
<a href="{{ route('saw.index') }}" class="btn btn-admin-solid w-100 py-3">
<i class="bi bi-calculator me-2"></i> Sistem Analisis
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Additional Stats Section -->
<div class="row g-4 mb-4">
<!-- Activity Stats -->
<div class="col-md-6 col-lg-3">
<div class="card border-0 shadow-sm" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 12px;">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<p class="mb-2 opacity-75 small fw-semibold">Total Review</p>
<h3 class="fw-bold mb-0">{{ $totalReviews ?? 0 }}</h3>
</div>
<div style="font-size: 2.5rem; opacity: 0.3;"></div>
</div>
</div>
</div>
</div>
<!-- System Status -->
<div class="col-md-6 col-lg-3">
<div class="card border-0 shadow-sm" style="background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; border-radius: 12px;">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<p class="mb-2 opacity-75 small fw-semibold">System Status</p>
<h3 class="fw-bold mb-0"><i class="bi bi-check-circle-fill"></i> Aktif</h3>
</div>
<div style="font-size: 2.5rem; opacity: 0.3;"></div>
</div>
</div>
</div>
</div>
<!-- Last Backup -->
<div class="col-md-6 col-lg-3">
<div class="card border-0 shadow-sm" style="background: linear-gradient(135deg, #17a2b8 0%, #20c997 100%); color: white; border-radius: 12px;">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<p class="mb-2 opacity-75 small fw-semibold">Database Size</p>
<h3 class="fw-bold mb-0">{{ number_format(round((($jumlahKontrakan + $jumlahLaundry) * 0.15) / 1024, 2), 2, ',', '.') }} MB</h3>
</div>
<div style="font-size: 2.5rem; opacity: 0.3;">💾</div>
</div>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="col-md-6 col-lg-3">
<div class="card border-0 shadow-sm" style="background: linear-gradient(135deg, #fd7e14 0%, #fd7e14 100%); color: white; border-radius: 12px;">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<p class="mb-2 opacity-75 small fw-semibold">Admin Users</p>
<h3 class="fw-bold mb-0">{{ $totalAdmins ?? 1 }}</h3>
</div>
<div style="font-size: 2.5rem; opacity: 0.3;">👤</div>
</div>
</div>
</div>
</div>
</div>
<!-- Recent Data Tables -->
<div class="row g-4">
<!-- Recent Kontrakan -->
<div class="col-lg-6">
<div class="card chart-card">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-bold" style="color: #667eea;">
<i class="bi bi-building me-2"></i>
Data Kontrakan Terbaru
</h5>
</div>
<div class="card-body">
@if($recentKontrakan && $recentKontrakan->count() > 0)
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th>Nama</th>
<th>Lokasi</th>
<th class="text-end">Harga</th>
</tr>
</thead>
<tbody>
@foreach($recentKontrakan as $k)
<tr>
<td>
<div class="fw-semibold">{{ $k->nama }}</div>
</td>
<td>
<small class="text-muted">
<i class="bi bi-geo-alt-fill me-1"></i>{{ $k->lokasi ?? $k->alamat }}
</small>
</td>
<td class="text-end">
<span class="badge px-3 py-2" style="background-color: rgba(102, 126, 234, 0.15); color: #667eea;">
Rp {{ number_format($k->harga, 0, ',', '.') }}
</span>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="text-center py-4">
<i class="bi bi-inbox text-muted" style="font-size: 3rem;"></i>
<p class="text-muted mt-2 mb-0">Belum ada data kontrakan</p>
</div>
@endif
<div class="mt-3">
<a href="{{ route('kontrakan.index') }}" class="btn btn-sm" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
<i class="bi bi-arrow-right-circle me-1"></i> Lihat Semua Data
</a>
</div>
</div>
</div>
</div>
<!-- Recent Laundry -->
<div class="col-lg-6">
<div class="card chart-card">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-bold" style="color: #667eea;">
<i class="bi bi-basket3 me-2"></i>
Data Laundry Terbaru
</h5>
</div>
<div class="card-body">
@if($recentLaundry && $recentLaundry->count() > 0)
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th>Nama</th>
<th>Lokasi</th>
<th class="text-end">Harga</th>
</tr>
</thead>
<tbody>
@foreach($recentLaundry as $l)
<tr>
<td>
<div class="fw-semibold">{{ $l->nama }}</div>
@if($l->layanan && $l->layanan->count() > 0)
<small class="text-muted">
{{ $l->layanan->count() }} layanan
</small>
@endif
</td>
<td>
<small class="text-muted">
<i class="bi bi-geo-alt-fill me-1"></i>{{ $l->alamat ?? '-' }}
</small>
</td>
<td class="text-end">
@if($l->layanan && $l->layanan->count() > 0)
@php
$minHarga = $l->layanan->min('harga');
@endphp
<span class="badge bg-success-subtle text-success px-3 py-2">
Rp {{ number_format($minHarga, 0, ',', '.') }}
</span>
@else
<span class="badge bg-secondary-subtle text-secondary px-3 py-2">
-
</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="text-center py-4">
<i class="bi bi-inbox text-muted" style="font-size: 3rem;"></i>
<p class="text-muted mt-2 mb-0">Belum ada data laundry</p>
</div>
@endif
<div class="mt-3">
<a href="{{ route('laundry.index') }}" class="btn btn-sm" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
<i class="bi bi-arrow-right-circle me-1"></i> Lihat Semua Data
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal Kontrakan -->
<div class="modal fade" id="kontrakanModal" tabindex="-1" aria-labelledby="kontrakanModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 1rem 1.25rem;">
<h6 class="modal-title mb-0" id="kontrakanModalLabel">
<i class="bi bi-building me-2"></i>Detail Kontrakan
</h6>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" style="padding: 1.25rem;">
<div class="row g-2 mb-3">
<div class="col-4">
<div class="card border-0 bg-light h-100">
<div class="card-body text-center p-2">
<i class="bi bi-building-fill" style="font-size: 1.5rem; color: #667eea;"></i>
<h5 class="mt-1 mb-0 fw-bold" style="color: #667eea; font-size: 1.25rem;">{{ $jumlahKontrakan }}</h5>
<p class="mb-0 text-muted" style="font-size: 0.7rem;">Total Kontrakan</p>
</div>
</div>
</div>
<div class="col-4">
<div class="card border-0 bg-light h-100">
<div class="card-body text-center p-2">
<i class="bi bi-currency-dollar" style="font-size: 1.5rem; color: #667eea;"></i>
<h6 class="mt-1 mb-0 fw-bold" style="color: #667eea; font-size: 0.85rem;">Rp {{ number_format($avgHargaKontrakan / 1000, 0) }}k</h6>
<p class="mb-0 text-muted" style="font-size: 0.7rem;">Rata-rata Harga</p>
</div>
</div>
</div>
<div class="col-4">
<div class="card border-0 bg-light h-100">
<div class="card-body text-center p-2">
<i class="bi bi-geo-alt-fill" style="font-size: 1.5rem; color: #667eea;"></i>
<h5 class="mt-1 mb-0 fw-bold" style="color: #667eea; font-size: 1.25rem;">{{ $jarakKontrakan['dekat'] + $jarakKontrakan['sedang'] + $jarakKontrakan['jauh'] }}</h5>
<p class="mb-0 text-muted" style="font-size: 0.7rem;">Lokasi Tersedia</p>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="fw-bold mb-0" style="font-size: 0.9rem;">Kontrakan Terbaru</h6>
<span class="badge" style="font-size: 0.7rem; background-color: #667eea;">{{ $recentKontrakan->count() ?? 0 }} data</span>
</div>
@if($recentKontrakan && $recentKontrakan->count() > 0)
<div class="list-group list-group-flush">
@foreach($recentKontrakan as $k)
<div class="list-group-item px-0 py-2">
<div class="d-flex w-100 justify-content-between align-items-start">
<div class="flex-grow-1 me-2">
<h6 class="mb-1 fw-semibold" style="font-size: 0.875rem;">{{ $k->nama }}</h6>
<p class="mb-0 text-muted" style="font-size: 0.75rem;">
<i class="bi bi-geo-alt me-1"></i>{{ Str::limit($k->lokasi ?? $k->alamat, 30) }}
</p>
</div>
<span class="badge" style="font-size: 0.75rem; white-space: nowrap; background-color: #667eea;">
Rp {{ number_format($k->harga / 1000, 0) }}k
</span>
</div>
</div>
@endforeach
</div>
@else
<p class="text-muted text-center py-3 mb-0" style="font-size: 0.875rem;">Belum ada data kontrakan</p>
@endif
</div>
<div class="modal-footer" style="padding: 0.75rem 1.25rem;">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Tutup</button>
<a href="{{ route('kontrakan.index') }}" class="btn btn-sm" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
<i class="bi bi-arrow-right-circle me-1"></i>Lihat Semua
</a>
</div>
</div>
</div>
</div>
<!-- Modal Laundry -->
<div class="modal fade" id="laundryModal" tabindex="-1" aria-labelledby="laundryModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; padding: 1rem 1.25rem;">
<h6 class="modal-title mb-0" id="laundryModalLabel">
<i class="bi bi-water me-2"></i>Detail Laundry
</h6>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" style="padding: 1.25rem;">
<div class="row g-2 mb-3">
<div class="col-4">
<div class="card border-0 bg-light h-100">
<div class="card-body text-center p-2">
<i class="bi bi-water" style="font-size: 1.5rem; color: #f5576c;"></i>
<h5 class="mt-1 mb-0 fw-bold" style="color: #f5576c; font-size: 1.25rem;">{{ $jumlahLaundry }}</h5>
<p class="mb-0 text-muted" style="font-size: 0.7rem;">Total Laundry</p>
</div>
</div>
</div>
<div class="col-4">
<div class="card border-0 bg-light h-100">
<div class="card-body text-center p-2">
<i class="bi bi-clock-history" style="font-size: 1.5rem; color: #f5576c;"></i>
<h5 class="mt-1 mb-0 fw-bold" style="color: #f5576c; font-size: 1.25rem;">{{ $avgKecepatan ?? 'N/A' }}</h5>
<p class="mb-0 text-muted" style="font-size: 0.7rem;">Jam Rata-rata</p>
</div>
</div>
</div>
<div class="col-4">
<div class="card border-0 bg-light h-100">
<div class="card-body text-center p-2">
<i class="bi bi-geo-alt-fill" style="font-size: 1.5rem; color: #f5576c;"></i>
<h5 class="mt-1 mb-0 fw-bold" style="color: #f5576c; font-size: 1.25rem;">{{ $jarakLaundry['dekat'] + $jarakLaundry['sedang'] + $jarakLaundry['jauh'] }}</h5>
<p class="mb-0 text-muted" style="font-size: 0.7rem;">Lokasi Tersedia</p>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="fw-bold mb-0" style="font-size: 0.9rem;">Laundry Terbaru</h6>
<span class="badge" style="background: #f5576c; font-size: 0.7rem;">{{ $recentLaundry->count() ?? 0 }} data</span>
</div>
@if($recentLaundry && $recentLaundry->count() > 0)
<div class="list-group list-group-flush">
@foreach($recentLaundry as $l)
<div class="list-group-item px-0 py-2">
<div class="d-flex w-100 justify-content-between align-items-start">
<div class="flex-grow-1 me-2">
<h6 class="mb-1 fw-semibold" style="font-size: 0.875rem;">{{ $l->nama }}</h6>
<p class="mb-0 text-muted" style="font-size: 0.75rem;">
<i class="bi bi-geo-alt me-1"></i>{{ Str::limit($l->alamat ?? '-', 25) }}
</p>
@if($l->layanan && $l->layanan->count() > 0)
<small class="text-muted" style="font-size: 0.7rem;">{{ $l->layanan->count() }} layanan</small>
@endif
</div>
@if($l->layanan && $l->layanan->count() > 0)
<span class="badge bg-success" style="font-size: 0.75rem; white-space: nowrap;">
Rp {{ number_format($l->layanan->min('harga') / 1000, 0) }}k
</span>
@endif
</div>
</div>
@endforeach
</div>
@else
<p class="text-muted text-center py-3 mb-0" style="font-size: 0.875rem;">Belum ada data laundry</p>
@endif
</div>
<div class="modal-footer" style="padding: 0.75rem 1.25rem;">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Tutup</button>
<a href="{{ route('laundry.index') }}" class="btn btn-sm" style="background: #f5576c; color: white;">
<i class="bi bi-arrow-right-circle me-1"></i>Lihat Semua
</a>
</div>
</div>
</div>
</div>
<!-- Modal Kriteria -->
<div class="modal fade" id="kriteriaModal" tabindex="-1" aria-labelledby="kriteriaModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); color: white; padding: 1rem 1.25rem;">
<h6 class="modal-title mb-0" id="kriteriaModalLabel">
<i class="bi bi-list-check me-2"></i>Detail Kriteria
</h6>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" style="padding: 1.25rem;">
<div class="row g-2 mb-3">
<div class="col-6">
<div class="card border-0 bg-light h-100">
<div class="card-body text-center p-2">
<i class="bi bi-list-check" style="font-size: 1.5rem; color: #4facfe;"></i>
<h5 class="mt-1 mb-0 fw-bold" style="color: #4facfe; font-size: 1.25rem;">{{ $jumlahKriteria }}</h5>
<p class="mb-0 text-muted" style="font-size: 0.7rem;">Total Kriteria</p>
</div>
</div>
</div>
<div class="col-6">
<div class="card border-0 bg-light h-100">
<div class="card-body text-center p-2">
<i class="bi bi-star-fill" style="font-size: 1.5rem; color: #4facfe;"></i>
<h5 class="mt-1 mb-0 fw-bold" style="color: #4facfe; font-size: 1.25rem;">SAW</h5>
<p class="mb-0 text-muted" style="font-size: 0.7rem;">Metode Perhitungan</p>
</div>
</div>
</div>
</div>
<h6 class="fw-bold mb-2" style="font-size: 0.9rem;">Informasi Kriteria</h6>
<div class="alert alert-info py-2 px-3 mb-3" style="font-size: 0.8rem;">
<i class="bi bi-info-circle me-1"></i>
Kriteria digunakan untuk penilaian dengan metode SAW (Simple Additive Weighting) untuk rekomendasi terbaik.
</div>
<div class="list-group list-group-flush">
<div class="list-group-item px-0 py-2">
<div class="d-flex w-100 justify-content-between align-items-center">
<div class="d-flex align-items-center">
<i class="bi bi-check-circle-fill me-2" style="font-size: 1rem; color: #667eea;"></i>
<div>
<h6 class="mb-0" style="font-size: 0.85rem;">Kriteria Aktif</h6>
<small class="text-muted" style="font-size: 0.7rem;">Digunakan dalam perhitungan SAW</small>
</div>
</div>
<span class="badge" style="font-size: 0.75rem; background-color: #667eea;">{{ $jumlahKriteria }}</span>
</div>
</div>
<div class="list-group-item px-0 py-2">
<div class="d-flex w-100 justify-content-between align-items-center">
<div class="d-flex align-items-center">
<i class="bi bi-calculator me-2" style="font-size: 1rem; color: #667eea;"></i>
<div>
<h6 class="mb-0" style="font-size: 0.85rem;">Bobot Total</h6>
<small class="text-muted" style="font-size: 0.7rem;">Total bobot harus 100%</small>
</div>
</div>
<span class="badge" style="font-size: 0.75rem; background-color: #667eea;">100%</span>
</div>
</div>
</div>
</div>
<div class="modal-footer" style="padding: 0.75rem 1.25rem;">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Tutup</button>
<a href="{{ route('kriteria.index') }}" class="btn btn-sm" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
<i class="bi bi-arrow-right-circle me-1"></i>Lihat Semua
</a>
</div>
</div>
</div>
</div>
<!-- Modal SAW -->
<div class="modal fade" id="sawModal" tabindex="-1" aria-labelledby="sawModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); color: white; padding: 1rem 1.25rem;">
<h6 class="modal-title mb-0" id="sawModalLabel">
<i class="bi bi-calculator me-2"></i>Sistem Rekomendasi
</h6>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" style="padding: 1.25rem;">
<div class="row g-2 mb-3">
<div class="col-6">
<div class="card border-0 bg-light h-100">
<div class="card-body text-center p-2">
<i class="bi bi-calculator" style="font-size: 1.5rem; color: #fa709a;"></i>
<h5 class="mt-1 mb-0 fw-bold" style="color: #fa709a; font-size: 1.25rem;">SAW</h5>
<p class="mb-0 text-muted" style="font-size: 0.7rem;">Metode Analisis</p>
</div>
</div>
</div>
<div class="col-6">
<div class="card border-0 bg-light h-100">
<div class="card-body text-center p-2">
<i class="bi bi-star-fill" style="font-size: 1.5rem; color: #fa709a;"></i>
<h5 class="mt-1 mb-0 fw-bold" style="color: #fa709a; font-size: 1.25rem;">{{ $jumlahKriteria }}</h5>
<p class="mb-0 text-muted" style="font-size: 0.7rem;">Kriteria Penilaian</p>
</div>
</div>
</div>
</div>
<h6 class="fw-bold mb-2" style="font-size: 0.9rem;">Tentang Sistem Analisis</h6>
<div class="alert alert-warning py-2 px-3 mb-3" style="font-size: 0.8rem;">
<i class="bi bi-lightbulb me-1"></i>
<strong>SAW</strong> adalah metode penjumlahan terbobot untuk rekomendasi kontrakan dan laundry terbaik berdasarkan kriteria.
</div>
<h6 class="fw-bold mb-2" style="font-size: 0.9rem;">Langkah-langkah Analisis</h6>
<div class="list-group list-group-flush">
<div class="list-group-item px-0 py-2">
<div class="d-flex align-items-center">
<span class="badge bg-primary rounded-circle me-2" style="width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 0.75rem;">1</span>
<div>
<h6 class="mb-0" style="font-size: 0.85rem;">Pilih Kriteria</h6>
<small class="text-muted" style="font-size: 0.7rem;">Tentukan kriteria penilaian</small>
</div>
</div>
</div>
<div class="list-group-item px-0 py-2">
<div class="d-flex align-items-center">
<span class="badge bg-primary rounded-circle me-2" style="width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 0.75rem;">2</span>
<div>
<h6 class="mb-0" style="font-size: 0.85rem;">Normalisasi Nilai</h6>
<small class="text-muted" style="font-size: 0.7rem;">Normalisasi nilai alternatif</small>
</div>
</div>
</div>
<div class="list-group-item px-0 py-2">
<div class="d-flex align-items-center">
<span class="badge bg-primary rounded-circle me-2" style="width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 0.75rem;">3</span>
<div>
<h6 class="mb-0" style="font-size: 0.85rem;">Perhitungan SAW</h6>
<small class="text-muted" style="font-size: 0.7rem;">Hitung nilai preferensi</small>
</div>
</div>
</div>
<div class="list-group-item px-0 py-2">
<div class="d-flex align-items-center">
<span class="badge bg-primary rounded-circle me-2" style="width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 0.75rem;">4</span>
<div>
<h6 class="mb-0" style="font-size: 0.85rem;">Hasil Rekomendasi</h6>
<small class="text-muted" style="font-size: 0.7rem;">Ranking rekomendasi terbaik</small>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer" style="padding: 0.75rem 1.25rem;">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Tutup</button>
<a href="{{ route('saw.index') }}" class="btn btn-sm" style="background: #fa709a; color: white;">
<i class="bi bi-arrow-right-circle me-1"></i>Mulai Analisis
</a>
</div>
</div>
</div>
</div>
<!-- Chart.js Library -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// ========== FADE-IN ANIMATION FOR STATS CARDS ==========
const statsCards = document.querySelectorAll('.stats-card');
statsCards.forEach((card, index) => {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
setTimeout(() => {
card.style.transition = 'all 0.6s ease';
card.style.opacity = '1';
card.style.transform = 'translateY(0)';
}, 100 * (index + 1));
});
// ========== COUNTER ANIMATION ==========
const counters = document.querySelectorAll('.counter');
counters.forEach(counter => {
const target = parseInt(counter.getAttribute('data-target'));
const duration = 1000;
const increment = target / (duration / 16);
let current = 0;
const updateCounter = () => {
current += increment;
if (current < target) {
counter.textContent = Math.ceil(current);
requestAnimationFrame(updateCounter);
} else {
counter.textContent = target;
}
};
updateCounter();
});
// ========== BAR CHART: PERBANDINGAN HARGA ==========
const hargaKontrakan = @json($hargaKontrakan);
const hargaLaundry = @json($hargaLaundry);
new Chart(document.getElementById('hargaChart'), {
type: 'bar',
data: {
labels: hargaKontrakan.map(k => k.nama),
datasets: [{
label: 'Kontrakan',
data: hargaKontrakan.map(k => k.harga),
backgroundColor: 'rgba(102, 126, 234, 0.8)',
borderColor: 'rgba(102, 126, 234, 1)',
borderWidth: 2
}, {
label: 'Laundry',
data: hargaLaundry.map(l => l.harga),
backgroundColor: 'rgba(240, 147, 251, 0.8)',
borderColor: 'rgba(240, 147, 251, 1)',
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
position: 'top',
},
tooltip: {
callbacks: {
label: function(context) {
return context.dataset.label + ': Rp ' + context.parsed.y.toLocaleString('id-ID');
}
}
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return 'Rp ' + (value/1000) + 'k';
}
}
}
}
}
});
// ========== DONUT CHART: DISTRIBUSI JARAK ==========
const jarakData = @json($jarakKontrakan);
const totalDekat = jarakData.dekat + @json($jarakLaundry['dekat']);
const totalSedang = jarakData.sedang + @json($jarakLaundry['sedang']);
const totalJauh = jarakData.jauh + @json($jarakLaundry['jauh']);
new Chart(document.getElementById('jarakChart'), {
type: 'doughnut',
data: {
labels: ['Dekat (≤500m)', 'Sedang (501-1000m)', 'Jauh (>1000m)'],
datasets: [{
data: [totalDekat, totalSedang, totalJauh],
backgroundColor: [
'rgba(25, 135, 84, 0.8)',
'rgba(255, 193, 7, 0.8)',
'rgba(220, 53, 69, 0.8)'
],
borderColor: [
'rgba(25, 135, 84, 1)',
'rgba(255, 193, 7, 1)',
'rgba(220, 53, 69, 1)'
],
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
// ========== LINE CHART: TREND DATA ==========
const monthlyData = @json($monthlyData);
new Chart(document.getElementById('trendChart'), {
type: 'line',
data: {
labels: monthlyData.map(m => m.month),
datasets: [{
label: 'Kontrakan',
data: monthlyData.map(m => m.kontrakan),
borderColor: 'rgba(102, 126, 234, 1)',
backgroundColor: 'rgba(102, 126, 234, 0.1)',
tension: 0.4,
fill: true
}, {
label: 'Laundry',
data: monthlyData.map(m => m.laundry),
borderColor: 'rgba(240, 147, 251, 1)',
backgroundColor: 'rgba(240, 147, 251, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
position: 'top'
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
});
</script>
<style>
.stats-card {
transition: all 0.3s ease;
position: relative;
z-index: 1;
}
.stats-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.15) !important;
}
.stats-card .card-body {
position: relative;
z-index: 2;
}
.stats-card .btn {
pointer-events: auto;
cursor: pointer;
position: relative;
z-index: 3;
text-decoration: none;
}
.stats-card .btn:hover {
background-color: #f8f9fa !important;
transform: scale(1.02);
}
.table > :not(caption) > * > * {
padding: 1rem 0.75rem;
}
.badge {
font-weight: 500;
font-size: 0.85rem;
}
.bg-primary-subtle {
background-color: rgba(13, 110, 253, 0.1) !important;
}
.bg-success-subtle {
background-color: rgba(25, 135, 84, 0.1) !important;
}
.bg-secondary-subtle {
background-color: rgba(108, 117, 125, 0.1) !important;
}
canvas {
max-height: 400px;
}
/* Perbaikan untuk developer tools inspect */
.stats-card,
.stats-card * {
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
}
</style>
@endsection