Checkpoint

This commit is contained in:
Stephen Gesityan 2025-06-05 01:57:25 +07:00
parent 73b264107e
commit 6873f94b83
9 changed files with 702 additions and 402 deletions

View File

@ -0,0 +1,73 @@
<?php
namespace App\Http\Controllers\admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;
class AdminProfileController extends Controller
{
/**
* Display the admin profile page.
*/
public function index()
{
$user = Auth::user();
return view('admin.profile.index', compact('user'));
}
/**
* Update the admin profile information.
*/
public function updateProfile(Request $request)
{
$user = Auth::user();
$request->validate([
'name' => 'required|string|max:255',
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique('users')->ignore($user->id),
],
]);
$user->update([
'name' => $request->name,
'email' => $request->email,
]);
return redirect()->route('admin.profile.index')
->with('success', 'Profile berhasil diperbarui.');
}
/**
* Update the admin password.
*/
public function updatePassword(Request $request)
{
$request->validate([
'current_password' => 'required',
'password' => 'required|string|min:8|confirmed',
]);
$user = Auth::user();
// Check if current password is correct
if (!Hash::check($request->current_password, $user->password)) {
return back()->withErrors(['current_password' => 'Password saat ini tidak sesuai.']);
}
$user->update([
'password' => Hash::make($request->password),
]);
return redirect()->route('admin.profile.index')
->with('success', 'Password berhasil diperbarui.');
}
}

View File

@ -63,7 +63,7 @@ public function index(Request $request)
$query->orderBy($sortColumn, $sortDirection);
}
$bookings = $query->paginate(10)->withQueryString();
$bookings = $query->paginate(20)->withQueryString();
return view('admin.bookings.index', compact('bookings'));
}

View File

@ -29,7 +29,7 @@ public function index(Request $request)
$query->where('status', $request->status);
}
$tables = $query->latest()->paginate(10);
$tables = $query->orderBy('created_at', 'asc')->paginate(10);
return view('admin.tables.index', compact('tables'));
}

View File

@ -251,13 +251,13 @@ class="inline">
</div>
<div class="px-4 py-3 bg-gray-50 border-t border-gray-200">
<div class="flex items-center justify-between">
<div class="flex flex-col md:flex-row items-center justify-between space-y-2 md:space-y-0">
<div class="text-sm text-gray-500">
Menampilkan {{ $bookings->firstItem() ?? 0 }} - {{ $bookings->lastItem() ?? 0 }} dari
{{ $bookings->total() }} data
</div>
<div>
{{ $bookings->appends(request()->query())->links() }}
<div class="flex justify-center">
{{ $bookings->appends(request()->query())->onEachSide(1)->links() }}
</div>
</div>
</div>

View File

