Super Admin Panel
This commit is contained in:
parent
b4d73f7d4d
commit
d167f80959
|
@ -63,7 +63,9 @@ protected function authenticated(Request $request, $user)
|
|||
}
|
||||
|
||||
session()->flash('success', 'Login berhasil!');
|
||||
if ($user->role === 'admin') {
|
||||
if ($user->role === 'superadmin') {
|
||||
return redirect('/superadmin');
|
||||
} elseif ($user->role === 'admin') {
|
||||
return redirect('/admin');
|
||||
}
|
||||
return redirect()->intended($this->redirectTo);
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\superadmin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use App\Models\Venue;
|
||||
|
||||
class AdminManagementController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the admins.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = User::where('role', 'admin');
|
||||
|
||||
// Filter by search term
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->search;
|
||||
$query->where(function($q) use ($search) {
|
||||
$q->where('name', 'like', "%{$search}%")
|
||||
->orWhere('email', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
// Filter by venue
|
||||
if ($request->filled('venue_id')) {
|
||||
$query->where('venue_id', $request->venue_id);
|
||||
}
|
||||
|
||||
$admins = $query->paginate(10)->withQueryString();
|
||||
$venues = Venue::orderBy('name')->get();
|
||||
|
||||
return view('superadmin.admin.index', compact('admins', 'venues'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new admin.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$venues = Venue::orderBy('id')->get();
|
||||
return view('superadmin.admin.create', compact('venues'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created admin in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255|unique:users',
|
||||
'password' => 'required|string|min:8|confirmed',
|
||||
'venue_id' => 'required|exists:venues,id',
|
||||
'role' => 'required|in:admin,user',
|
||||
]);
|
||||
|
||||
$admin = new User();
|
||||
$admin->name = $request->name;
|
||||
$admin->email = $request->email;
|
||||
$admin->password = bcrypt($request->password);
|
||||
$admin->role = $request->role;
|
||||
$admin->venue_id = $request->venue_id;
|
||||
$admin->email_verified_at = now(); // Admin langsung terverifikasi
|
||||
$admin->save();
|
||||
|
||||
return redirect()->route('superadmin.admin.index')
|
||||
->with('success', 'Admin berhasil ditambahkan!');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\superadmin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use App\Models\Venue;
|
||||
use App\Models\Table;
|
||||
|
||||
class SuperAdminController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the superadmin dashboard.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Menghitung jumlah admin, venue, user, venue aktif, dan meja
|
||||
$adminCount = User::where('role', 'admin')->count();
|
||||
$venueCount = Venue::count();
|
||||
$userCount = User::where('role', 'user')->count();
|
||||
// $activeVenueCount = Venue::where('status', 'active')->count();
|
||||
$tableCount = Table::count();
|
||||
|
||||
return view('superadmin.dashboard', compact(
|
||||
'adminCount',
|
||||
'venueCount',
|
||||
'userCount',
|
||||
// 'activeVenueCount',
|
||||
'tableCount'
|
||||
));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\superadmin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Venue;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class VenueManagementController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the venues.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$venues = Venue::paginate(10);
|
||||
return view('superadmin.venue.index', compact('venues'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new venue.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('superadmin.venue.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created venue in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'address' => 'required|string',
|
||||
'description' => 'required|string',
|
||||
'phone' => 'required|string|max:20',
|
||||
'open_time' => 'required|date_format:H:i',
|
||||
'close_time' => 'required|date_format:H:i',
|
||||
'image' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048',
|
||||
// 'status' => 'required|in:active,inactive',
|
||||
]);
|
||||
|
||||
// Handle file upload
|
||||
$imagePath = null;
|
||||
if ($request->hasFile('image')) {
|
||||
// Get the file extension
|
||||
$extension = $request->file('image')->getClientOriginalExtension();
|
||||
|
||||
// Generate a unique filename
|
||||
$imageName = time() . '_' . uniqid() . '.' . $extension;
|
||||
|
||||
// Store the file in the public disk under venues directory
|
||||
$imagePath = $request->file('image')->storeAs('venues', $imageName, 'public');
|
||||
}
|
||||
|
||||
$venue = new Venue();
|
||||
$venue->name = $request->name;
|
||||
$venue->address = $request->address;
|
||||
$venue->description = $request->description;
|
||||
$venue->phone = $request->phone;
|
||||
$venue->open_time = $request->open_time;
|
||||
$venue->close_time = $request->close_time;
|
||||
$venue->image = $imagePath;
|
||||
// $venue->status = $request->status;
|
||||
$venue->save();
|
||||
|
||||
return redirect()->route('superadmin.venue.index')
|
||||
->with('success', 'Venue berhasil ditambahkan!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified venue.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$venue = Venue::findOrFail($id);
|
||||
return view('superadmin.venue.edit', compact('venue'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified venue in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'address' => 'required|string',
|
||||
'description' => 'required|string',
|
||||
'phone' => 'required|string|max:20',
|
||||
'open_time' => 'required|date_format:H:i',
|
||||
'close_time' => 'required|date_format:H:i',
|
||||
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
|
||||
// 'status' => 'required|in:active,inactive',
|
||||
]);
|
||||
|
||||
$venue = Venue::findOrFail($id);
|
||||
|
||||
// Handle file upload if a new image is provided
|
||||
if ($request->hasFile('image')) {
|
||||
// Delete old image if exists
|
||||
if ($venue->image && Storage::disk('public')->exists($venue->image)) {
|
||||
Storage::disk('public')->delete($venue->image);
|
||||
}
|
||||
|
||||
// Get the file extension
|
||||
$extension = $request->file('image')->getClientOriginalExtension();
|
||||
|
||||
// Generate a unique filename
|
||||
$imageName = time() . '_' . uniqid() . '.' . $extension;
|
||||
|
||||
// Store the file in the public disk under venues directory
|
||||
$imagePath = $request->file('image')->storeAs('venues', $imageName, 'public');
|
||||
|
||||
$venue->image = $imagePath;
|
||||
}
|
||||
|
||||
$venue->name = $request->name;
|
||||
$venue->address = $request->address;
|
||||
$venue->description = $request->description;
|
||||
$venue->phone = $request->phone;
|
||||
$venue->open_time = $request->open_time;
|
||||
$venue->close_time = $request->close_time;
|
||||
// $venue->status = $request->status;
|
||||
$venue->save();
|
||||
|
||||
return redirect()->route('superadmin.venue.index')
|
||||
->with('success', 'Venue berhasil diperbarui!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified venue from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$venue = Venue::findOrFail($id);
|
||||
|
||||
// Delete the venue image if exists
|
||||
if ($venue->image && Storage::disk('public')->exists($venue->image)) {
|
||||
Storage::disk('public')->delete($venue->image);
|
||||
}
|
||||
|
||||
$venue->delete();
|
||||
|
||||
return redirect()->route('superadmin.venue.index')
|
||||
->with('success', 'Venue berhasil dihapus!');
|
||||
}
|
||||
}
|
|
@ -65,5 +65,6 @@ class Kernel extends HttpKernel
|
|||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
'is_admin' => \App\Http\Middleware\IsAdmin::class,
|
||||
'is_superadmin' => \App\Http\Middleware\IsSuperAdmin::class,
|
||||
];
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class IsSuperAdmin
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (Auth::check() && Auth::user()->role === 'superadmin') {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'role',
|
||||
'venue_id',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -52,4 +54,12 @@ public function isAdmin()
|
|||
{
|
||||
return $this->role === 'admin';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the venue that the admin belongs to.
|
||||
*/
|
||||
public function venue()
|
||||
{
|
||||
return $this->belongsTo(Venue::class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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>
|
||||
<!-- 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>
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-100">
|
||||
<div x-data="{ sidebarOpen: true }">
|
||||
<!-- Sidebar -->
|
||||
<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">
|
||||
<div class="flex items-center justify-between p-4 border-b border-blue-700">
|
||||
<h2 class="text-2xl font-bold">Venue System</h2>
|
||||
<button @click="sidebarOpen = !sidebarOpen" class="lg:hidden">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="mb-8">
|
||||
<div class="flex items-center mb-4">
|
||||
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm font-medium">Super Admin</p>
|
||||
<p class="text-xs opacity-75">{{ auth()->user()->name ?? 'Admin' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav>
|
||||
<ul>
|
||||
<li class="mb-2">
|
||||
<a href="{{ route('superadmin.dashboard') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-blue-700 {{ request()->routeIs('superadmin.dashboard') ? 'bg-blue-700' : '' }}">
|
||||
<i class="fas fa-tachometer-alt w-5"></i>
|
||||
<span class="ml-3">Dashboard</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<a href="{{ route('superadmin.admin.index') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-blue-700 {{ request()->routeIs('superadmin.admin.*') ? 'bg-blue-700' : '' }}">
|
||||
<i class="fas fa-users-cog w-5"></i>
|
||||
<span class="ml-3">Manajemen Admin</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<a href="{{ route('superadmin.venue.index') }}"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-blue-700 {{ request()->routeIs('superadmin.venue.*') ? 'bg-blue-700' : '' }}">
|
||||
<i class="fas fa-building w-5"></i>
|
||||
<span class="ml-3">Manajemen Venue</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<a href="{{ route('logout') }}"
|
||||
onclick="event.preventDefault(); document.getElementById('logout-form').submit();"
|
||||
class="flex items-center p-3 rounded-lg hover:bg-blue-700">
|
||||
<i class="fas fa-sign-out-alt w-5"></i>
|
||||
<span class="ml-3">Logout</span>
|
||||
</a>
|
||||
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="hidden">
|
||||
@csrf
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Content -->
|
||||
<div :class="sidebarOpen ? 'lg:ml-64' : ''" class="transition-all duration-300">
|
||||
<!-- Top bar -->
|
||||
<header class="bg-white border-b border-gray-200 sticky top-0 z-30">
|
||||
<div class="px-4 py-3 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="relative" x-data="{ open: false }">
|
||||
<button @click="open = !open" class="flex items-center text-gray-600 focus:outline-none">
|
||||
<span class="mr-2">{{ auth()->user()->name ?? 'Admin' }}</span>
|
||||
<i class="fas fa-chevron-down text-xs"></i>
|
||||
</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">
|
||||
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main content -->
|
||||
<main class="p-6">
|
||||
@yield('content')
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,101 @@
|
|||
@extends('layouts.super-admin')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="text-3xl font-bold text-gray-800">Tambah Admin</h1>
|
||||
<p class="text-gray-600">Tambahkan akun admin baru untuk venue</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<form action="{{ route('superadmin.admin.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Nama Admin -->
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium text-gray-700 mb-1">Nama Admin</label>
|
||||
<input type="text" name="name" id="name" value="{{ old('name') }}"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 @error('name') border-red-300 @enderror"
|
||||
required>
|
||||
@error('name')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
|
||||
<input type="email" name="email" id="email" value="{{ old('email') }}"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 @error('email') border-red-300 @enderror"
|
||||
required>
|
||||
@error('email')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label>
|
||||
<input type="password" name="password" id="password"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 @error('password') border-red-300 @enderror"
|
||||
required>
|
||||
@error('password')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Konfirmasi Password -->
|
||||
<div>
|
||||
<label for="password_confirmation" class="block text-sm font-medium text-gray-700 mb-1">Konfirmasi
|
||||
Password</label>
|
||||
<input type="password" name="password_confirmation" id="password_confirmation"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<!-- Venue -->
|
||||
<div>
|
||||
<label for="venue_id" class="block text-sm font-medium text-gray-700 mb-1">Venue</label>
|
||||
<select name="venue_id" id="venue_id"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 @error('venue_id') border-red-300 @enderror"
|
||||
required>
|
||||
<option value="">-- Pilih Venue --</option>
|
||||
@foreach($venues as $venue)
|
||||
<option value="{{ $venue->id }}" {{ old('venue_id') == $venue->id ? 'selected' : '' }}>
|
||||
{{ $venue->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('venue_id')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Role (defaultnya Admin, tapi bisa diganti) -->
|
||||
<div>
|
||||
<label for="role" class="block text-sm font-medium text-gray-700 mb-1">Role</label>
|
||||
<select name="role" id="role"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 @error('role') border-red-300 @enderror"
|
||||
required>
|
||||
<option value="admin" {{ old('role') == 'admin' ? 'selected' : '' }}>Admin</option>
|
||||
<option value="user" {{ old('role') == 'user' ? 'selected' : '' }}>User</option>
|
||||
</select>
|
||||
@error('role')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center justify-end">
|
||||
<a href="{{ route('superadmin.admin.index') }}"
|
||||
class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
Batal
|
||||
</a>
|
||||
<button type="submit"
|
||||
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
Simpan
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
|
@ -0,0 +1,100 @@
|
|||
@extends('layouts.super-admin')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="text-3xl font-bold text-gray-800">Edit Admin</h1>
|
||||
<p class="text-gray-600">Ubah data dan role admin</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<form action="{{ route('superadmin.admin.update', $admin->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Nama Admin (read-only, tidak bisa diubah) -->
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium text-gray-700 mb-1">Nama Admin</label>
|
||||
<input type="text" name="name" id="name" value="{{ old('name', $admin->name) }}"
|
||||
class="block w-full rounded-md border-gray-300 bg-gray-100 shadow-sm @error('name') border-red-300 @enderror"
|
||||
readonly>
|
||||
@error('name')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Email (read-only, tidak bisa diubah) -->
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
|
||||
<input type="email" name="email" id="email" value="{{ old('email', $admin->email) }}"
|
||||
class="block w-full rounded-md border-gray-300 bg-gray-100 shadow-sm @error('email') border-red-300 @enderror"
|
||||
readonly>
|
||||
@error('email')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Password (opsional) -->
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password (Opsional)</label>
|
||||
<input type="password" name="password" id="password"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 @error('password') border-red-300 @enderror">
|
||||
<p class="mt-1 text-xs text-gray-500">Kosongkan jika tidak ingin mengubah password</p>
|
||||
@error('password')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Konfirmasi Password (opsional) -->
|
||||
<div>
|
||||
<label for="password_confirmation" class="block text-sm font-medium text-gray-700 mb-1">Konfirmasi Password</label>
|
||||
<input type="password" name="password_confirmation" id="password_confirmation"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
</div>
|
||||
|
||||
<!-- Venue -->
|
||||
<div>
|
||||
<label for="venue_id" class="block text-sm font-medium text-gray-700 mb-1">Venue</label>
|
||||
<select name="venue_id" id="venue_id"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 @error('venue_id') border-red-300 @enderror"
|
||||
required>
|
||||
<option value="">-- Pilih Venue --</option>
|
||||
@foreach($venues as $venue)
|
||||
<option value="{{ $venue->id }}" {{ old('venue_id', $admin->venue_id) == $venue->id ? 'selected' : '' }}>
|
||||
{{ $venue->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('venue_id')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Role -->
|
||||
<div>
|
||||
<label for="role" class="block text-sm font-medium text-gray-700 mb-1">Role</label>
|
||||
<select name="role" id="role"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 @error('role') border-red-300 @enderror"
|
||||
required>
|
||||
<option value="admin" {{ old('role', $admin->role) == 'admin' ? 'selected' : '' }}>Admin</option>
|
||||
<option value="user" {{ old('role', $admin->role) == 'user' ? 'selected' : '' }}>User</option>
|
||||
</select>
|
||||
@error('role')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center justify-end">
|
||||
<a href="{{ route('superadmin.admin.index') }}"
|
||||
class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
Batal
|
||||
</a>
|
||||
<button type="submit"
|
||||
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
Simpan Perubahan
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
|
@ -0,0 +1,222 @@
|
|||
@extends('layouts.super-admin')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6 flex justify-between items-center">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gray-800">Manajemen Admin</h1>
|
||||
<p class="text-gray-600">Kelola admin untuk setiap venue</p>
|
||||
</div>
|
||||
<a href="{{ route('superadmin.admin.create') }}"
|
||||
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 flex items-center">
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
Tambah Admin
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="bg-green-100 border-l-4 border-green-500 text-green-700 p-4 mb-6" role="alert">
|
||||
<p>{{ session('success') }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Filter and Search -->
|
||||
<div class="bg-white rounded-lg shadow mb-6 p-4">
|
||||
<form action="{{ route('superadmin.admin.index') }}" method="GET">
|
||||
<div class="flex flex-col md:flex-row gap-4">
|
||||
<div class="flex-grow">
|
||||
<label for="search" class="block text-sm font-medium text-gray-700 mb-1">Cari</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<i class="fas fa-search text-gray-400"></i>
|
||||
</div>
|
||||
<input type="text" id="search" name="search" value="{{ request('search') }}"
|
||||
class="pl-10 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
placeholder="Cari nama atau email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:w-64">
|
||||
<label for="venue_filter" class="block text-sm font-medium text-gray-700 mb-1">Filter Venue</label>
|
||||
<select id="venue_filter" name="venue_id"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
<option value="">Semua Venue</option>
|
||||
@foreach($venues as $venue)
|
||||
<option value="{{ $venue->id }}" {{ request('venue_id') == $venue->id ? 'selected' : '' }}>
|
||||
{{ $venue->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="self-end">
|
||||
<button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 text-sm">
|
||||
Cari
|
||||
</button>
|
||||
<a href="{{ route('superadmin.admin.index') }}"
|
||||
class="px-4 py-2 border border-gray-300 rounded-md bg-white hover:bg-gray-50 text-sm ml-2">
|
||||
Reset Filter
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Admin Table -->
|
||||
<div class="bg-white rounded-lg shadow overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Nama
|
||||
</th>
|
||||
<th scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Email
|
||||
</th>
|
||||
<th scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Venue
|
||||
</th>
|
||||
<th scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Status
|
||||
</th>
|
||||
<th scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Terdaftar
|
||||
</th>
|
||||
<th scope="col"
|
||||
class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Aksi
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
@forelse($admins as $admin)
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<div
|
||||
class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<div class="text-sm font-medium text-gray-900">{{ $admin->name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="text-sm text-gray-900">{{ $admin->email }}</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="text-sm text-gray-900">
|
||||
@if($admin->venue)
|
||||
{{ $admin->venue->name }}
|
||||
@else
|
||||
<span class="text-gray-400">Tidak ada venue</span>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {{ $admin->email_verified_at ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800' }}">
|
||||
{{ $admin->email_verified_at ? 'Terverifikasi' : 'Belum Verifikasi' }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{{ $admin->created_at->format('d M Y') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<a href="{{ route('superadmin.admin.edit', $admin->id) }}"
|
||||
class="text-blue-600 hover:text-blue-800 mr-3">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<button type="button" data-admin-id="{{ $admin->id }}" data-admin-name="{{ $admin->name }}"
|
||||
class="text-red-600 hover:text-red-800 delete-admin-btn">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="px-6 py-4 text-center text-gray-500">
|
||||
Tidak ada data admin ditemukan
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="px-6 py-4 border-t border-gray-200">
|
||||
{{ $admins->links() }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Admin Confirmation Modal -->
|
||||
<div id="deleteAdminModal" tabindex="-1" aria-hidden="true"
|
||||
class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-modal md:h-full">
|
||||
<div class="relative w-full h-full max-w-md md:h-auto">
|
||||
<div class="relative bg-white rounded-lg shadow">
|
||||
<div class="flex items-center justify-between p-4 border-b">
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
Konfirmasi Hapus
|
||||
</h3>
|
||||
<button type="button"
|
||||
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center"
|
||||
data-modal-hide="deleteAdminModal">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6 text-center">
|
||||
<i class="fas fa-exclamation-triangle text-5xl text-yellow-400 mb-4"></i>
|
||||
<h3 class="mb-5 text-lg font-normal text-gray-500">Apakah Anda yakin ingin menghapus admin <span
|
||||
id="admin-name-to-delete" class="font-medium"></span>?</h3>
|
||||
<form id="deleteAdminForm" method="POST" action="">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit"
|
||||
class="text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center mr-2">
|
||||
Ya, saya yakin
|
||||
</button>
|
||||
<button type="button" data-modal-hide="deleteAdminModal"
|
||||
class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10">
|
||||
Batal
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Handle delete admin button clicks
|
||||
const deleteButtons = document.querySelectorAll('.delete-admin-btn');
|
||||
deleteButtons.forEach(button => {
|
||||
button.addEventListener('click', function () {
|
||||
const adminId = this.getAttribute('data-admin-id');
|
||||
const adminName = this.getAttribute('data-admin-name');
|
||||
const form = document.getElementById('deleteAdminForm');
|
||||
form.action = "{{ route('superadmin.admin.destroy', '') }}/" + adminId;
|
||||
// Set the admin name in the confirmation message
|
||||
document.getElementById('admin-name-to-delete').textContent = adminName;
|
||||
|
||||
// Show the delete confirmation modal
|
||||
const modal = document.getElementById('deleteAdminModal');
|
||||
modal.classList.remove('hidden');
|
||||
});
|
||||
});
|
||||
|
||||
// Modal hide functionality
|
||||
const modalHideButtons = document.querySelectorAll('[data-modal-hide]');
|
||||
modalHideButtons.forEach(button => {
|
||||
button.addEventListener('click', function () {
|
||||
const modalId = this.getAttribute('data-modal-hide');
|
||||
document.getElementById(modalId).classList.add('hidden');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
|
@ -0,0 +1,120 @@
|
|||
@extends('layouts.super-admin')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="text-3xl font-bold text-gray-800">Dashboard Super Admin</h1>
|
||||
<p class="text-gray-600">Selamat datang di panel kontrol Super Admin</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-xl font-semibold text-gray-800">
|
||||
<i class="fas fa-users-cog mr-2 text-blue-600"></i>Admin
|
||||
</h2>
|
||||
<span class="bg-blue-100 text-blue-800 text-xs font-medium px-3 py-1 rounded-full">
|
||||
Total: {{ $adminCount ?? 0 }}
|
||||
</span>
|
||||
</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 class="bg-white rounded-lg shadow p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-xl font-semibold text-gray-800">
|
||||
<i class="fas fa-building mr-2 text-green-600"></i>Venue
|
||||
</h2>
|
||||
<span class="bg-green-100 text-green-800 text-xs font-medium px-3 py-1 rounded-full">
|
||||
Total: {{ $venueCount ?? 0 }}
|
||||
</span>
|
||||
</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>
|
||||
|
||||
<!-- Recent Activity Section -->
|
||||
<div class="bg-white rounded-lg shadow overflow-hidden mb-6">
|
||||
<div class="border-b border-gray-200 px-6 py-4">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Aktivitas Terbaru</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="space-y-4">
|
||||
<!-- Sample activity items - in production, these would come from database -->
|
||||
<div class="flex items-center">
|
||||
<div class="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600">
|
||||
<i class="fas fa-user-plus"></i>
|
||||
</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 class="flex items-center">
|
||||
<div class="w-10 h-10 rounded-full bg-green-100 flex items-center justify-center text-green-600">
|
||||
<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>
|
||||
|
||||
<!-- Quick Stats -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="rounded-full bg-blue-100 p-3">
|
||||
<i class="fas fa-users text-blue-600 text-xl"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<h4 class="text-gray-500 text-sm">Total Pengguna</h4>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ $userCount ?? 0 }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="rounded-full bg-green-100 p-3">
|
||||
<i class="fas fa-check-circle text-green-600 text-xl"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<h4 class="text-gray-500 text-sm">Venue Aktif</h4>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ $activeVenueCount ?? 0 }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="rounded-full bg-purple-100 p-3">
|
||||
<i class="fas fa-table text-purple-600 text-xl"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<h4 class="text-gray-500 text-sm">Total Meja</h4>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ $tableCount ?? 0 }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
|
@ -0,0 +1,142 @@
|
|||
@extends('layouts.super-admin')
|
||||
|
||||
@section('content')
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ __('Tambah Venue Baru') }}</div>
|
||||
|
||||
<div class="card-body">
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('superadmin.venue.store') }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="name"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Nama Venue') }}</label>
|
||||
<div class="col-md-6">
|
||||
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror"
|
||||
name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
|
||||
@error('name')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="address"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Alamat') }}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea id="address" class="form-control @error('address') is-invalid @enderror"
|
||||
name="address" required>{{ old('address') }}</textarea>
|
||||
@error('address')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="phone"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Nomor Telepon') }}</label>
|
||||
<div class="col-md-6">
|
||||
<input id="phone" type="text" class="form-control @error('phone') is-invalid @enderror"
|
||||
name="phone" value="{{ old('phone') }}" required>
|
||||
@error('phone')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="description"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Deskripsi') }}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea id="description"
|
||||
class="form-control @error('description') is-invalid @enderror" name="description"
|
||||
rows="4" required>{{ old('description') }}</textarea>
|
||||
@error('description')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="open_time"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Jam Buka') }}</label>
|
||||
<div class="col-md-6">
|
||||
<input id="open_time" type="time"
|
||||
class="form-control @error('open_time') is-invalid @enderror" name="open_time"
|
||||
value="{{ old('open_time') }}" required>
|
||||
@error('open_time')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="close_time"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Jam Tutup') }}</label>
|
||||
<div class="col-md-6">
|
||||
<input id="close_time" type="time"
|
||||
class="form-control @error('close_time') is-invalid @enderror" name="close_time"
|
||||
value="{{ old('close_time') }}" required>
|
||||
@error('close_time')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="image"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Gambar Venue') }}</label>
|
||||
<div class="col-md-6">
|
||||
<input id="image" type="file" class="form-control @error('image') is-invalid @enderror"
|
||||
name="image" accept="image/*" required>
|
||||
<small class="form-text text-muted">Format: JPG, PNG, GIF. Ukuran maksimal: 2MB</small>
|
||||
@error('image')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-0">
|
||||
<div class="col-md-6 offset-md-4">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{{ __('Simpan') }}
|
||||
</button>
|
||||
<a href="{{ route('superadmin.venue.index') }}" class="btn btn-secondary">
|
||||
{{ __('Batal') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
|
@ -0,0 +1,168 @@
|
|||
@extends('layouts.super-admin')
|
||||
|
||||
@section('content')
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ __('Edit Venue') }}</div>
|
||||
|
||||
<div class="card-body">
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('superadmin.venue.update', $venue->id) }}"
|
||||
enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="name"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Nama Venue') }}</label>
|
||||
<div class="col-md-6">
|
||||
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror"
|
||||
name="name" value="{{ old('name', $venue->name) }}" required autocomplete="name"
|
||||
autofocus>
|
||||
@error('name')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="address"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Alamat') }}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea id="address" class="form-control @error('address') is-invalid @enderror"
|
||||
name="address" required>{{ old('address', $venue->address) }}</textarea>
|
||||
@error('address')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="phone"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Nomor Telepon') }}</label>
|
||||
<div class="col-md-6">
|
||||
<input id="phone" type="text" class="form-control @error('phone') is-invalid @enderror"
|
||||
name="phone" value="{{ old('phone', $venue->phone) }}" required>
|
||||
@error('phone')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="description"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Deskripsi') }}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea id="description"
|
||||
class="form-control @error('description') is-invalid @enderror" name="description"
|
||||
rows="4" required>{{ old('description', $venue->description) }}</textarea>
|
||||
@error('description')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="open_time"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Jam Buka') }}</label>
|
||||
<div class="col-md-6">
|
||||
<input id="open_time" type="time"
|
||||
class="form-control @error('open_time') is-invalid @enderror" name="open_time"
|
||||
value="{{ old('open_time', $venue->open_time_formatted) }}" required>
|
||||
@error('open_time')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="close_time"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Jam Tutup') }}</label>
|
||||
<div class="col-md-6">
|
||||
<input id="close_time" type="time"
|
||||
class="form-control @error('close_time') is-invalid @enderror" name="close_time"
|
||||
value="{{ old('close_time', $venue->close_time_formatted) }}" required>
|
||||
@error('close_time')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="image"
|
||||
class="col-md-4 col-form-label text-md-right">{{ __('Gambar Venue') }}</label>
|
||||
<div class="col-md-6">
|
||||
@if($venue->image)
|
||||
<div class="mb-2">
|
||||
<img src="{{ $venue->image_url }}" alt="{{ $venue->name }}" class="img-thumbnail"
|
||||
style="max-height: 150px;">
|
||||
</div>
|
||||
@endif
|
||||
<input id="image" type="file" class="form-control @error('image') is-invalid @enderror"
|
||||
name="image" accept="image/*">
|
||||
<small class="form-text text-muted">Format: JPG, PNG, GIF. Ukuran maksimal: 2MB. Biarkan
|
||||
kosong jika tidak ingin mengubah gambar.</small>
|
||||
@error('image')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-3">
|
||||
<label for="status" class="col-md-4 col-form-label text-md-right">{{ __('Status') }}</label>
|
||||
<div class="col-md-6">
|
||||
<select id="status" class="form-control @error('status') is-invalid @enderror"
|
||||
name="status" required>
|
||||
<option value="active" {{ old('status', $venue->status) == 'active' ? 'selected' : '' }}>Aktif</option>
|
||||
<option value="inactive" {{ old('status', $venue->status) == 'inactive' ? 'selected' : '' }}>Tidak Aktif</option>
|
||||
</select>
|
||||
@error('status')
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $message }}</strong>
|
||||
</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mb-0">
|
||||
<div class="col-md-6 offset-md-4">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{{ __('Perbarui') }}
|
||||
</button>
|
||||
<a href="{{ route('superadmin.venue.index') }}" class="btn btn-secondary">
|
||||
{{ __('Batal') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
|
@ -0,0 +1,202 @@
|
|||
@extends('layouts.super-admin')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6 flex justify-between items-center">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gray-800">Manajemen Venue</h1>
|
||||
<p class="text-gray-600">Kelola semua venue dalam sistem</p>
|
||||
</div>
|
||||
<a href="{{ route('superadmin.venue.create') }}"
|
||||
class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 flex items-center">
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
Tambah Venue
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Flash Messages -->
|
||||
@if(session('success'))
|
||||
<div class="bg-green-100 border-l-4 border-green-500 text-green-700 p-4 mb-6" role="alert">
|
||||
<p>{{ session('success') }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(session('error'))
|
||||
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6" role="alert">
|
||||
<p>{{ session('error') }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Filter and Search -->
|
||||
<div class="bg-white rounded-lg shadow mb-6 p-4">
|
||||
<form action="{{ route('superadmin.venue.index') }}" method="GET">
|
||||
<div class="flex flex-col md:flex-row gap-4">
|
||||
<div class="flex-grow">
|
||||
<label for="search" class="block text-sm font-medium text-gray-700 mb-1">Cari</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<i class="fas fa-search text-gray-400"></i>
|
||||
</div>
|
||||
<input type="text" id="search" name="search" value="{{ request('search') }}"
|
||||
class="pl-10 block w-full rounded-md border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500"
|
||||
placeholder="Cari nama venue atau lokasi">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="status" class="block text-sm font-medium text-gray-700 mb-1">Status</label>
|
||||
<select id="status" name="status"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500">
|
||||
<option value="">Semua Status</option>
|
||||
<option value="active" {{ request('status') == 'active' ? 'selected' : '' }}>Aktif</option>
|
||||
<option value="inactive" {{ request('status') == 'inactive' ? 'selected' : '' }}>Tidak Aktif</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="self-end">
|
||||
<button type="submit" class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 text-sm">
|
||||
Filter
|
||||
</button>
|
||||
<a href="{{ route('superadmin.venue.index') }}"
|
||||
class="px-4 py-2 border border-gray-300 rounded-md bg-white hover:bg-gray-50 text-sm ml-2">
|
||||
Reset
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Venue Grid -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@forelse($venues as $venue)
|
||||
<div class="bg-white rounded-lg shadow overflow-hidden">
|
||||
<div class="relative">
|
||||
<img src="{{ asset('storage/' . ($venue->image ?? 'images/venue-placeholder.jpg')) }}"
|
||||
alt="{{ $venue->name }}" class="w-full h-48 object-cover">
|
||||
<div class="absolute top-3 right-3 flex gap-2">
|
||||
<a href="{{ route('superadmin.venue.edit', $venue->id) }}"
|
||||
class="p-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<button type="button" onclick="confirmDelete({{ $venue->id }})"
|
||||
class="p-2 bg-red-600 text-white rounded-lg hover:bg-red-700">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-5">
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-2">{{ $venue->name }}</h3>
|
||||
<div class="flex items-center mb-2">
|
||||
<i class="fas fa-map text-gray-500 mr-2"></i>
|
||||
<span class="text-gray-600 truncate">{{ $venue->address }}</span>
|
||||
</div>
|
||||
<div class="flex items-center mb-2">
|
||||
<i class="fas fa-phone text-gray-500 mr-2"></i>
|
||||
<span class="text-gray-600">{{ $venue->phone }}</span>
|
||||
</div>
|
||||
<div class="flex items-center mb-4">
|
||||
<i
|
||||
class="fas fa-check-circle {{ $venue->status == 'active' ? 'text-green-500' : 'text-red-500' }} mr-2"></i>
|
||||
<span class="text-gray-600">{{ $venue->status == 'active' ? 'Aktif' : 'Tidak Aktif' }}</span>
|
||||
</div>
|
||||
<div class="border-t pt-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-sm text-gray-500">
|
||||
<span class="font-medium">{{ $venue->created_at->format('d M Y') }}</span>
|
||||
</div>
|
||||
<a href="{{ route('superadmin.venue.edit', $venue->id) }}"
|
||||
class="text-green-600 hover:text-green-800 flex items-center text-sm">
|
||||
Detail
|
||||
<i class="fas fa-arrow-right ml-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="col-span-3 bg-white rounded-lg shadow p-6 text-center">
|
||||
<i class="fas fa-building text-gray-300 text-5xl mb-4"></i>
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-1">Belum ada venue</h3>
|
||||
<p class="text-gray-500 mb-4">Mulai tambahkan venue baru untuk mengelola bisnis Anda</p>
|
||||
<a href="{{ route('superadmin.venue.create') }}"
|
||||
class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 inline-flex items-center">
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
Tambah Venue
|
||||
</a>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
@if($venues->hasPages())
|
||||
<div class="mt-6">
|
||||
{{ $venues->links() }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Delete Venue Confirmation Modal -->
|
||||
<div id="deleteVenueModal" tabindex="-1" aria-hidden="true"
|
||||
class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-modal md:h-full">
|
||||
<div class="relative w-full h-full max-w-md md:h-auto">
|
||||
<div class="relative bg-white rounded-lg shadow">
|
||||
<div class="flex items-center justify-between p-4 border-b">
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
Konfirmasi Hapus
|
||||
</h3>
|
||||
<button type="button" onclick="closeDeleteModal()"
|
||||
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6 text-center">
|
||||
<i class="fas fa-exclamation-triangle text-5xl text-yellow-400 mb-4"></i>
|
||||
<h3 class="mb-5 text-lg font-normal text-gray-500">Apakah Anda yakin ingin menghapus venue ini? Semua
|
||||
data terkait dengan venue ini akan ikut terhapus.</h3>
|
||||
<form id="deleteVenueForm" method="POST" action="">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit"
|
||||
class="text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center mr-2">
|
||||
Ya, saya yakin
|
||||
</button>
|
||||
<button type="button" onclick="closeDeleteModal()"
|
||||
class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10">
|
||||
Batal
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function confirmDelete(venueId) {
|
||||
const form = document.getElementById('deleteVenueForm');
|
||||
form.action = "{{ route('superadmin.venue.destroy', '') }}/" + venueId;
|
||||
|
||||
// Show modal
|
||||
const modal = document.getElementById('deleteVenueModal');
|
||||
modal.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function closeDeleteModal() {
|
||||
const modal = document.getElementById('deleteVenueModal');
|
||||
modal.classList.add('hidden');
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Escape key closes modal
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape') {
|
||||
closeDeleteModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Click outside modal closes it
|
||||
window.onclick = function (event) {
|
||||
const modal = document.getElementById('deleteVenueModal');
|
||||
if (event.target === modal) {
|
||||
closeDeleteModal();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@endsection
|
|
@ -9,6 +9,9 @@
|
|||
use App\Http\Controllers\admin\TableController;
|
||||
use App\Http\Controllers\admin\AdminController;
|
||||
use App\Http\Controllers\Auth\VerificationController;
|
||||
use App\Http\Controllers\superadmin\SuperAdminController;
|
||||
use App\Http\Controllers\superadmin\AdminManagementController;
|
||||
use App\Http\Controllers\superadmin\VenueManagementController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
@ -74,4 +77,25 @@
|
|||
Route::get('/tables/{id}/edit', [TableController::class, 'edit'])->name('admin.tables.edit');
|
||||
Route::put('/tables/{id}', [TableController::class, 'update'])->name('admin.tables.update');
|
||||
Route::delete('/tables/{id}', [TableController::class, 'destroy'])->name('admin.tables.destroy');
|
||||
});
|
||||
});
|
||||
|
||||
// Superadmin routes
|
||||
Route::middleware(['auth', 'verified', 'is_superadmin'])->prefix('superadmin')->group(function () {
|
||||
Route::get('/', [App\Http\Controllers\superadmin\SuperAdminController::class, 'index'])->name('superadmin.dashboard');
|
||||
|
||||
// Tambahkan route untuk manajemen Admin
|
||||
Route::get('/admin', [AdminManagementController::class, 'index'])->name('superadmin.admin.index');
|
||||
Route::get('/admin/create', [AdminManagementController::class, 'create'])->name('superadmin.admin.create');
|
||||
Route::post('/admin', [AdminManagementController::class, 'store'])->name('superadmin.admin.store');
|
||||
Route::get('/admin/{id}/edit', [AdminManagementController::class, 'edit'])->name('superadmin.admin.edit');
|
||||
Route::put('/admin/{id}', [AdminManagementController::class, 'update'])->name('superadmin.admin.update');
|
||||
Route::delete('/admin/{id}', [AdminManagementController::class, 'destroy'])->name('superadmin.admin.destroy');
|
||||
|
||||
// Tambahkan route untuk manajemen Venue
|
||||
Route::get('/venue', [VenueManagementController::class, 'index'])->name('superadmin.venue.index');
|
||||
Route::get('/venue/create', [VenueManagementController::class, 'create'])->name('superadmin.venue.create');
|
||||
Route::post('/venue', [VenueManagementController::class, 'store'])->name('superadmin.venue.store');
|
||||
Route::get('/venue/{id}/edit', [VenueManagementController::class, 'edit'])->name('superadmin.venue.edit');
|
||||
Route::put('/venue/{id}', [VenueManagementController::class, 'update'])->name('superadmin.venue.update');
|
||||
Route::delete('/venue/{id}', [VenueManagementController::class, 'destroy'])->name('superadmin.venue.destroy');
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue