SuperAdmin Grafik

This commit is contained in:
Stephen Gesityan 2025-05-19 18:23:14 +07:00
parent ac9d47071e
commit 50accc8a00
3 changed files with 504 additions and 156 deletions

View File

@ -5,8 +5,11 @@
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\User; use App\Models\User;
use App\Models\Booking;
use App\Models\Venue; use App\Models\Venue;
use App\Models\Table; use App\Models\Table;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
class SuperAdminController extends Controller class SuperAdminController extends Controller
{ {
@ -17,19 +20,63 @@ class SuperAdminController extends Controller
*/ */
public function index() public function index()
{ {
// Menghitung jumlah admin, venue, user, venue aktif, dan meja // Basic counts
$adminCount = User::where('role', 'admin')->count(); $adminCount = User::where('role', 'admin')->count();
$venueCount = Venue::count(); $venueCount = Venue::count();
$userCount = User::where('role', 'user')->count(); $userCount = User::where('role', 'user')->count();
// $activeVenueCount = Venue::where('status', 'active')->count();
$tableCount = Table::count(); $tableCount = Table::count();
// Revenue comparison for current month
$currentMonth = Carbon::now()->startOfMonth();
$revenueData = $this->getMonthlyRevenueByVenue($currentMonth);
// Popular venues ranking data
$popularVenuesData = $this->getPopularVenuesData($currentMonth);
return view('superadmin.dashboard', compact( return view('superadmin.dashboard', compact(
'adminCount', 'adminCount',
'venueCount', 'venueCount',
'userCount', 'userCount',
// 'activeVenueCount', 'tableCount',
'tableCount' 'revenueData',
'popularVenuesData'
)); ));
} }
/**
* Get monthly revenue by venue for chart
*/
private function getMonthlyRevenueByVenue($startDate)
{
return Venue::leftJoin('tables', 'venues.id', '=', 'tables.venue_id')
->leftJoin('bookings', function($join) use ($startDate) {
$join->on('tables.id', '=', 'bookings.table_id')
->where('bookings.status', 'paid')
->where('bookings.created_at', '>=', $startDate)
->where('bookings.created_at', '<', $startDate->copy()->endOfMonth());
})
->select('venues.name as venue_name',
DB::raw('COALESCE(SUM(bookings.total_amount), 0) as total_revenue'))
->groupBy('venues.id', 'venues.name')
->get();
}
/**
* Get popular venues data for ranking
*/
private function getPopularVenuesData($startDate)
{
return Venue::leftJoin('tables', 'venues.id', '=', 'tables.venue_id')
->leftJoin('bookings', function($join) use ($startDate) {
$join->on('tables.id', '=', 'bookings.table_id')
->where('bookings.status', 'paid')
->where('bookings.created_at', '>=', $startDate)
->where('bookings.created_at', '<', $startDate->copy()->endOfMonth());
})
->select('venues.name as venue_name',
DB::raw('COALESCE(COUNT(bookings.id), 0) as total_bookings'),
DB::raw('COALESCE(SUM(bookings.total_amount), 0) as total_revenue'))
->groupBy('venues.id', 'venues.name')
->get();
}
} }

View File