@ -0,0 +1,177 @@
@extends('layouts.admin')
@section('content')
<div class="p-6">
<!-- Page Header -->
<div class="mb-8">
<h1 class="text-2xl font-bold text-gray-900">Pengaturan Profile</h1>
<p class="text-gray-600 mt-1">Kelola informasi akun dan keamanan Anda</p>
</div>
<!-- Flash Messages -->
@if(session('success'))
<div class="mb-6 bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-lg">
{{ session('success') }}
</div>
@endif
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<!-- Profile Information Card -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200">
<div class="p-6 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900">Informasi Profile</h2>
<p class="text-sm text-gray-600 mt-1">Perbarui informasi dasar akun Anda</p>
</div>
<form action="{{ route('admin.profile.update') }}" method="POST" class="p-6">
@csrf
@method('PUT')
<!-- Profile Avatar -->
<div class="flex items-center space-x-4 mb-6">
<div
class="w-16 h-16 bg-blue-600 rounded-full flex items-center justify-center text-white font-bold text-xl">
{{ substr($user->name, 0, 1) }}
</div>
<div>
<h3 class="font-medium text-gray-900">{{ $user->name }}</h3>
<p class="text-sm text-gray-500">{{ ucfirst($user->role) }}</p>
@if($user->venue)
<p class="text-sm text-blue-600">{{ $user->venue->name }}</p>
@endif
</div>
</div>
<!-- Name Field -->
<div class="mb-4">
<label for="name" class="block text-sm font-medium text-gray-700 mb-2">
Nama Lengkap
</label>
<input type="text" id="name" name="name" value="{{ old('name', $user->name) }}"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent @error('name') border-red-500 @enderror">
@error('name')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<!-- Email Field -->
<div class="mb-6">
<label for="email" class="block text-sm font-medium text-gray-700 mb-2">
Email
</label>
<input type="email" id="email" name="email" value="{{ old('email', $user->email) }}"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent @error('email') border-red-500 @enderror"
readonly>
@error('email')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<!-- Account Info -->
<div class="mb-6 p-4 bg-gray-50 rounded-lg">
<h4 class="font-medium text-gray-900 mb-2">Informasi Akun</h4>
<div class="text-sm text-gray-600 space-y-1">
<p><span class="font-medium">Role:</span> {{ ucfirst($user->role) }}</p>
<p><span class="font-medium">Bergabung:</span> {{ $user->created_at->format('d M Y') }}</p>
@if($user->email_verified_at)
<p><span class="font-medium">Status Email:</span>
<span class="text-green-600"> Terverifikasi</span>
</p>
@else
<p><span class="font-medium">Status Email:</span>
<span class="text-red-600"> Belum Terverifikasi</span>
</p>
@endif
</div>
</div>
<!-- Submit Button -->
<button type="submit"
class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition duration-200">
Perbarui Profile
</button>
</form>
</div>
<!-- Change Password Card -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200">
<div class="p-6 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900">Ubah Password</h2>
<p class="text-sm text-gray-600 mt-1">Pastikan akun Anda menggunakan password yang kuat</p>
</div>
<form action="{{ route('admin.profile.password') }}" method="POST" class="p-6">
@csrf
@method('PUT')
<!-- Current Password -->
<div class="mb-4">
<label for="current_password" class="block text-sm font-medium text-gray-700 mb-2">
Password Saat Ini
</label>
<input type="password" id="current_password" name="current_password"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent @error('current_password') border-red-500 @enderror">
@error('current_password')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<!-- New Password -->
<div class="mb-4">
<label for="password" class="block text-sm font-medium text-gray-700 mb-2">
Password Baru
</label>
<input type="password" id="password" name="password"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent @error('password') border-red-500 @enderror">
@error('password')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<!-- Confirm Password -->
<div class="mb-6">
<label for="password_confirmation" class="block text-sm font-medium text-gray-700 mb-2">
Konfirmasi Password Baru
</label>
<input type="password" id="password_confirmation" name="password_confirmation"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<!-- Password Requirements -->
<div class="mb-6 p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
<h4 class="font-medium text-yellow-800 mb-2">Persyaratan Password:</h4>
<ul class="text-sm text-yellow-700 space-y-1">
<li> Minimal 8 karakter</li>
<li> Kombinasi huruf besar dan kecil</li>
<li> Minimal satu angka</li>
<li> Minimal satu karakter khusus</li>
</ul>
</div>
<!-- Submit Button -->
<button type="submit"
class="w-full bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded-lg transition duration-200">
Ubah Password
</button>
</form>
</div>
</div>
<!-- Security Notice -->
<div class="mt-8 bg-blue-50 border border-blue-200 rounded-lg p-6">
<div class="flex items-start space-x-3">
<svg class="w-5 h-5 text-blue-600 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div>
<h3 class="font-medium text-blue-900">Tips Keamanan</h3>
<p class="text-sm text-blue-700 mt-1">
Selalu gunakan password yang unik dan kuat. Jangan bagikan informasi login Anda kepada siapa pun.
Jika Anda mencurigai adanya aktivitas yang tidak biasa, segera ubah password Anda.
</p>
</div>
</div>
</div>
</div>
@endsection

View File

@ -189,8 +189,10 @@ class="ml-auto h-4 w-4 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="cu
<!-- Dropdown -->
<div x-show="open" @click.outside="open = false"
class="absolute bottom-full left-0 mb-1 w-full bg-white rounded-lg shadow-lg border border-gray-200 py-1 dropdown-transition">
{{-- <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a>
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Settings</a> --}}
<a href="{{ route('admin.profile.index') }}"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a>
{{-- <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Settings</a>
--}}
<div class="border-t border-gray-200 my-1"></div>
<form method="POST" action="{{ route('logout') }}">
@csrf

