1622 lines
74 KiB
PHP
1622 lines
74 KiB
PHP
@extends('layouts.admin')
|
|
|
|
@section('title', 'Dashboard Admin')
|
|
|
|
@section('content')
|
|
<!-- Load Skeleton Loader Script -->
|
|
@include('components.skeleton-script')
|
|
|
|
@php
|
|
$isSuperAdmin = Auth::user()->role === 'super_admin';
|
|
@endphp
|
|
|
|
<div class="container-fluid px-2 px-md-4 dashboard-shell">
|
|
<style>
|
|
.dashboard-shell {
|
|
background: radial-gradient(1200px 400px at 10% -10%, rgba(102, 126, 234, 0.12), transparent 60%),
|
|
radial-gradient(1000px 360px at 90% -15%, rgba(118, 75, 162, 0.12), transparent 60%);
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
.header-meta {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.75rem;
|
|
margin-top: 1.25rem;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.meta-chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.5rem 0.85rem;
|
|
border-radius: 999px;
|
|
background: rgba(255, 255, 255, 0.18);
|
|
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
font-size: 0.9rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.2px;
|
|
}
|
|
|
|
.header-actions {
|
|
margin-top: 1.25rem;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.75rem;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.header-actions.profile-actions {
|
|
margin-top: 0.75rem;
|
|
}
|
|
|
|
.action-chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.65rem 1rem;
|
|
border-radius: 12px;
|
|
border: 1px solid rgba(255, 255, 255, 0.35);
|
|
background: rgba(255, 255, 255, 0.12);
|
|
color: white;
|
|
text-decoration: none;
|
|
font-weight: 600;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.action-chip:hover {
|
|
transform: translateY(-2px);
|
|
background: rgba(255, 255, 255, 0.22);
|
|
color: white;
|
|
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.stats-card {
|
|
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
border: none !important;
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
position: relative;
|
|
}
|
|
|
|
.glass-panel {
|
|
background: rgba(255, 255, 255, 0.7);
|
|
border: 1px solid rgba(148, 163, 184, 0.25);
|
|
backdrop-filter: blur(12px);
|
|
border-radius: 18px;
|
|
box-shadow: 0 16px 36px rgba(15, 23, 42, 0.08);
|
|
}
|
|
|
|
.mini-metric {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.75rem;
|
|
padding: 1rem 1.25rem;
|
|
border-radius: 14px;
|
|
background: linear-gradient(135deg, rgba(102, 126, 234, 0.12), rgba(118, 75, 162, 0.08));
|
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
|
}
|
|
|
|
.mini-metric span {
|
|
color: #64748b;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.mini-metric strong {
|
|
font-size: 1.35rem;
|
|
color: #4338ca;
|
|
}
|
|
|
|
|
|
.metric-card {
|
|
border-radius: 16px;
|
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
|
background: rgba(255, 255, 255, 0.85);
|
|
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.08);
|
|
}
|
|
|
|
.metric-card h6 {
|
|
font-weight: 700;
|
|
color: #4f46e5;
|
|
}
|
|
|
|
.metric-grid {
|
|
display: grid;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.metric-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0.75rem 1rem;
|
|
border-radius: 12px;
|
|
background: rgba(248, 250, 252, 0.95);
|
|
border: 1px solid rgba(226, 232, 240, 0.8);
|
|
}
|
|
|
|
.metric-row span {
|
|
color: #64748b;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.metric-row strong {
|
|
color: #1f2937;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.progress-rail {
|
|
height: 10px;
|
|
border-radius: 999px;
|
|
background: rgba(226, 232, 240, 0.9);
|
|
overflow: hidden;
|
|
margin-top: 0.4rem;
|
|
}
|
|
|
|
.progress-fill {
|
|
height: 100%;
|
|
border-radius: 999px;
|
|
}
|
|
|
|
.rank-list {
|
|
display: grid;
|
|
gap: 0.6rem;
|
|
}
|
|
|
|
.rank-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0.7rem 0.9rem;
|
|
border-radius: 12px;
|
|
background: rgba(248, 250, 252, 0.95);
|
|
border: 1px solid rgba(226, 232, 240, 0.8);
|
|
}
|
|
|
|
.rank-item small {
|
|
color: #64748b;
|
|
display: block;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
.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);
|
|
}
|
|
|
|
</style>
|
|
|
|
<!-- Header Section -->
|
|
<div class="header-dashboard">
|
|
<h2 class="mb-2">Dashboard Admin</h2>
|
|
<p class="mb-0">Selamat datang, <strong>{{ Auth::user()->name }}!</strong> Kelola data kontrakan dan booking dengan lebih mudah.</p>
|
|
<div class="header-meta">
|
|
<div class="meta-chip"><i class="bi bi-calendar-event"></i>{{ now()->format('d M Y') }}</div>
|
|
<div class="meta-chip"><i class="bi bi-building"></i>{{ $jumlahKontrakan }} kontrakan</div>
|
|
@if($isSuperAdmin)
|
|
<div class="meta-chip"><i class="bi bi-droplet"></i>{{ $jumlahLaundry }} laundry</div>
|
|
@endif
|
|
</div>
|
|
<div class="header-actions">
|
|
<a href="{{ route('kontrakan.index') }}" class="action-chip"><i class="bi bi-building"></i>Kontrakan</a>
|
|
<a href="{{ route('admin.bookings.index') }}" class="action-chip"><i class="bi bi-calendar-check"></i>Booking</a>
|
|
@if($isSuperAdmin)
|
|
<a href="{{ route('laundry.index') }}" class="action-chip"><i class="bi bi-water"></i>Laundry</a>
|
|
<a href="{{ route('kriteria.index') }}" class="action-chip"><i class="bi bi-list-check"></i>Kriteria</a>
|
|
<a href="{{ route('saw.index') }}" class="action-chip"><i class="bi bi-calculator"></i>Analisis</a>
|
|
@endif
|
|
</div>
|
|
<div class="header-actions profile-actions">
|
|
<a href="{{ route('admin.profile') }}" class="action-chip"><i class="bi bi-person-circle"></i>Profil</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="row g-3 g-md-4 mb-4 mb-md-5" id="statsContainer">
|
|
<!-- Card Kontrakan - Purple Theme -->
|
|
<div class="{{ $isSuperAdmin ? 'col-6 col-lg-3' : '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>
|
|
|
|
@if(!$isSuperAdmin)
|
|
<!-- Card Booking - Blue Theme -->
|
|
<div class="col-6 col-lg-3">
|
|
<div class="card stats-card h-100" style="background: linear-gradient(135deg, #38bdf8 0%, #2563eb 100%); border: none !important; box-shadow: 0 8px 25px rgba(37, 99, 235, 0.25); 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-calendar-check me-1"></i>Total Booking</p>
|
|
<h2 class="mb-2 mb-md-3 fw-bold display-6">{{ $totalBookings }}</h2>
|
|
<p class="mb-0 opacity-85 small d-none d-md-block">
|
|
<i class="bi bi-clock me-1"></i>Data booking terbaru
|
|
</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-calendar2-week" style="font-size: 1.5rem;"></i>
|
|
</div>
|
|
</div>
|
|
<a href="{{ route('admin.bookings.index') }}" class="btn btn-light btn-sm w-100 fw-semibold d-none d-md-block" style="position: relative; z-index: 3; color: #1d4ed8;">
|
|
<i class="bi bi-arrow-right me-2"></i>Kelola Booking
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@endif
|
|
|
|
<!-- Card Booking Menunggu -->
|
|
<div class="col-6 col-lg-3">
|
|
<div class="card stats-card h-100" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); border: none !important; box-shadow: 0 8px 25px rgba(245, 158, 11, 0.25); 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-hourglass-split me-1"></i>Booking Menunggu</p>
|
|
<h2 class="mb-2 mb-md-3 fw-bold display-6">{{ $bookingMenunggu ?? 0 }}</h2>
|
|
<p class="mb-0 opacity-85 small d-none d-md-block">Perlu dikonfirmasi</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-clock-history" style="font-size: 1.5rem;"></i>
|
|
</div>
|
|
</div>
|
|
<a href="{{ route('admin.bookings.index') }}" class="btn btn-light btn-sm w-100 fw-semibold d-none d-md-block" style="color: #b45309;">
|
|
<i class="bi bi-arrow-right me-2"></i>Cek Booking
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Card Booking Selesai -->
|
|
<div class="col-6 col-lg-3">
|
|
<div class="card stats-card h-100" style="background: linear-gradient(135deg, #22c55e 0%, #15803d 100%); border: none !important; box-shadow: 0 8px 25px rgba(34, 197, 94, 0.25); 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-check-circle me-1"></i>Booking Selesai</p>
|
|
<h2 class="mb-2 mb-md-3 fw-bold display-6">{{ $bookingSelesai ?? 0 }}</h2>
|
|
<p class="mb-0 opacity-85 small d-none d-md-block">Transaksi selesai</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-check2-circle" style="font-size: 1.5rem;"></i>
|
|
</div>
|
|
</div>
|
|
<a href="{{ route('admin.bookings.index') }}" class="btn btn-light btn-sm w-100 fw-semibold d-none d-md-block" style="color: #15803d;">
|
|
<i class="bi bi-arrow-right me-2"></i>Lihat Riwayat
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if($isSuperAdmin)
|
|
<!-- 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>
|
|
|
|
@endif
|
|
|
|
@if($isSuperAdmin)
|
|
<!-- 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>
|
|
@endif
|
|
|
|
@if($isSuperAdmin)
|
|
<!-- 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>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Snapshot Section -->
|
|
<div class="row g-3 g-md-4 mb-4 mb-md-5">
|
|
<div class="col-12">
|
|
<div class="glass-panel p-4 h-100">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h5 class="fw-bold mb-0" style="color: #4f46e5;">Ringkasan Aktivitas</h5>
|
|
<span class="badge" style="background: rgba(102, 126, 234, 0.12); color: #4f46e5;">Update Hari Ini</span>
|
|
</div>
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<div class="mini-metric">
|
|
<span>Kontrakan Aktif</span>
|
|
<strong>{{ $jumlahKontrakan }}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mini-metric">
|
|
<span>Booking Masuk</span>
|
|
<strong>{{ $totalBookings ?? 0 }}</strong>
|
|
</div>
|
|
</div>
|
|
@if($isSuperAdmin)
|
|
<div class="col-md-6">
|
|
<div class="mini-metric">
|
|
<span>Laundry Terdaftar</span>
|
|
<strong>{{ $jumlahLaundry }}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mini-metric">
|
|
<span>Kriteria Aktif</span>
|
|
<strong>{{ $jumlahKriteria }}</strong>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Compact Visual Widgets -->
|
|
<div class="row g-3 g-md-4 mb-4 mb-md-5">
|
|
<div class="col-12 col-lg-4">
|
|
<div class="metric-card p-4 h-100">
|
|
<h6 class="mb-3">Ringkasan Harga Kontrakan</h6>
|
|
<div class="metric-grid">
|
|
<div class="metric-row">
|
|
<span>Harga Terendah</span>
|
|
<strong>Rp {{ number_format($minHargaKontrakan, 0, ',', '.') }}</strong>
|
|
</div>
|
|
<div class="metric-row">
|
|
<span>Harga Rata-rata</span>
|
|
<strong>Rp {{ number_format($avgHargaKontrakan, 0, ',', '.') }}</strong>
|
|
</div>
|
|
<div class="metric-row">
|
|
<span>Harga Tertinggi</span>
|
|
<strong>Rp {{ number_format($maxHargaKontrakan, 0, ',', '.') }}</strong>
|
|
</div>
|
|
<div class="metric-row">
|
|
<span>Rata-rata Jarak</span>
|
|
<strong>{{ number_format($avgJarakKontrakan, 0, ',', '.') }} m</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-4">
|
|
<div class="metric-card p-4 h-100">
|
|
<h6 class="mb-3">Distribusi Jarak Kontrakan</h6>
|
|
@php
|
|
$totalJarakKontrakan = max(1, $jarakKontrakan['dekat'] + $jarakKontrakan['sedang'] + $jarakKontrakan['jauh']);
|
|
@endphp
|
|
<div class="metric-grid">
|
|
<div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span class="fw-semibold">Dekat (≤500m)</span>
|
|
<strong>{{ $jarakKontrakan['dekat'] }}</strong>
|
|
</div>
|
|
<div class="progress-rail">
|
|
<div class="progress-fill" style="width: {{ ($jarakKontrakan['dekat'] / $totalJarakKontrakan) * 100 }}%; background: #22c55e;"></div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span class="fw-semibold">Sedang (501-1000m)</span>
|
|
<strong>{{ $jarakKontrakan['sedang'] }}</strong>
|
|
</div>
|
|
<div class="progress-rail">
|
|
<div class="progress-fill" style="width: {{ ($jarakKontrakan['sedang'] / $totalJarakKontrakan) * 100 }}%; background: #f59e0b;"></div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span class="fw-semibold">Jauh (>1000m)</span>
|
|
<strong>{{ $jarakKontrakan['jauh'] }}</strong>
|
|
</div>
|
|
<div class="progress-rail">
|
|
<div class="progress-fill" style="width: {{ ($jarakKontrakan['jauh'] / $totalJarakKontrakan) * 100 }}%; background: #ef4444;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-4">
|
|
<div class="metric-card p-4 h-100">
|
|
<h6 class="mb-3">Kontrakan Harga Tertinggi</h6>
|
|
<div class="rank-list">
|
|
@foreach($topKontrakan as $kontrakan)
|
|
<div class="rank-item">
|
|
<div>
|
|
<div class="fw-semibold">{{ $kontrakan->nama }}</div>
|
|
<small>{{ $kontrakan->jumlah_kamar ?? 0 }} kamar • {{ $kontrakan->jarak ?? 0 }} m</small>
|
|
</div>
|
|
<strong>Rp {{ number_format($kontrakan->harga, 0, ',', '.') }}</strong>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if($isSuperAdmin)
|
|
<div class="row g-3 g-md-4 mb-4 mb-md-5">
|
|
<div class="col-12 col-lg-6">
|
|
<div class="metric-card p-4 h-100">
|
|
<h6 class="mb-3">Laundry Harga Terendah</h6>
|
|
<div class="rank-list">
|
|
@foreach($hargaLaundry as $laundry)
|
|
<div class="rank-item">
|
|
<div>
|
|
<div class="fw-semibold">{{ $laundry->nama }}</div>
|
|
<small>Harga mulai</small>
|
|
</div>
|
|
<strong>Rp {{ number_format($laundry->harga, 0, ',', '.') }}</strong>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-6">
|
|
<div class="metric-card p-4 h-100">
|
|
<h6 class="mb-3">Distribusi Jarak Laundry</h6>
|
|
@php
|
|
$totalJarakLaundry = max(1, $jarakLaundry['dekat'] + $jarakLaundry['sedang'] + $jarakLaundry['jauh']);
|
|
@endphp
|
|
<div class="metric-grid">
|
|
<div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span class="fw-semibold">Dekat (≤500m)</span>
|
|
<strong>{{ $jarakLaundry['dekat'] }}</strong>
|
|
</div>
|
|
<div class="progress-rail">
|
|
<div class="progress-fill" style="width: {{ ($jarakLaundry['dekat'] / $totalJarakLaundry) * 100 }}%; background: #22c55e;"></div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span class="fw-semibold">Sedang (501-1000m)</span>
|
|
<strong>{{ $jarakLaundry['sedang'] }}</strong>
|
|
</div>
|
|
<div class="progress-rail">
|
|
<div class="progress-fill" style="width: {{ ($jarakLaundry['sedang'] / $totalJarakLaundry) * 100 }}%; background: #f59e0b;"></div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span class="fw-semibold">Jauh (>1000m)</span>
|
|
<strong>{{ $jarakLaundry['jauh'] }}</strong>
|
|
</div>
|
|
<div class="progress-rail">
|
|
<div class="progress-fill" style="width: {{ ($jarakLaundry['jauh'] / $totalJarakLaundry) * 100 }}%; background: #ef4444;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
|
|
|
|
<!-- 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">
|
|
@if($isSuperAdmin)
|
|
<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>
|
|
@else
|
|
<div class="col-12 col-md-4">
|
|
<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-12 col-md-4">
|
|
<a href="{{ route('admin.bookings.index') }}" class="btn btn-admin-outline w-100 py-3">
|
|
<i class="bi bi-calendar-check me-2"></i> Kelola Booking
|
|
</a>
|
|
</div>
|
|
<div class="col-12 col-md-4">
|
|
<a href="{{ route('admin.profile') }}" class="btn btn-admin-solid w-100 py-3">
|
|
<i class="bi bi-person-circle me-2"></i> Profil Admin
|
|
</a>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Additional Stats Section -->
|
|
@if($isSuperAdmin)
|
|
<div class="row g-4 mb-4">
|
|
|
|
<!-- Booking Terbaru -->
|
|
<div class="col-12 mb-4">
|
|
<div class="card chart-card border-0 shadow-sm">
|
|
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h5 class="mb-1 fw-bold text-admin-primary">
|
|
<i class="bi bi-calendar-check me-2"></i>Booking Terbaru
|
|
</h5>
|
|
<small class="text-muted">Menampilkan booking terbaru dari kontrakan milik admin yang login.</small>
|
|
</div>
|
|
<a href="{{ route('admin.bookings.index') }}" class="btn btn-admin-outline btn-sm">
|
|
Lihat Semua
|
|
</a>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
@if(isset($recentBookings) && $recentBookings->count() > 0)
|
|
<div class="table-responsive">
|
|
<table class="table align-middle mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Nama Penyewa</th>
|
|
<th>Kontrakan</th>
|
|
<th>Status</th>
|
|
<th>Tanggal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($recentBookings as $booking)
|
|
<tr>
|
|
<td class="fw-semibold">
|
|
{{ $booking->user->name ?? $booking->nama_penyewa ?? 'Mahasiswa' }}
|
|
</td>
|
|
<td>
|
|
{{ $booking->kontrakan->nama ?? '-' }}
|
|
</td>
|
|
<td>
|
|
@php
|
|
$status = strtolower($booking->status ?? 'menunggu');
|
|
$badgeClass = match($status) {
|
|
'selesai' => 'success',
|
|
'dikonfirmasi' => 'primary',
|
|
'ditempati' => 'info',
|
|
'ditolak' => 'danger',
|
|
default => 'warning'
|
|
};
|
|
@endphp
|
|
<span class="badge bg-{{ $badgeClass }}">
|
|
{{ ucfirst($booking->status ?? 'Menunggu') }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{{ $booking->created_at ? $booking->created_at->format('d M Y') : '-' }}
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@else
|
|
<div class="text-center py-5">
|
|
<i class="bi bi-calendar-x text-muted" style="font-size: 3rem;"></i>
|
|
<h6 class="mt-3 mb-1 fw-bold">Belum ada booking masuk</h6>
|
|
<p class="text-muted mb-0">Booking dari mahasiswa akan tampil di bagian ini.</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12">
|
|
<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>
|
|
@endif
|
|
|
|
<!-- Recent Data Tables -->
|
|
<div class="row g-4">
|
|
<!-- Recent Kontrakan -->
|
|
<div class="{{ $isSuperAdmin ? 'col-lg-6' : '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-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>
|
|
|
|
@if($isSuperAdmin)
|
|
<!-- 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>
|
|
@endif
|
|
</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>
|
|
|
|
@if($isSuperAdmin)
|
|
<!-- Modal Laundry -->
|
|
@if($isSuperAdmin)
|
|
<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 -->
|
|
@endif
|
|
|
|
<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>
|
|
@endif
|
|
|
|
<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();
|
|
});
|
|
|
|
});
|
|
</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
|