@ -12,91 +12,230 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Alpine.js --> <!-- Alpine.js -->
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.12.0/cdn.min.js"></script> <script defer src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.12.0/cdn.min.js"></script>
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'Inter', sans-serif; }
/* Custom scrollbar for sidebar */
.sidebar-scroll::-webkit-scrollbar {
width: 4px;
}
.sidebar-scroll::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
}
.sidebar-scroll::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 2px;
}
/* Hover effects */
.nav-item {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.nav-item::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transition: left 0.5s;
}
.nav-item:hover::before {
left: 100%;
}
/* Active nav indicator */
.nav-active {
background: rgba(255, 255, 255, 0.15);
border-right: 3px solid white;
}
/* Profile section gradient */
.profile-section {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
</style>
</head> </head>
<body class="bg-gray-100"> <body class="bg-gray-50">
<div x-data="{ sidebarOpen: true }"> <div x-data="{ sidebarOpen: true }">
<!-- Sidebar --> <!-- Sidebar -->
<aside :class="sidebarOpen ? 'translate-x-0' : '-translate-x-full'" <aside :class="sidebarOpen ? 'translate-x-0' : '-translate-x-full'"
class="fixed top-0 left-0 z-40 w-64 h-screen transition-transform bg-blue-800 text-white"> class="fixed top-0 left-0 z-40 w-72 h-screen transition-transform duration-300 ease-in-out bg-gradient-to-br from-slate-800 via-slate-700 to-slate-900 text-white shadow-2xl">
<div class="flex items-center justify-between p-4 border-b border-blue-700">
<h2 class="text-2xl font-bold">Venue System</h2> <!-- Header -->
<button @click="sidebarOpen = !sidebarOpen" class="lg:hidden"> <div class="flex items-center justify-between p-6 border-b border-slate-600/30">
<i class="fas fa-times"></i> <div class="flex items-center space-x-3">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center shadow-lg">
<i class="fas fa-cubes text-white text-lg"></i>
</div>
<div>
<h2 class="text-xl font-bold bg-gradient-to-r from-white to-slate-300 bg-clip-text text-transparent">
VenueSystem
</h2>
<p class="text-xs text-slate-400 font-medium">Super Admin Panel</p>
</div>
</div>
<button @click="sidebarOpen = !sidebarOpen"
class="lg:hidden w-8 h-8 rounded-lg bg-slate-600/50 hover:bg-slate-600 transition-colors flex items-center justify-center">
<i class="fas fa-times text-sm"></i>
</button> </button>
</div> </div>
<div class="p-4">
<div class="mb-8"> <!-- Profile Section -->
<div class="flex items-center mb-4"> <div class="p-6">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center"> <div class="profile-section rounded-xl p-4 mb-6">
<i class="fas fa-user-shield"></i> <div class="flex items-center space-x-4">
<div class="relative">
<div class="w-12 h-12 rounded-full bg-gradient-to-br from-emerald-400 to-cyan-500 flex items-center justify-center shadow-lg">
<i class="fas fa-user-shield text-white text-lg"></i>
</div>
<div class="absolute -bottom-1 -right-1 w-4 h-4 bg-green-400 rounded-full border-2 border-slate-800"></div>
</div> </div>
<div class="ml-3"> <div class="flex-1">
<p class="text-sm font-medium">Super Admin</p> <p class="font-semibold text-white">{{ auth()->user()->name ?? 'Admin' }}</p>
<p class="text-xs opacity-75">{{ auth()->user()->name ?? 'Admin' }}</p> <p class="text-sm text-slate-300">Super Administrator</p>
<div class="mt-1">
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs bg-emerald-500/20 text-emerald-300 border border-emerald-500/30">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-400 mr-1"></span>
Online
</span>
</div>
</div> </div>
</div> </div>
</div> </div>
<nav>
<ul> <!-- Navigation -->
<li class="mb-2"> <nav class="space-y-2">
<a href="{{ route('superadmin.dashboard') }}" <div class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-4 px-3">
class="flex items-center p-3 rounded-lg hover:bg-blue-700 {{ request()->routeIs('superadmin.dashboard') ? 'bg-blue-700' : '' }}"> Main Navigation
<i class="fas fa-tachometer-alt w-5"></i> </div>
<span class="ml-3">Dashboard</span>
</a> <a href="{{ route('superadmin.dashboard') }}"
</li> class="nav-item flex items-center p-3 rounded-lg hover:bg-slate-600/30 {{ request()->routeIs('superadmin.dashboard') ? 'nav-active' : '' }} group">
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-blue-500/20 to-blue-600/20 flex items-center justify-center group-hover:from-blue-500/30 group-hover:to-blue-600/30 transition-all">
<i class="fas fa-chart-line text-blue-400 group-hover:text-blue-300"></i>
</div>
<div class="ml-4">
<span class="font-medium text-slate-200 group-hover:text-white transition-colors">Dashboard</span>
<p class="text-xs text-slate-400 group-hover:text-slate-300">Analytics & Overview</p>
</div>
</a>
<li class="mb-2"> <a href="{{ route('superadmin.venue.index') }}"
<a href="{{ route('superadmin.venue.index') }}" class="nav-item flex items-center p-3 rounded-lg hover:bg-slate-600/30 {{ request()->routeIs('superadmin.venue.*') ? 'nav-active' : '' }} group">
class="flex items-center p-3 rounded-lg hover:bg-blue-700 {{ request()->routeIs('superadmin.venue.*') ? 'bg-blue-700' : '' }}"> <div class="w-10 h-10 rounded-lg bg-gradient-to-br from-emerald-500/20 to-emerald-600/20 flex items-center justify-center group-hover:from-emerald-500/30 group-hover:to-emerald-600/30 transition-all">
<i class="fas fa-building w-5"></i> <i class="fas fa-building text-emerald-400 group-hover:text-emerald-300"></i>
<span class="ml-3">Manajemen Venue</span> </div>
</a> <div class="ml-4">
</li> <span class="font-medium text-slate-200 group-hover:text-white transition-colors">Manajemen Venue</span>
<li class="mb-2"> <p class="text-xs text-slate-400 group-hover:text-slate-300">Kelola semua venue</p>
<a href="{{ route('superadmin.admin.index') }}" </div>
class="flex items-center p-3 rounded-lg hover:bg-blue-700 {{ request()->routeIs('superadmin.admin.*') ? 'bg-blue-700' : '' }}"> </a>
<i class="fas fa-users-cog w-5"></i>
<span class="ml-3">Manajemen Admin</span> <a href="{{ route('superadmin.admin.index') }}"
</a> class="nav-item flex items-center p-3 rounded-lg hover:bg-slate-600/30 {{ request()->routeIs('superadmin.admin.*') ? 'nav-active' : '' }} group">
</li> <div class="w-10 h-10 rounded-lg bg-gradient-to-br from-purple-500/20 to-purple-600/20 flex items-center justify-center group-hover:from-purple-500/30 group-hover:to-purple-600/30 transition-all">
<li class="mb-2"> <i class="fas fa-users-cog text-purple-400 group-hover:text-purple-300"></i>
<a href="{{ route('logout') }}" </div>
<div class="ml-4">
<span class="font-medium text-slate-200 group-hover:text-white transition-colors">Manajemen Admin</span>
<p class="text-xs text-slate-400 group-hover:text-slate-300">Kelola admin venue</p>
</div>
</a>
<a href="{{ route('logout') }}"
onclick="event.preventDefault(); document.getElementById('logout-form').submit();" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"
class="flex items-center p-3 rounded-lg hover:bg-blue-700"> class="flex items-center p-3 rounded-lg hover:bg-red-700">
<i class="fas fa-sign-out-alt w-5"></i> <i class="fas fa-sign-out-alt w-5"></i>
<span class="ml-3">Logout</span> <span class="ml-3">Logout</span>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="hidden">
@csrf
</form>
</a> </a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="hidden">
@csrf
</form>
</li>
</ul>
</nav> </nav>
</div> </div>
</aside> </aside>
<!-- Content --> <!-- Content -->
<div :class="sidebarOpen ? 'lg:ml-64' : ''" class="transition-all duration-300"> <div :class="sidebarOpen ? 'lg:ml-72' : ''" class="transition-all duration-300">
<!-- Top bar --> <!-- Top bar -->
<header class="bg-white border-b border-gray-200 sticky top-0 z-30"> <header class="bg-white/80 backdrop-blur-md border-b border-gray-200/50 sticky top-0 z-30 shadow-sm">
<div class="px-4 py-3 flex items-center justify-between"> <div class="px-6 py-4 flex items-center justify-between">
<button @click="sidebarOpen = !sidebarOpen" class="text-gray-600 focus:outline-none">
<i class="fas fa-bars"></i>
</button>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<button @click="sidebarOpen = !sidebarOpen"
class="w-10 h-10 rounded-lg bg-gray-100 hover:bg-gray-200 transition-colors flex items-center justify-center text-gray-600 hover:text-gray-800">
<i class="fas fa-bars text-sm"></i>
</button>
<!-- Breadcrumb -->
<nav class="flex items-center space-x-2 text-sm text-gray-500">
<span class="font-medium text-gray-900">Super Admin</span>
<i class="fas fa-chevron-right text-xs"></i>
<span>Dashboard</span>
</nav>
</div>
<div class="flex items-center space-x-4">
<!-- Notifications -->
<button class="relative w-10 h-10 rounded-lg bg-gray-100 hover:bg-gray-200 transition-colors flex items-center justify-center text-gray-600 hover:text-gray-800">
<i class="fas fa-bell text-sm"></i>
<span class="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full"></span>
</button>
<!-- Profile Dropdown -->
<div class="relative" x-data="{ open: false }"> <div class="relative" x-data="{ open: false }">
<button @click="open = !open" class="flex items-center text-gray-600 focus:outline-none"> <button @click="open = !open"
<span class="mr-2">{{ auth()->user()->name ?? 'Admin' }}</span> class="flex items-center space-x-3 p-2 rounded-lg hover:bg-gray-100 transition-colors">
<i class="fas fa-chevron-down text-xs"></i> <div class="w-8 h-8 rounded-full bg-gradient-to-br from-emerald-400 to-cyan-500 flex items-center justify-center">
<i class="fas fa-user text-white text-sm"></i>
</div>
<div class="text-left hidden md:block">
<p class="text-sm font-medium text-gray-900">{{ auth()->user()->name ?? 'Admin' }}</p>
<p class="text-xs text-gray-500">Super Admin</p>
</div>
<i class="fas fa-chevron-down text-xs text-gray-400"
:class="{ 'rotate-180': open }"
style="transition: transform 0.2s"></i>
</button> </button>
<div x-show="open" @click.away="open = false"
class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50"> <div x-show="open"
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a> @click.away="open = false"
<a href="{{ route('logout') }}" x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="absolute right-0 mt-2 w-56 bg-white rounded-xl shadow-lg border border-gray-200 py-2 z-50">
<div class="px-4 py-3 border-b border-gray-100">
<p class="text-sm font-medium text-gray-900">{{ auth()->user()->name ?? 'Admin' }}</p>
<p class="text-xs text-gray-500">{{ auth()->user()->email ?? 'admin@example.com' }}</p>
</div>
<a href="#" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-50">
<i class="fas fa-user mr-3 text-gray-400"></i>
Profile Settings
</a>
<a href="#" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-50">
<i class="fas fa-cog mr-3 text-gray-400"></i>
Account Settings
</a>
<div class="border-t border-gray-100 mt-2 pt-2">
<a href="{{ route('logout') }}"
onclick="event.preventDefault(); document.getElementById('logout-form').submit();" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Logout</a> class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Logout</a>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,120 +1,282 @@
@extends('layouts.super-admin') @extends('layouts.super-admin')
@section('content') @section('content')
<div class="mb-6"> <div class="mb-8">
<h1 class="text-3xl font-bold text-gray-800">Dashboard Super Admin</h1> <h1 class="text-4xl font-bold bg-gradient-to-r from-purple-600 to-pink-600 bg-clip-text text-transparent">
<p class="text-gray-600">Selamat datang di panel kontrol Super Admin</p> Dashboard Super Admin
</h1>
<p class="text-gray-600 text-lg">Analytics & Overview dari seluruh venue</p>
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> <!-- Main Stats Cards -->
<div class="bg-white rounded-lg shadow p-6"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div class="flex items-center justify-between mb-4"> <div class="bg-gradient-to-br from-blue-500 to-blue-600 rounded-xl shadow-lg p-6 text-white">
<h2 class="text-xl font-semibold text-gray-800"> <div class="flex items-center justify-between">
<i class="fas fa-users-cog mr-2 text-blue-600"></i>Admin <div>
</h2> <p class="text-blue-100 text-sm font-medium">Total Admin</p>
<span class="bg-blue-100 text-blue-800 text-xs font-medium px-3 py-1 rounded-full"> <p class="text-3xl font-bold">{{ $adminCount ?? 0 }}</p>
Total: {{ $adminCount ?? 0 }} </div>
</span> <div class="bg-blue-400 bg-opacity-30 rounded-full p-3">
<i class="fas fa-users-cog text-2xl"></i>
</div>
</div> </div>
<p class="text-gray-600 mb-4">Kelola semua admin venue dalam sistem</p>
<a href="{{ route('superadmin.admin.index') }}"
class="inline-flex items-center text-blue-600 hover:text-blue-800">
Lihat Detail
<i class="fas fa-arrow-right ml-2"></i>
</a>
</div> </div>
<div class="bg-white rounded-lg shadow p-6"> <div class="bg-gradient-to-br from-emerald-500 to-emerald-600 rounded-xl shadow-lg p-6 text-white">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between">
<h2 class="text-xl font-semibold text-gray-800"> <div>
<i class="fas fa-building mr-2 text-green-600"></i>Venue <p class="text-emerald-100 text-sm font-medium">Total Venue</p>
</h2> <p class="text-3xl font-bold">{{ $venueCount ?? 0 }}</p>
<span class="bg-green-100 text-green-800 text-xs font-medium px-3 py-1 rounded-full"> </div>
Total: {{ $venueCount ?? 0 }} <div class="bg-emerald-400 bg-opacity-30 rounded-full p-3">
</span> <i class="fas fa-building text-2xl"></i>
</div>
</div> </div>
<p class="text-gray-600 mb-4">Kelola semua venue dalam sistem</p>
<a href="{{ route('superadmin.venue.index') }}"
class="inline-flex items-center text-green-600 hover:text-green-800">
Lihat Detail
<i class="fas fa-arrow-right ml-2"></i>
</a>
</div> </div>
</div>
<!-- Recent Activity Section --> <div class="bg-gradient-to-br from-amber-500 to-orange-500 rounded-xl shadow-lg p-6 text-white">
<div class="bg-white rounded-lg shadow overflow-hidden mb-6"> <div class="flex items-center justify-between">
<div class="border-b border-gray-200 px-6 py-4"> <div>
<h3 class="text-lg font-semibold text-gray-800">Aktivitas Terbaru</h3> <p class="text-amber-100 text-sm font-medium">Total User</p>
<p class="text-3xl font-bold">{{ $userCount ?? 0 }}</p>
</div>
<div class="bg-amber-400 bg-opacity-30 rounded-full p-3">
<i class="fas fa-users text-2xl"></i>
</div>
</div>
</div> </div>
<div class="p-6">
<div class="space-y-4"> <div class="bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl shadow-lg p-6 text-white">
<!-- Sample activity items - in production, these would come from database --> <div class="flex items-center justify-between">
<div class="flex items-center"> <div>
<div class="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600"> <p class="text-purple-100 text-sm font-medium">Total Meja</p>
<i class="fas fa-user-plus"></i> <p class="text-3xl font-bold">{{ $tableCount ?? 0 }}</p>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-900">Admin baru ditambahkan</p>
<p class="text-xs text-gray-500">2 jam yang lalu</p>
</div>
</div> </div>
<div class="flex items-center"> <div class="bg-purple-400 bg-opacity-30 rounded-full p-3">
<div class="w-10 h-10 rounded-full bg-green-100 flex items-center justify-center text-green-600"> <i class="fas fa-table text-2xl"></i>
<i class="fas fa-building"></i>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-900">Venue baru ditambahkan</p>
<p class="text-xs text-gray-500">1 hari yang lalu</p>
</div>
</div>
<div class="flex items-center">
<div class="w-10 h-10 rounded-full bg-yellow-100 flex items-center justify-center text-yellow-600">
<i class="fas fa-edit"></i>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-900">Venue diperbarui</p>
<p class="text-xs text-gray-500">2 hari yang lalu</p>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- Quick Stats --> <!-- Charts Section -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div class="bg-white rounded-lg shadow p-6"> <!-- Revenue Comparison Chart -->
<div class="flex items-center"> <div class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="rounded-full bg-blue-100 p-3"> <div class="bg-gradient-to-r from-indigo-500 to-purple-600 px-6 py-4">
<i class="fas fa-users text-blue-600 text-xl"></i> <h3 class="text-xl font-bold text-white flex items-center">
</div> <i class="fas fa-chart-bar mr-3"></i>
<div class="ml-4"> Revenue Bulan Ini
<h4 class="text-gray-500 text-sm">Total Pengguna</h4> </h3>
<p class="text-2xl font-bold text-gray-800">{{ $userCount ?? 0 }}</p> <p class="text-indigo-100 text-sm">Per venue - {{ Carbon\Carbon::now()->format('F Y') }}</p>
</div> </div>
<div class="p-6">
<canvas id="revenueChart" width="400" height="300"></canvas>
</div> </div>
</div> </div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center"> <!-- Popular Venues Ranking -->
<div class="rounded-full bg-green-100 p-3"> <div class="bg-white rounded-xl shadow-lg overflow-hidden">
<i class="fas fa-check-circle text-green-600 text-xl"></i> <div class="bg-gradient-to-r from-pink-500 to-rose-600 px-6 py-4">
</div> <h3 class="text-xl font-bold text-white flex items-center">
<div class="ml-4"> <i class="fas fa-trophy mr-3"></i>
<h4 class="text-gray-500 text-sm">Venue Aktif</h4> Ranking Popularitas
<p class="text-2xl font-bold text-gray-800">{{ $activeVenueCount ?? 0 }}</p> </h3>
</div> <p class="text-pink-100 text-sm">Venue terpopuler bulan ini</p>
</div> </div>
</div> <div class="p-6">
<div class="bg-white rounded-lg shadow p-6"> <!-- Filter Toggle -->
<div class="flex items-center"> <div class="mb-4">
<div class="rounded-full bg-purple-100 p-3"> <div class="bg-gray-100 rounded-lg p-1 flex">
<i class="fas fa-table text-purple-600 text-xl"></i> <button
</div> id="rankingByBookings"
<div class="ml-4"> class="flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all duration-200 bg-rose-500 text-white"
<h4 class="text-gray-500 text-sm">Total Meja</h4> onclick="toggleRanking('bookings')">
<p class="text-2xl font-bold text-gray-800">{{ $tableCount ?? 0 }}</p> <i class="fas fa-calendar-check mr-2"></i>
Total Booking
</button>
<button
id="rankingByRevenue"
class="flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all duration-200 text-gray-600 hover:text-gray-800"
onclick="toggleRanking('revenue')">
<i class="fas fa-money-bill-wave mr-2"></i>
Total Revenue
</button>
</div>
</div> </div>
<canvas id="popularVenuesChart" width="400" height="300"></canvas>
</div> </div>
</div> </div>
</div> </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<script>
// Revenue Chart Data
const revenueData = @json($revenueData);
// Popular Venues Data
const popularVenuesData = @json($popularVenuesData);
// Color Palettes
const revenueColors = [
'rgba(99, 102, 241, 0.8)', // Indigo
'rgba(168, 85, 247, 0.8)', // Purple
'rgba(236, 72, 153, 0.8)', // Pink
'rgba(34, 197, 94, 0.8)', // Emerald
'rgba(251, 146, 60, 0.8)', // Orange
];
const rankingColors = [
'rgba(239, 68, 68, 0.8)', // Red
'rgba(245, 158, 11, 0.8)', // Amber
'rgba(34, 197, 94, 0.8)', // Emerald
'rgba(59, 130, 246, 0.8)', // Blue
'rgba(168, 85, 247, 0.8)', // Purple
];
// Revenue Chart
const revenueCtx = document.getElementById('revenueChart').getContext('2d');
const revenueChart = new Chart(revenueCtx, {
type: 'bar',
data: {
labels: revenueData.map(item => item.venue_name),
datasets: [{
label: 'Revenue (Rp)',
data: revenueData.map(item => parseFloat(item.total_revenue)),
backgroundColor: revenueColors,
borderRadius: 8,
borderSkipped: false,
}]
},
options: {
indexAxis: 'y',
responsive: true,
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
label: function(context) {
return 'Revenue: Rp ' + new Intl.NumberFormat('id-ID').format(context.parsed.x);
}
}
}
},
scales: {
x: {
beginAtZero: true,
ticks: {
callback: function(value) {
return 'Rp ' + new Intl.NumberFormat('id-ID', {
notation: 'compact',
maximumFractionDigits: 1
}).format(value);
}
}
},
y: {
grid: {
display: false
}
}
}
}
});
// Popular Venues Chart
const popularCtx = document.getElementById('popularVenuesChart').getContext('2d');
let popularChart;
function createPopularChart(sortBy) {
const sortedData = [...popularVenuesData].sort((a, b) => {
if (sortBy === 'revenue') {
return parseFloat(b.total_revenue) - parseFloat(a.total_revenue);
} else {
return parseInt(b.total_bookings) - parseInt(a.total_bookings);
}
});
const chartData = {
labels: sortedData.map(item => item.venue_name),
datasets: [{
label: sortBy === 'revenue' ? 'Revenue (Rp)' : 'Total Booking',
data: sortedData.map(item => sortBy === 'revenue' ? parseFloat(item.total_revenue) : parseInt(item.total_bookings)),
backgroundColor: rankingColors,
borderRadius: 8,
borderSkipped: false,
}]
};
if (popularChart) {
popularChart.destroy();
}
popularChart = new Chart(popularCtx, {
type: 'bar',
data: chartData,
options: {
indexAxis: 'y',
responsive: true,
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
label: function(context) {
if (sortBy === 'revenue') {
return 'Revenue: Rp ' + new Intl.NumberFormat('id-ID').format(context.parsed.x);
} else {
return 'Total Booking: ' + context.parsed.x;
}
}
}
}
},
scales: {
x: {
beginAtZero: true,
ticks: {
callback: function(value) {
if (sortBy === 'revenue') {
return 'Rp ' + new Intl.NumberFormat('id-ID', {
notation: 'compact',
maximumFractionDigits: 1
}).format(value);
} else {
return value;
}
}
}
},
y: {
grid: {
display: false
}
}
}
}
});
}
// Initialize popular venues chart
createPopularChart('bookings');
// Toggle function for ranking chart
function toggleRanking(type) {
const bookingBtn = document.getElementById('rankingByBookings');
const revenueBtn = document.getElementById('rankingByRevenue');
if (type === 'bookings') {
bookingBtn.className = 'flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all duration-200 bg-rose-500 text-white';
revenueBtn.className = 'flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all duration-200 text-gray-600 hover:text-gray-800';
createPopularChart('bookings');
} else {
revenueBtn.className = 'flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all duration-200 bg-rose-500 text-white';
bookingBtn.className = 'flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all duration-200 text-gray-600 hover:text-gray-800';
createPopularChart('revenue');
}
}
</script>
@endsection @endsection