View File

@ -7,178 +7,159 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Super Admin Dashboard</title>
<!-- Tailwind CSS via CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<!-- FontAwesome Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Alpine.js -->
<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;
body {
font-family: 'Inter', sans-serif;
}
.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;
transition: all 0.3s ease;
}
.nav-item::before {
.nav-item.active {
position: relative;
background-color: rgb(239, 246, 255);
color: rgb(37, 99, 235);
font-weight: 500;
}
.nav-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transition: left 0.5s;
width: 4px;
background-color: rgb(37, 99, 235);
border-radius: 0 4px 4px 0;
}
.nav-item:hover::before {
left: 100%;
.nav-item:hover:not(.active) {
background-color: rgb(249, 250, 251);
color: rgb(55, 65, 81);
}
/* 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);
.dropdown-transition {
transition: all 0.2s ease-out;
}
</style>
</head>
<body class="bg-gray-50">
<div x-data="{ sidebarOpen: true }">
<body x-data="{ sidebarOpen: true, userDropdownOpen: false }" class="bg-gray-50">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar Overlay -->
<div x-show="sidebarOpen" @click="sidebarOpen = false"
class="fixed inset-0 z-20 bg-black bg-opacity-50 lg:hidden"></div>
<!-- Sidebar -->
<aside :class="sidebarOpen ? 'translate-x-0' : '-translate-x-full'"
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">
<!-- Header -->
<div class="flex items-center justify-between p-6 border-b border-slate-600/30">
<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 :class="sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0 lg:w-20'"
class="fixed inset-y-0 left-0 z-30 w-64 bg-white shadow-lg transition-all duration-300 transform lg:relative lg:translate-x-0">
<!-- Sidebar Header -->
<div class="flex items-center justify-between h-16 px-4 border-b">
<div class="flex items-center space-x-2">
<span class="font-bold text-lg text-gray-800" x-show="sidebarOpen">Super Admin</span>
</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 @click="sidebarOpen = !sidebarOpen" class="p-1 rounded-md hover:bg-gray-100 focus:outline-none">
<svg x-show="sidebarOpen" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500"
fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
</svg>
<svg x-show="!sidebarOpen" class="w-6 h-6 text-gray-500" fill="none" stroke="currentColor"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16m-7 6h7"></path>
</svg>
</button>
</div>
<!-- Profile Section -->
<div class="p-6">
<div class="profile-section rounded-xl p-4 mb-6">
<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 class="flex-1">
<p class="font-semibold text-white">{{ 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>
<!-- Navigation -->
<div class="px-2 py-4">
<div x-show="sidebarOpen"
class="text-xs font-semibold text-gray-400 uppercase tracking-wider px-3 mb-2">
Menu Utama
</div>
<!-- Navigation -->
<nav class="space-y-2">
<div class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-4 px-3">
Main Navigation
</div>
<nav class="space-y-1">
<a href="{{ route('superadmin.dashboard') }}"
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>
class="nav-item flex items-center px-3 py-2.5 rounded-lg {{ request()->routeIs('superadmin.dashboard') ? 'active' : '' }}">
<i class="fas fa-chart-line w-5 h-5 mr-2 text-sm"></i>
<span x-show="sidebarOpen">Dashboard</span>
</a>
<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">
<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 text-emerald-400 group-hover:text-emerald-300"></i>
</div>
<div class="ml-4">
<span class="font-medium text-slate-200 group-hover:text-white transition-colors">Manajemen Venue</span>
<p class="text-xs text-slate-400 group-hover:text-slate-300">Kelola semua venue</p>
</div>
</a>
<a href="{{ route('superadmin.admin.index') }}"
class="nav-item flex items-center p-3 rounded-lg hover:bg-slate-600/30 {{ request()->routeIs('superadmin.admin.*') ? 'nav-active' : '' }} group">
<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">
<i class="fas fa-users-cog text-purple-400 group-hover:text-purple-300"></i>
</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>
class="nav-item flex items-center px-3 py-2.5 rounded-lg {{ request()->routeIs('superadmin.venue.*') ? 'active' : '' }}">
<i class="fas fa-building w-5 h-5 mr-2 text-sm"></i>
<span x-show="sidebarOpen">Manajemen Venue</span>
</a>
<a href="{{ route('logout') }}"
onclick="event.preventDefault(); document.getElementById('logout-form').submit();"
class="flex items-center p-3 rounded-lg hover:bg-red-700">
<i class="fas fa-sign-out-alt w-5"></i>
<span class="ml-3">Logout</span>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="hidden">
@csrf
</form>
</a>
<a href="{{ route('superadmin.admin.index') }}"
class="nav-item flex items-center px-3 py-2.5 rounded-lg {{ request()->routeIs('superadmin.admin.*') ? 'active' : '' }}">
<i class="fas fa-users-cog w-5 h-5 mr-2 text-sm"></i>
<span x-show="sidebarOpen">Manajemen Admin</span>
</a>
</nav>
</div>
</aside>
<!-- Content -->
<div :class="sidebarOpen ? 'lg:ml-72' : ''" class="transition-all duration-300">
<!-- Top bar -->
<header class="bg-white/80 backdrop-blur-md border-b border-gray-200/50 sticky top-0 z-30 shadow-sm">
<div class="px-6 py-4 flex items-center justify-between">
<!-- User Profile -->
<div class="absolute bottom-0 w-full border-t border-gray-200">
<div x-data="{ open: false }" class="relative p-4">
<button @click="open = !open" class="flex items-center w-full text-left focus:outline-none">
<div class="flex-shrink-0">
<div
class="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center text-white font-semibold">
{{ substr(auth()->user()->name ?? 'SA', 0, 1) }}
</div>
</div>
<div x-show="sidebarOpen" class="ml-3">
<p class="text-sm font-medium text-gray-800 truncate">
{{ auth()->user()->name ?? 'Super Admin' }}</p>
<p class="text-xs text-gray-500 truncate">{{ auth()->user()->email ??
'superadmin@example.com' }}</p>
</div>
<svg x-show="sidebarOpen" xmlns="http://www.w3.org/2000/svg"
class="ml-auto h-4 w-4 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
<!-- Dropdown -->
<div x-show="open" @click.outside="open = false"
class="absolute bottom-full left-0 mb-1 w-full bg-white rounded-lg shadow-lg border border-gray-200 py-1 dropdown-transition">
<div class="border-t border-gray-200 my-1"></div>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit"
class="block w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-gray-100">
Logout
</button>
</form>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Top Header -->
{{-- <header class="bg-white shadow-sm">
<div class="px-4 sm:px-6 lg:px-8 py-4 flex items-center justify-between">
<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 @click="sidebarOpen = !sidebarOpen"
class="p-1 rounded-md text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 lg:inline-block">
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</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>
@ -186,68 +167,67 @@ class="w-10 h-10 rounded-lg bg-gray-100 hover:bg-gray-200 transition-colors flex
<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">
<button class="relative p-2 rounded-md text-gray-500 hover:bg-gray-100 focus:outline-none">
<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 }">
<button @click="open = !open"
<button @click="open = !open"
class="flex items-center space-x-3 p-2 rounded-lg hover:bg-gray-100 transition-colors">
<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
class="w-8 h-8 rounded-full bg-blue-600 flex items-center justify-center text-white font-semibold">
{{ substr(auth()->user()->name ?? 'SA', 0, 1) }}
</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-sm font-medium text-gray-900">{{ auth()->user()->name ?? 'Super
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>
<i class="fas fa-chevron-down text-xs text-gray-400" :class="{ 'rotate-180': open }"
style="transition: transform 0.2s"></i>
</button>
<div x-show="open"
@click.away="open = false"
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 x-show="open" @click.away="open = false"
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>
<p class="text-sm font-medium text-gray-900">{{ auth()->user()->name ?? 'Super
Admin' }}</p>
<p class="text-xs text-gray-500">{{ auth()->user()->email ??
'superadmin@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();"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Logout</a>
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>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="hidden">
@csrf
</form>
</div>
</div>
</div>
</div>
</div>
</header>
</header> --}}
<!-- Main content -->
<main class="p-6">
<!-- Page Content -->
<main class="flex-1 overflow-x-hidden overflow-y-auto">
@yield('content')
</main>
</div>
</div>
@stack('scripts')
</body>
</html>

View File

@ -1,235 +1,206 @@
@extends('layouts.super-admin')
@section('content')
<div class="mb-8">
<h1 class="text-4xl font-bold bg-gradient-to-r from-purple-600 to-pink-600 bg-clip-text text-transparent">
Dashboard Super Admin
</h1>
<p class="text-gray-600 text-lg">Analytics & Overview dari seluruh venue</p>
</div>
<!-- Main Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div class="bg-gradient-to-br from-blue-500 to-blue-600 rounded-xl shadow-lg p-6 text-white">
<div class="flex items-center justify-between">
<div class="bg-gray-50 min-h-screen">
<div class="p-6">
<!-- Header and Welcome -->
<div class="flex justify-between items-center mb-6">
<div>
<p class="text-blue-100 text-sm font-medium">Total Admin</p>
<p class="text-3xl font-bold">{{ $adminCount ?? 0 }}</p>
<h1 class="text-3xl font-bold text-gray-800">Dashboard Super Admin</h1>
<p class="text-gray-600 mt-1">Analytics & Overview dari seluruh venue</p>
</div>
<div class="bg-blue-400 bg-opacity-30 rounded-full p-3">
<i class="fas fa-users-cog text-2xl"></i>
<div class="text-right">
<p class="text-sm text-gray-500">{{ now()->format('l, d F Y') }}</p>
<p class="text-2xl font-semibold text-gray-800">{{ now()->format('H:i') }}</p>
</div>
</div>
</div>
<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">
<div>
<p class="text-emerald-100 text-sm font-medium">Total Venue</p>
<p class="text-3xl font-bold">{{ $venueCount ?? 0 }}</p>
<!-- Main Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<!-- Total Admin Card -->
<div class="bg-white rounded-xl shadow-sm p-6 border-l-4 border-blue-500 hover:shadow-md transition">
<div class="flex justify-between items-start">
<div>
<p class="text-sm font-medium text-gray-500">Total Admin</p>
<p class="text-2xl font-bold text-gray-800">{{ $adminCount ?? 0 }}</p>
</div>
<div class="text-blue-500 p-2 bg-blue-50 rounded-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z" />
</svg>
</div>
</div>
<p class="text-xs text-gray-500 mt-2">Administrator aktif</p>
</div>
<div class="bg-emerald-400 bg-opacity-30 rounded-full p-3">
<i class="fas fa-building text-2xl"></i>
</div>
</div>
</div>
<div class="bg-gradient-to-br from-amber-500 to-orange-500 rounded-xl shadow-lg p-6 text-white">
<div class="flex items-center justify-between">
<div>
<p class="text-amber-100 text-sm font-medium">Total User</p>
<p class="text-3xl font-bold">{{ $userCount ?? 0 }}</p>
<!-- Total Venue Card -->
<div class="bg-white rounded-xl shadow-sm p-6 border-l-4 border-green-500 hover:shadow-md transition">
<div class="flex justify-between items-start">
<div>
<p class="text-sm font-medium text-gray-500">Total Venue</p>
<p class="text-2xl font-bold text-gray-800">{{ $venueCount ?? 0 }}</p>
</div>
<div class="text-green-500 p-2 bg-green-50 rounded-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
</div>
</div>
<p class="text-xs text-gray-500 mt-2">Venue terdaftar</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 class="bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl shadow-lg p-6 text-white">
<div class="flex items-center justify-between">
<div>
<p class="text-purple-100 text-sm font-medium">Total Meja</p>
<p class="text-3xl font-bold">{{ $tableCount ?? 0 }}</p>
<!-- Total User Card -->
<div class="bg-white rounded-xl shadow-sm p-6 border-l-4 border-amber-500 hover:shadow-md transition">
<div class="flex justify-between items-start">
<div>
<p class="text-sm font-medium text-gray-500">Total User</p>
<p class="text-2xl font-bold text-gray-800">{{ $userCount ?? 0 }}</p>
</div>
<div class="text-amber-500 p-2 bg-amber-50 rounded-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z" />
</svg>
</div>
</div>
<p class="text-xs text-gray-500 mt-2">Pengguna terdaftar</p>
</div>
<div class="bg-purple-400 bg-opacity-30 rounded-full p-3">
<i class="fas fa-table text-2xl"></i>
<!-- Total Meja Card -->
<div class="bg-white rounded-xl shadow-sm p-6 border-l-4 border-purple-500 hover:shadow-md transition">
<div class="flex justify-between items-start">
<div>
<p class="text-sm font-medium text-gray-500">Total Meja</p>
<p class="text-2xl font-bold text-gray-800">{{ $tableCount ?? 0 }}</p>
</div>
<div class="text-purple-500 p-2 bg-purple-50 rounded-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
</div>
</div>
<p class="text-xs text-gray-500 mt-2">Meja tersedia</p>
</div>
</div>
</div>
</div>
<!-- Charts Section -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<!-- Revenue Comparison Chart -->
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="bg-gradient-to-r from-indigo-500 to-purple-600 px-6 py-4">
<h3 class="text-xl font-bold text-white flex items-center">
<i class="fas fa-chart-bar mr-3"></i>
Revenue Bulan Ini
</h3>
<p class="text-indigo-100 text-sm">Per venue - {{ Carbon\Carbon::now()->format('F Y') }}</p>
</div>
<div class="p-6">
<canvas id="revenueChart" width="400" height="300"></canvas>
</div>
</div>
<!-- Popular Venues Ranking -->
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="bg-gradient-to-r from-pink-500 to-rose-600 px-6 py-4">
<h3 class="text-xl font-bold text-white flex items-center">
<i class="fas fa-trophy mr-3"></i>
Ranking Popularitas
</h3>
<p class="text-pink-100 text-sm">Venue terpopuler bulan ini</p>
</div>
<div class="p-6">
<!-- Filter Toggle -->
<div class="mb-4">
<div class="bg-gray-100 rounded-lg p-1 flex">
<button
id="rankingByBookings"
class="flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all duration-200 bg-rose-500 text-white"
onclick="toggleRanking('bookings')">
<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>
<!-- Charts Section -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Revenue Comparison Chart -->
<div class="bg-white rounded-xl shadow-sm">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-semibold text-gray-800 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
Revenue Bulan Ini
</h3>
<p class="text-gray-500 text-sm">Per venue - {{ Carbon\Carbon::now()->format('F Y') }}</p>
</div>
<div class="p-6">
<div style="height: 300px;">
<canvas id="revenueChart"></canvas>
</div>
</div>
</div>
<!-- Popular Venues Ranking -->
<div class="bg-white rounded-xl shadow-sm">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-semibold text-gray-800 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" />
</svg>
Ranking Popularitas
</h3>
<p class="text-gray-500 text-sm">Venue terpopuler bulan ini</p>
</div>
<div class="p-6">
<!-- Filter Toggle -->
<div class="mb-4">
<div class="bg-gray-100 rounded-lg p-1 flex">
<button id="rankingByBookings"
class="flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all duration-200 bg-green-600 text-white"
onclick="toggleRanking('bookings')">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
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')">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Total Revenue
</button>
</div>
</div>
<div style="height: 300px;">
<canvas id="popularVenuesChart"></canvas>
</div>
</div>
</div>
<canvas id="popularVenuesChart" width="400" height="300"></canvas>
</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);
@push('scripts')
<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);
// 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
];
// Popular Venues Data
const popularVenuesData = @json($popularVenuesData);
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
];
// Color Palettes - More subtle and professional
const revenueColors = [
'rgba(59, 130, 246, 0.8)', // Blue
'rgba(16, 185, 129, 0.8)', // Emerald
'rgba(245, 158, 11, 0.8)', // Amber
'rgba(139, 92, 246, 0.8)', // Violet
'rgba(236, 72, 153, 0.8)', // Pink
];
// 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
}
}
}
}
});
const rankingColors = [
'rgba(34, 197, 94, 0.8)', // Green
'rgba(59, 130, 246, 0.8)', // Blue
'rgba(245, 158, 11, 0.8)', // Amber
'rgba(139, 92, 246, 0.8)', // Violet
'rgba(107, 114, 128, 0.8)', // Gray
];
// 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, {
// Revenue Chart
const revenueCtx = document.getElementById('revenueChart').getContext('2d');
const revenueChart = new Chart(revenueCtx, {
type: 'bar',
data: chartData,
data: {
labels: revenueData.map(item => item.venue_name),
datasets: [{
label: 'Revenue (Rp)',
data: revenueData.map(item => parseFloat(item.total_revenue)),
backgroundColor: revenueColors,
borderRadius: 4,
borderSkipped: false,
}]
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
tooltip: {
backgroundColor: 'rgba(17, 24, 39, 0.9)',
titleColor: '#fff',
bodyColor: '#fff',
borderColor: 'rgba(156, 163, 175, 0.2)',
borderWidth: 1,
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;
}
label: function (context) {
return 'Revenue: Rp ' + new Intl.NumberFormat('id-ID').format(context.parsed.x);
}
}
}
@ -237,46 +208,138 @@ function createPopularChart(sortBy) {
scales: {
x: {
beginAtZero: true,
grid: {
color: 'rgba(156, 163, 175, 0.1)'
},
ticks: {
callback: function(value) {
if (sortBy === 'revenue') {
return 'Rp ' + new Intl.NumberFormat('id-ID', {
notation: 'compact',
maximumFractionDigits: 1
}).format(value);
} else {
return value;
}
color: '#6B7280',
callback: function (value) {
return 'Rp ' + new Intl.NumberFormat('id-ID', {
notation: 'compact',
maximumFractionDigits: 1
}).format(value);
}
}
},
y: {
grid: {
display: false
},
ticks: {
color: '#6B7280'
}
}
}
}
});
}
// Initialize popular venues chart
createPopularChart('bookings');
// Popular Venues Chart
const popularCtx = document.getElementById('popularVenuesChart').getContext('2d');
let popularChart;
// Toggle function for ranking chart
function toggleRanking(type) {
const bookingBtn = document.getElementById('rankingByBookings');
const revenueBtn = document.getElementById('rankingByRevenue');
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);
}
});
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');
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: 4,
borderSkipped: false,
}]
};
if (popularChart) {
popularChart.destroy();
}
popularChart = new Chart(popularCtx, {
type: 'bar',
data: chartData,
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
tooltip: {
backgroundColor: 'rgba(17, 24, 39, 0.9)',
titleColor: '#fff',
bodyColor: '#fff',
borderColor: 'rgba(156, 163, 175, 0.2)',
borderWidth: 1,
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,
grid: {
color: 'rgba(156, 163, 175, 0.1)'
},
ticks: {
color: '#6B7280',
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
},
ticks: {
color: '#6B7280'
}
}
}
}
});
}
}
</script>
// 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-green-600 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-green-600 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>
@endpush
@endsection

View File

@ -74,6 +74,11 @@
Route::middleware(['auth', 'verified', 'is_admin'])->prefix('admin')->group(function () {
Route::get('/', [AdminController::class, 'index'])->name('admin.dashboard');
// Admin Profile Routes
Route::get('/profile', [App\Http\Controllers\admin\AdminProfileController::class, 'index'])->name('admin.profile.index');
Route::put('/profile/update', [App\Http\Controllers\admin\AdminProfileController::class, 'updateProfile'])->name('admin.profile.update');
Route::put('/profile/password', [App\Http\Controllers\admin\AdminProfileController::class, 'updatePassword'])->name('admin.profile.password');
// Booking management routes
Route::get('/bookings', [BookingsController::class, 'index'])->name('admin.bookings.index');
Route::get('/bookings/export', [BookingsController::class, 'export'])->name('admin.bookings.export');