Tampilan dan fitur halaman admin

This commit is contained in:
Stephen Gesityan 2025-05-09 05:55:09 +07:00
parent d4305a4f61
commit 2e9086a761
13 changed files with 1443 additions and 236 deletions

View File

@ -0,0 +1,111 @@
<?php
namespace App\Exports;
use App\Models\Booking;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
class BookingsExport implements FromCollection, WithHeadings, WithMapping, WithStyles, ShouldAutoSize
{
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function collection()
{
$query = Booking::with(['table', 'user']);
// Apply the same filters as in the controller
if ($this->request->has('search') && !empty($this->request->search)) {
$search = $this->request->search;
$query->where(function ($q) use ($search) {
$q->whereHas('user', function ($query) use ($search) {
$query->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
})->orWhereHas('table', function ($query) use ($search) {
$query->where('name', 'like', "%{$search}%");
});
});
}
if ($this->request->has('status') && !empty($this->request->status)) {
$query->where('status', $this->request->status);
}
if ($this->request->has('date_from') && !empty($this->request->date_from)) {
$dateFrom = Carbon::parse($this->request->date_from)->startOfDay();
$query->where('start_time', '>=', $dateFrom);
}
if ($this->request->has('date_to') && !empty($this->request->date_to)) {
$dateTo = Carbon::parse($this->request->date_to)->endOfDay();
$query->where('start_time', '<=', $dateTo);
}
// Sort by start time by default
$sortColumn = $this->request->sort ?? 'start_time';
$sortDirection = $this->request->direction ?? 'desc';
return $query->orderBy($sortColumn, $sortDirection)->get();
}
public function headings(): array
{
return [
'ID',
'User',
'Email',
'Meja',
'Kapasitas',
'Mulai',
'Selesai',
'Durasi (jam)',
'Status',
'Dibuat Pada',
];
}
public function map($booking): array
{
$startTime = Carbon::parse($booking->start_time);
$endTime = Carbon::parse($booking->end_time);
$duration = $startTime->diffInHours($endTime);
return [
$booking->id,
$booking->user->name,
$booking->user->email,
$booking->table->name,
$booking->table->capacity . ' orang',
$startTime->format('d/m/Y H:i'),
$endTime->format('d/m/Y H:i'),
$duration,
ucfirst($booking->status),
Carbon::parse($booking->created_at)->format('d/m/Y H:i'),
];
}
public function styles(Worksheet $sheet)
{
return [
// Style the first row (headers)
1 => [
'font' => ['bold' => true],
'fill' => [
'fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID,
'startColor' => ['argb' => 'FFE0E0E0'],
],
],
];
}
}

View File

@ -15,16 +15,49 @@ public function index()
{
$venue = Venue::find(auth()->user()->venue_id);
// Menghitung booking hari ini
$todayBookings = Booking::whereDate('created_at', now())
->whereHas('table', function ($query) use ($venue) {
$query->where('venue_id', $venue->id);
})
->count();
// Kalkulasi status meja
$totalTables = Table::where('venue_id', $venue->id)->count();
$usedTables = Table::where('venue_id', $venue->id)->where('status', 'booked')->count();
$availableTables = Table::where('venue_id', $venue->id)->where('status', 'available')->count();
// Menghitung pendapatan hari ini
$todayRevenue = Booking::whereDate('created_at', now())
->whereHas('table', function ($query) use ($venue) {
$query->where('venue_id', $venue->id);
})
->where('status', 'paid')
->sum('total_amount');
// Menghitung pendapatan bulan ini
$monthlyRevenue = Booking::whereMonth('created_at', now()->month)
->whereYear('created_at', now()->year)
->whereHas('table', function ($query) use ($venue) {
$query->where('venue_id', $venue->id);
})
->where('status', 'paid')
->sum('total_amount');
// Menghitung jumlah booking berdasarkan status
$pendingBookings = Booking::whereHas('table', function ($query) use ($venue) {
$query->where('venue_id', $venue->id);
})
->where('status', 'pending')
->count();
$paidBookings = Booking::whereHas('table', function ($query) use ($venue) {
$query->where('venue_id', $venue->id);
})
->where('status', 'paid')
->count();
// Ambil booking terbaru
$recentBookings = Booking::whereHas('table', function ($query) use ($venue) {
$query->where('venue_id', $venue->id);
})
@ -33,13 +66,33 @@ public function index()
->with(['user', 'table'])
->get();
// Menghitung data analitik untuk diagram
$lastWeekBookings = [];
for ($i = 6; $i >= 0; $i--) {
$date = now()->subDays($i);
$count = Booking::whereDate('created_at', $date)
->whereHas('table', function ($query) use ($venue) {
$query->where('venue_id', $venue->id);
})
->count();
$lastWeekBookings[] = [
'date' => $date->format('d/m'),
'count' => $count
];
}
return view('admin.dashboard', compact(
'venue',
'todayBookings',
'totalTables',
'usedTables',
'availableTables',
'recentBookings'
'recentBookings',
'todayRevenue',
'monthlyRevenue',
'pendingBookings',
'paidBookings',
'lastWeekBookings'
));
}
}

View File

@ -1,20 +1,124 @@
<?php
namespace App\Http\Controllers\admin;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Booking;
use App\Models\Table;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\BookingsExport;
class BookingsController extends Controller
{
public function index()
public function index(Request $request)
{
$bookings = Booking::with(['table', 'user'])
->orderBy('start_time', 'desc')
->paginate(10);
$query = Booking::with(['table', 'user']);
// Search functionality
if ($request->has('search') && !empty($request->search)) {
$search = $request->search;
$query->where(function ($q) use ($search) {
$q->whereHas('user', function ($query) use ($search) {
$query->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
})->orWhereHas('table', function ($query) use ($search) {
$query->where('name', 'like', "%{$search}%");
});
});
}
// Status filter
if ($request->has('status') && !empty($request->status)) {
$query->where('status', $request->status);
}
// Date range filter
if ($request->has('date_from') && !empty($request->date_from)) {
$dateFrom = Carbon::parse($request->date_from)->startOfDay();
$query->where('start_time', '>=', $dateFrom);
}
if ($request->has('date_to') && !empty($request->date_to)) {
$dateTo = Carbon::parse($request->date_to)->endOfDay();
$query->where('start_time', '<=', $dateTo);
}
// Sorting
$sortColumn = $request->sort ?? 'start_time';
$sortDirection = $request->direction ?? 'desc';
// Handle related column sorting
if ($sortColumn === 'user') {
$query->join('users', 'bookings.user_id', '=', 'users.id')
->select('bookings.*')
->orderBy('users.name', $sortDirection);
} elseif ($sortColumn === 'table') {
$query->join('tables', 'bookings.table_id', '=', 'tables.id')
->select('bookings.*')
->orderBy('tables.name', $sortDirection);
} else {
$query->orderBy($sortColumn, $sortDirection);
}
$bookings = $query->paginate(10)->withQueryString();
return view('admin.bookings.index', compact('bookings'));
}
public function show($id)
{
$booking = Booking::with(['table', 'user'])->findOrFail($id);
return view('admin.bookings.show', compact('booking'));
}
public function edit($id)
{
$booking = Booking::findOrFail($id);
$tables = Table::all();
return view('admin.bookings.edit', compact('booking', 'tables'));
}
public function update(Request $request, $id)
{
$request->validate([
'table_id' => 'required|exists:tables,id',
'start_time' => 'required|date',
'end_time' => 'required|date|after:start_time',
]);
$booking = Booking::findOrFail($id);
$booking->update($request->all());
return redirect()->route('admin.bookings.index')
->with('success', 'Booking berhasil diperbarui');
}
public function complete($id)
{
$booking = Booking::findOrFail($id);
$booking->status = 'selesai';
$booking->save();
return redirect()->route('admin.bookings.index')
->with('success', 'Booking berhasil diselesaikan');
}
public function cancel($id)
{
$booking = Booking::findOrFail($id);
$booking->status = 'cancelled';
$booking->save();
return redirect()->route('admin.bookings.index')
->with('success', 'Booking berhasil dibatalkan');
}
public function export(Request $request)
{
$filename = 'bookings-' . Carbon::now()->format('Y-m-d') . '.xlsx';
return Excel::download(new BookingsExport($request), $filename);
}
}

View File

@ -5,24 +5,110 @@
use App\Http\Controllers\Controller;
use App\Models\Table;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class TableController extends Controller
{
public function kelolaMeja()
/**
* Display a listing of the tables.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$tables = Table::where('venue_id', auth()->user()->venue_id)->paginate(10);
$query = Table::query()->where('venue_id', auth()->user()->venue_id);
// Search by name
if ($request->has('search') && $request->search != '') {
$query->where('name', 'like', '%' . $request->search . '%');
}
// Filter by status
if ($request->has('status') && $request->status != '') {
$query->where('status', $request->status);
}
$tables = $query->latest()->paginate(10);
return view('admin.tables.index', compact('tables'));
}
public function editTable($id)
/**
* Show the form for creating a new table.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$table = Table::findOrFail($id);
return view('admin.tables.create');
}
/**
* Store a newly created table in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'brand' => 'required|string|max:255',
'status' => 'required|in:Available,Booked,Unavailable',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
Table::create([
'name' => $request->name,
'brand' => $request->brand,
'status' => $request->status,
'venue_id' => auth()->user()->venue_id,
]);
return redirect()->route('admin.tables.index')
->with('success', 'Meja baru berhasil ditambahkan.');
}
/**
* Show the form for editing the specified table.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$table = Table::where('venue_id', auth()->user()->venue_id)->findOrFail($id);
return view('admin.tables.edit', compact('table'));
}
public function updateTable(Request $request, $id)
/**
* Update the specified table in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$table = Table::findOrFail($id);
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'brand' => 'required|string|max:255',
'status' => 'required|in:Available,Booked,Unavailable',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$table = Table::where('venue_id', auth()->user()->venue_id)->findOrFail($id);
$table->update([
'name' => $request->name,
@ -30,7 +116,22 @@ public function updateTable(Request $request, $id)
'status' => $request->status,
]);
return redirect()->route('admin.tables.index')->with('success', 'Data meja berhasil diperbarui.');
return redirect()->route('admin.tables.index')
->with('success', 'Data meja berhasil diperbarui.');
}
/**
* Remove the specified table from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$table = Table::where('venue_id', auth()->user()->venue_id)->findOrFail($id);
$table->delete();
return redirect()->route('admin.tables.index')
->with('success', 'Meja berhasil dihapus.');
}
}

View File

@ -12,6 +12,7 @@
"laravel/sanctum": "^3.3",
"laravel/tinker": "^2.8",
"laravel/ui": "^4.6",
"maatwebsite/excel": "^1.1",
"midtrans/midtrans-php": "^2.6"
},
"require-dev": {

123
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "edf30558ed2abcf5bae02cf31ec6ee71",
"content-hash": "bce8ddce84cf8a2d572b421f6992903e",
"packages": [
{
"name": "brick/math",
@ -2290,6 +2290,65 @@
],
"time": "2024-09-21T08:32:55+00:00"
},
{
"name": "maatwebsite/excel",
"version": "v1.1.5",
"source": {
"type": "git",
"url": "https://github.com/Maatwebsite/Laravel-Excel.git",
"reference": "0c67aba8387726458d42461eae91a3415593bbc4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Maatwebsite/Laravel-Excel/zipball/0c67aba8387726458d42461eae91a3415593bbc4",
"reference": "0c67aba8387726458d42461eae91a3415593bbc4",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"phpoffice/phpexcel": "~1.8.0"
},
"require-dev": {
"mockery/mockery": "~0.9",
"orchestra/testbench": "~2.2.0@dev",
"phpunit/phpunit": "~4.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Maatwebsite\\Excel\\": "src/"
},
"classmap": [
"src/Maatwebsite/Excel",
"tests/TestCase.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL"
],
"authors": [
{
"name": "Maatwebsite.nl",
"email": "patrick@maatwebsite.nl"
}
],
"description": "An eloquent way of importing and exporting Excel and CSV in Laravel 4 with the power of PHPExcel",
"keywords": [
"PHPExcel",
"batch",
"csv",
"excel",
"export",
"import",
"laravel"
],
"support": {
"issues": "https://github.com/Maatwebsite/Laravel-Excel/issues",
"source": "https://github.com/Maatwebsite/Laravel-Excel/tree/master"
},
"time": "2014-07-10T09:06:07+00:00"
},
{
"name": "midtrans/midtrans-php",
"version": "2.6.2",
@ -2851,6 +2910,68 @@
],
"time": "2023-02-08T01:06:31+00:00"
},
{
"name": "phpoffice/phpexcel",
"version": "1.8.1",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PHPExcel.git",
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
"shasum": ""
},
"require": {
"ext-xml": "*",
"ext-xmlwriter": "*",
"php": ">=5.2.0"
},
"type": "library",
"autoload": {
"psr-0": {
"PHPExcel": "Classes/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL"
],
"authors": [
{
"name": "Maarten Balliauw",
"homepage": "http://blog.maartenballiauw.be"
},
{
"name": "Mark Baker"
},
{
"name": "Franck Lefevre",
"homepage": "http://blog.rootslabs.net"
},
{
"name": "Erik Tilt"
}
],
"description": "PHPExcel - OpenXML - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
"homepage": "http://phpexcel.codeplex.com",
"keywords": [
"OpenXML",
"excel",
"php",
"spreadsheet",
"xls",
"xlsx"
],
"support": {
"issues": "https://github.com/PHPOffice/PHPExcel/issues",
"source": "https://github.com/PHPOffice/PHPExcel/tree/master"
},
"abandoned": "phpoffice/phpspreadsheet",
"time": "2015-05-01T07:00:55+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.9.3",

View File

@ -1,42 +1,298 @@
@extends('layouts.admin')
@section('content')
<div class="p-4">
<h1 class="text-2xl font-bold mb-4">Daftar Booking</h1>
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<div class="flex flex-col md:flex-row justify-between items-center mb-6">
<h1 class="text-3xl font-bold text-gray-800 mb-4 md:mb-0">
<i class="fas fa-calendar-check mr-2"></i>Daftar Booking
</h1>
<div class="overflow-x-auto bg-white rounded shadow">
<table class="min-w-full table-auto">
<thead class="bg-gray-100">
<tr>
<th class="px-4 py-2 text-left">User</th>
<th class="px-4 py-2 text-left">Meja</th>
<th class="px-4 py-2 text-left">Mulai</th>
<th class="px-4 py-2 text-left">Selesai</th>
<th class="px-4 py-2 text-left">Status</th>
</tr>
</thead>
<tbody>
@forelse ($bookings as $booking)
<tr class="border-b">
<td class="px-4 py-2">{{ $booking->user->name }}</td>
<td class="px-4 py-2">{{ $booking->table->name }}</td>
<td class="px-4 py-2">{{ \Carbon\Carbon::parse($booking->start_time)->format('H:i d/m') }}</td>
<td class="px-4 py-2">{{ \Carbon\Carbon::parse($booking->end_time)->format('H:i d/m') }}</td>
<td class="px-4 py-2">
<span
class="text-sm px-2 py-1 rounded
{{ $booking->status === 'booked' ? 'bg-blue-200 text-blue-800' : ($booking->status === 'selesai' ? 'bg-green-200 text-green-800' : 'bg-red-200 text-red-800') }}">
{{ ucfirst($booking->status) }}
</span>
</td>
</tr>
@empty
<div class="flex flex-col md:flex-row gap-3 w-full md:w-auto">
<a href="{{ route('admin.dashboard') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left mr-1"></i> Kembali
</a>
<a href="{{ route('admin.bookings.export') }}" class="btn btn-success">
<i class="fas fa-file-excel mr-1"></i> Export Excel
</a>
</div>
</div>
<!-- Search and Filter Card -->
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
<form action="{{ route('admin.bookings.index') }}" method="GET" class="space-y-4">
<div class="flex flex-col md:flex-row gap-4">
<div class="w-full md:w-1/4">
<label for="search" class="block text-sm font-medium text-gray-700 mb-1">Cari</label>
<div class="relative">
<input type="text" name="search" id="search" placeholder="Cari user atau meja..."
class="form-input w-full rounded-md border-gray-300 focus:border-blue-500 focus:ring focus:ring-blue-200"
value="{{ request('search') }}">
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<i class="fas fa-search text-gray-400"></i>
</div>
</div>
</div>
<div class="w-full md:w-1/4">
<label for="status" class="block text-sm font-medium text-gray-700 mb-1">Status</label>
<select name="status" id="status" class="form-select w-full rounded-md border-gray-300 focus:border-blue-500 focus:ring focus:ring-blue-200">
<option value="">Semua Status</option>
<option value="booked" {{ request('status') == 'booked' ? 'selected' : '' }}>Booked</option>
<option value="selesai" {{ request('status') == 'selesai' ? 'selected' : '' }}>Selesai</option>
<option value="cancelled" {{ request('status') == 'cancelled' ? 'selected' : '' }}>Dibatalkan</option>
</select>
</div>
<div class="w-full md:w-1/4">
<label for="date_from" class="block text-sm font-medium text-gray-700 mb-1">Dari Tanggal</label>
<input type="date" name="date_from" id="date_from"
class="form-input w-full rounded-md border-gray-300 focus:border-blue-500 focus:ring focus:ring-blue-200"
value="{{ request('date_from') }}">
</div>
<div class="w-full md:w-1/4">
<label for="date_to" class="block text-sm font-medium text-gray-700 mb-1">Sampai Tanggal</label>
<input type="date" name="date_to" id="date_to"
class="form-input w-full rounded-md border-gray-300 focus:border-blue-500 focus:ring focus:ring-blue-200"
value="{{ request('date_to') }}">
</div>
</div>
<div class="flex justify-end gap-2">
<a href="{{ route('admin.bookings.index') }}" class="btn btn-secondary">
<i class="fas fa-redo mr-1"></i> Reset
</a>
<button type="submit" class="btn btn-primary">
<i class="fas fa-filter mr-1"></i> Filter
</button>
</div>
</form>
</div>
<!-- Bookings Table Card -->
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-4 border-b border-gray-200 bg-gray-50 flex justify-between items-center">
<h2 class="font-semibold text-lg text-gray-700">Data Booking</h2>
<div class="text-sm text-gray-500">
Total: <span class="font-semibold">{{ $bookings->total() }}</span> data
</div>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<td colspan="6" class="px-4 py-2 text-center text-gray-500">Belum ada data booking.</td>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a href="{{ route('admin.bookings.index', array_merge(request()->all(), ['sort' => 'user', 'direction' => request('direction') == 'asc' && request('sort') == 'user' ? 'desc' : 'asc'])) }}" class="flex items-center">
User
@if(request('sort') == 'user')
<i class="fas fa-sort-{{ request('direction') == 'asc' ? 'up' : 'down' }} ml-1"></i>
@else
<i class="fas fa-sort ml-1 opacity-50"></i>
@endif
</a>
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a href="{{ route('admin.bookings.index', array_merge(request()->all(), ['sort' => 'table', 'direction' => request('direction') == 'asc' && request('sort') == 'table' ? 'desc' : 'asc'])) }}" class="flex items-center">
Meja
@if(request('sort') == 'table')
<i class="fas fa-sort-{{ request('direction') == 'asc' ? 'up' : 'down' }} ml-1"></i>
@else
<i class="fas fa-sort ml-1 opacity-50"></i>
@endif
</a>
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a href="{{ route('admin.bookings.index', array_merge(request()->all(), ['sort' => 'start_time', 'direction' => request('direction') == 'asc' && request('sort') == 'start_time' ? 'desc' : 'asc'])) }}" class="flex items-center">
Mulai
@if(request('sort') == 'start_time')
<i class="fas fa-sort-{{ request('direction') == 'asc' ? 'up' : 'down' }} ml-1"></i>
@else
<i class="fas fa-sort ml-1 opacity-50"></i>
@endif
</a>
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a href="{{ route('admin.bookings.index', array_merge(request()->all(), ['sort' => 'end_time', 'direction' => request('direction') == 'asc' && request('sort') == 'end_time' ? 'desc' : 'asc'])) }}" class="flex items-center">
Selesai
@if(request('sort') == 'end_time')
<i class="fas fa-sort-{{ request('direction') == 'asc' ? 'up' : 'down' }} ml-1"></i>
@else
<i class="fas fa-sort ml-1 opacity-50"></i>
@endif
</a>
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a href="{{ route('admin.bookings.index', array_merge(request()->all(), ['sort' => 'status', 'direction' => request('direction') == 'asc' && request('sort') == 'status' ? 'desc' : 'asc'])) }}" class="flex items-center">
Status
@if(request('sort') == 'status')
<i class="fas fa-sort-{{ request('direction') == 'asc' ? 'up' : 'down' }} ml-1"></i>
@else
<i class="fas fa-sort ml-1 opacity-50"></i>
@endif
</a>
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Aksi
</th>
</tr>
@endforelse
</tbody>
</table>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse ($bookings as $booking)
<tr class="hover:bg-gray-50 transition">
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="flex-shrink-0 h-10 w-10">
<img class="h-10 w-10 rounded-full" src="https://ui-avatars.com/api/?name={{ urlencode($booking->user->name) }}&color=7F9CF5&background=EBF4FF" alt="">
</div>
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">{{ $booking->user->name }}</div>
<div class="text-sm text-gray-500">{{ $booking->user->email }}</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">{{ $booking->table->name }}</div>
<div class="text-sm text-gray-500">Kapasitas: {{ $booking->table->capacity }} orang</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">{{ \Carbon\Carbon::parse($booking->start_time)->format('H:i') }}</div>
<div class="text-sm text-gray-500">{{ \Carbon\Carbon::parse($booking->start_time)->format('d M Y') }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">{{ \Carbon\Carbon::parse($booking->end_time)->format('H:i') }}</div>
<div class="text-sm text-gray-500">{{ \Carbon\Carbon::parse($booking->end_time)->format('d M Y') }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
@if($booking->status === 'booked')
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800">
<i class="fas fa-clock mr-1"></i> Booked
</span>
@elseif($booking->status === 'selesai')
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
<i class="fas fa-check mr-1"></i> Selesai
</span>
@else
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
<i class="fas fa-times mr-1"></i> Dibatalkan
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div class="flex space-x-2">
<a href="{{ route('admin.bookings.show', $booking->id) }}" class="text-blue-600 hover:text-blue-900" title="Detail">
<i class="fas fa-eye"></i>
</a>
@if($booking->status === 'booked')
<a href="{{ route('admin.bookings.edit', $booking->id) }}" class="text-yellow-600 hover:text-yellow-900" title="Edit">
<i class="fas fa-edit"></i>
</a>
<form action="{{ route('admin.bookings.complete', $booking->id) }}" method="POST" class="inline">
@csrf
@method('PATCH')
<button type="submit" class="text-green-600 hover:text-green-900" title="Selesai" onclick="return confirm('Apakah anda yakin menyelesaikan booking ini?')">
<i class="fas fa-check-circle"></i>
</button>
</form>
<form action="{{ route('admin.bookings.cancel', $booking->id) }}" method="POST" class="inline">
@csrf
@method('PATCH')
<button type="submit" class="text-red-600 hover:text-red-900" title="Batalkan" onclick="return confirm('Apakah anda yakin membatalkan booking ini?')">
<i class="fas fa-ban"></i>
</button>
</form>
@endif
</div>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-6 py-10 text-center text-gray-500">
<div class="flex flex-col items-center justify-center">
<i class="fas fa-calendar-times text-4xl text-gray-300 mb-3"></i>
<p class="text-lg font-medium">Belum ada data booking</p>
<p class="text-sm">Coba ubah filter atau tambahkan booking baru</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="px-4 py-3 bg-gray-50 border-t border-gray-200">
<div class="flex items-center justify-between">
<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>
</div>
</div>
</div>
</div>
@push('styles')
<style>
.btn {
@apply inline-flex items-center px-4 py-2 border border-transparent rounded-md font-semibold text-xs uppercase tracking-wider transition focus:outline-none focus:ring-2 focus:ring-offset-2;
}
.btn-primary {
@apply bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500;
}
.btn-secondary {
@apply bg-gray-200 text-gray-700 hover:bg-gray-300 focus:ring-gray-500;
}
.btn-success {
@apply bg-green-600 text-white hover:bg-green-700 focus:ring-green-500;
}
.pagination {
@apply flex rounded-md;
}
.page-item {
@apply -ml-px;
}
.page-link {
@apply relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50;
}
.page-item.active .page-link {
@apply z-10 bg-blue-50 border-blue-500 text-blue-600;
}
.page-item.disabled .page-link {
@apply bg-gray-50 text-gray-500 cursor-not-allowed;
}
</style>
@endpush
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Date range validation
const dateFrom = document.getElementById('date_from');
const dateTo = document.getElementById('date_to');
if (dateFrom && dateTo) {
dateFrom.addEventListener('change', function() {
dateTo.min = dateFrom.value;
});
dateTo.addEventListener('change', function() {
dateFrom.max = dateTo.value;
});
}
});
</script>
@endpush
@endsection

View File

@ -1,57 +1,211 @@
@extends('layouts.admin')
@section('content')
<div class="p-4">
<h1 class="text-2xl font-bold mb-4">Admin {{ $venue->name }}</h1>
<p>Selamat datang, {{ auth()->user()->name }}!</p>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 my-6">
<a href="#" class="bg-blue-500 text-white p-4 rounded-lg">
<p class="text-sm">Jumlah Booking Hari Ini</p>
<p class="text-2xl font-bold">{{ $todayBookings }}</p>
</a>
<a href="#" class="bg-gray-600 text-white p-4 rounded-lg">
<p class="text-sm">Total Meja</p>
<p class="text-2xl font-bold">{{ $totalTables }}</p>
</a>
<a href="#" class="bg-red-600 text-white p-4 rounded-lg">
<p class="text-sm">Meja Sedang Digunakan</p>
<p class="text-2xl font-bold">{{ $usedTables }}</p>
</a>
<a href="#" class="bg-green-600 text-white p-4 rounded-lg">
<p class="text-sm">Meja Tersedia</p>
<p class="text-2xl font-bold">{{ $availableTables }}</p>
</a>
</div>
<h2 class="font-semibold text-lg mt-8 mb-2">Booking Terbaru</h2>
@if($recentBookings->isEmpty())
<p class="text-gray-500">Belum ada booking terbaru.</p>
@else
<div class="bg-white rounded shadow overflow-x-auto">
<table class="w-full table-auto">
<thead class="bg-gray-100">
<tr>
<th class="px-4 py-2 text-left">Nama User</th>
<th class="px-4 py-2 text-left">Meja</th>
<th class="px-4 py-2 text-left">Waktu</th>
<th class="px-4 py-2 text-left">Status</th>
</tr>
</thead>
<tbody>
@foreach($recentBookings as $booking)
<tr class="border-t">
<td class="px-4 py-2">{{ $booking->user->name }}</td>
<td class="px-4 py-2">{{ $booking->table->name }}</td>
<td class="px-4 py-2">{{ \Carbon\Carbon::parse($booking->start_time)->format('H:i') }} -
{{ \Carbon\Carbon::parse($booking->end_time)->format('H:i') }}</td>
<td class="px-4 py-2 capitalize">{{ $booking->status }}</td>
</tr>
@endforeach
</tbody>
</table>
<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>
<h1 class="text-3xl font-bold text-gray-800">Dashboard {{ $venue->name }}</h1>
<p class="text-gray-600 mt-1">Selamat datang, {{ auth()->user()->name }}!</p>
</div>
<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>
@endif
<!-- Stats Cards - Row 1 -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6">
<!-- Today's Revenue -->
<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">Pendapatan Hari Ini</p>
<p class="text-2xl font-bold text-gray-800">Rp{{ number_format($todayRevenue, 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="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>
</div>
</div>
<p class="text-xs text-gray-500 mt-2">Pendapatan Bulan Ini: Rp{{ number_format($monthlyRevenue, 0, ',', '.') }}</p>
</div>
<!-- Today's Bookings -->
<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">Booking Hari Ini</p>
<p class="text-2xl font-bold text-gray-800">{{ $todayBookings }}</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="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>
</div>
</div>
<div class="flex mt-2 space-x-4">
<p class="text-xs text-gray-500">Pending: <span class="font-semibold text-amber-500">{{ $pendingBookings }}</span></p>
<p class="text-xs text-gray-500">Paid: <span class="font-semibold text-green-500">{{ $paidBookings }}</span></p>
</div>
</div>
<!-- Total Tables -->
<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">{{ $totalTables }}</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="M4 6h16M4 12h16M4 18h7" />
</svg>
</div>
</div>
<div class="flex mt-2 space-x-4">
<p class="text-xs text-gray-500">Tersedia: <span class="font-semibold text-green-500">{{ $availableTables }}</span></p>
<p class="text-xs text-gray-500">Digunakan: <span class="font-semibold text-red-500">{{ $usedTables }}</span></p>
</div>
</div>
<!-- Table Usage -->
<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">Penggunaan Meja</p>
<p class="text-2xl font-bold text-gray-800">{{ $totalTables > 0 ? round(($usedTables / $totalTables) * 100) : 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="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>
</div>
</div>
<div class="w-full bg-gray-200 rounded-full h-2.5 mt-3">
<div class="bg-amber-500 h-2.5 rounded-full" style="width: {{ $totalTables > 0 ? ($usedTables / $totalTables) * 100 : 0 }}%"></div>
</div>
</div>
</div>
<!-- Charts and Recent Bookings -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Weekly Bookings Chart -->
<div class="lg:col-span-2 bg-white rounded-xl shadow-sm p-6">
<h2 class="font-semibold text-lg mb-4">Booking Per Hari (7 Hari Terakhir)</h2>
<div class="h-80" id="bookingsChart"></div>
</div>
<!-- Recent Bookings -->
<div class="bg-white rounded-xl shadow-sm p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="font-semibold text-lg">Booking Terbaru</h2>
<a href="{{ route('admin.bookings.index') }}" class="text-sm text-blue-600 hover:underline">Lihat Semua</a>
</div>
@if($recentBookings->isEmpty())
<p class="text-gray-500 text-center py-4">Belum ada booking terbaru.</p>
@else
<div class="space-y-4">
@foreach($recentBookings as $booking)
<div class="flex items-center p-3 border border-gray-100 rounded-lg hover:bg-gray-50 transition">
<div class="p-2 mr-3 bg-gray-100 rounded-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
</div>
<div class="flex-1">
<p class="font-medium text-gray-800">{{ $booking->user->name }}</p>
<div class="flex items-center text-sm text-gray-500">
<span class="mr-2">{{ $booking->table->name }}</span>
<span class="text-xs px-2 py-0.5 rounded-full {{
$booking->status === 'paid' ? 'bg-green-100 text-green-800' :
($booking->status === 'pending' ? 'bg-amber-100 text-amber-800' :
'bg-gray-100 text-gray-800')
}}">
{{ ucfirst($booking->status) }}
</span>
</div>
</div>
<div class="text-right text-sm text-gray-500">
<p>{{ \Carbon\Carbon::parse($booking->start_time)->format('H:i') }}</p>
<p>{{ \Carbon\Carbon::parse($booking->start_time)->format('d/m') }}</p>
</div>
</div>
@endforeach
</div>
@endif
</div>
</div>
</div>
</div>
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Booking Chart
var bookingData = @json($lastWeekBookings);
var options = {
series: [{
name: 'Booking',
data: bookingData.map(item => item.count)
}],
chart: {
type: 'bar',
height: 300,
toolbar: {
show: false
}
},
plotOptions: {
bar: {
borderRadius: 4,
columnWidth: '60%',
}
},
colors: ['#3b82f6'],
dataLabels: {
enabled: false
},
xaxis: {
categories: bookingData.map(item => item.date),
axisBorder: {
show: false
}
},
yaxis: {
title: {
text: 'Jumlah Booking'
},
labels: {
formatter: function (val) {
return Math.floor(val);
}
}
},
fill: {
opacity: 1
},
tooltip: {
y: {
formatter: function (val) {
return val + " booking";
}
}
},
grid: {
borderColor: '#f3f4f6',
strokeDashArray: 5
}
};
var chart = new ApexCharts(document.querySelector("#bookingsChart"), options);
chart.render();
});
</script>
@endpush
@endsection

View File

@ -0,0 +1,70 @@
@extends('layouts.admin')
@section('content')
<div class="p-6 bg-gray-50">
<div class="flex items-center mb-6">
<a href="{{ route('admin.tables.index') }}" class="text-blue-600 hover:text-blue-800 mr-4">
<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="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
</a>
<h1 class="text-3xl font-bold text-gray-800">Tambah Meja Baru</h1>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<form action="{{ route('admin.tables.store') }}" method="POST" class="space-y-6">
@csrf
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="name" class="block text-sm font-medium text-gray-700 mb-1">Nama Meja</label>
<input type="text" id="name" name="name" value="{{ old('name') }}"
placeholder="Masukkan nama meja"
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 @error('name') border-red-500 @enderror">
@error('name')
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
@enderror
</div>
<div>
<label for="brand" class="block text-sm font-medium text-gray-700 mb-1">Merek</label>
<input type="text" id="brand" name="brand" value="{{ old('brand') }}"
placeholder="Masukkan merek meja"
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 @error('brand') border-red-500 @enderror">
@error('brand')
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
@enderror
</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="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 @error('status') border-red-500 @enderror">
<option value="Available" {{ old('status') === 'Available' ? 'selected' : '' }}>Available</option>
<option value="Booked" {{ old('status') === 'Booked' ? 'selected' : '' }}>Booked</option>
<option value="Unavailable" {{ old('status') === 'Unavailable' ? 'selected' : '' }}>Unavailable
</option>
</select>
@error('status')
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
@enderror
</div>
<div class="flex justify-end space-x-3">
<a href="{{ route('admin.tables.index') }}"
class="bg-gray-300 text-gray-700 px-6 py-2 rounded-lg hover:bg-gray-400 transition-colors duration-300">
Batal
</a>
<button type="submit"
class="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors duration-300">
Simpan Meja
</button>
</div>
</form>
</div>
</div>
</div>
@endsection

View File

@ -1,34 +1,70 @@
@extends('layouts.admin')
@section('content')
<div class="p-4">
<h1 class="text-2xl font-bold mb-4">Edit Meja: {{ $table->name }}</h1>
<div class="p-6 bg-gray-50">
<div class="flex items-center mb-6">
<a href="{{ route('admin.tables.index') }}" class="text-blue-600 hover:text-blue-800 mr-4">
<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="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
</a>
<h1 class="text-3xl font-bold text-gray-800">Edit Meja: {{ $table->name }}</h1>
</div>
<form action="{{ route('admin.tables.update', $table->id) }}" method="POST" class="space-y-4">
@csrf
@method('PUT')
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<form action="{{ route('admin.tables.update', $table->id) }}" method="POST" class="space-y-6">
@csrf
@method('PUT')
<div>
<label class="block text-sm font-medium text-gray-700">Nama Meja</label>
<input type="text" name="name" value="{{ $table->name }}" class="w-full border border-gray-300 p-2 rounded">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="name" class="block text-sm font-medium text-gray-700 mb-1">Nama Meja</label>
<input type="text" id="name" name="name" value="{{ old('name', $table->name) }}"
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 @error('name') border-red-500 @enderror">
@error('name')
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
@enderror
</div>
<div>
<label for="brand" class="block text-sm font-medium text-gray-700 mb-1">Merek</label>
<input type="text" id="brand" name="brand" value="{{ old('brand', $table->brand) }}"
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 @error('brand') border-red-500 @enderror">
@error('brand')
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
@enderror
</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="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 @error('status') border-red-500 @enderror">
<option value="Available" {{ old('status', $table->status) === 'Available' ? 'selected' : '' }}>
Available</option>
<option value="Booked" {{ old('status', $table->status) === 'Booked' ? 'selected' : '' }}>Booked
</option>
<option value="Unavailable" {{ old('status', $table->status) === 'Unavailable' ? 'selected' : '' }}>Unavailable</option>
</select>
@error('status')
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
@enderror
</div>
<div class="flex justify-end space-x-3">
<a href="{{ route('admin.tables.index') }}"
class="bg-gray-300 text-gray-700 px-6 py-2 rounded-lg hover:bg-gray-400 transition-colors duration-300">
Batal
</a>
<button type="submit"
class="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors duration-300">
Simpan Perubahan
</button>
</div>
</form>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Merek</label>
<input type="text" name="brand" value="{{ $table->brand }}"
class="w-full border border-gray-300 p-2 rounded">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Status</label>
<select name="status" class="w-full border border-gray-300 p-2 rounded">
<option value="Available" {{ $table->status === 'Available' ? 'selected' : '' }}>Available</option>
<option value="Booked" {{ $table->status === 'Booked' ? 'selected' : '' }}>Booked</option>
<option value="Unavailable" {{ $table->status === 'Unavailable' ? 'selected' : '' }}>Unavailable</option>
</select>
</div>
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded">Simpan</button>
</form>
</div>
</div>
@endsection

View File

@ -1,48 +1,125 @@
@extends('layouts.admin')
@section('content')
<div class="p-4">
<h1 class="text-2xl font-bold mb-4">Kelola Meja</h1>
<div class="overflow-x-auto bg-white shadow rounded-lg">
<table class="min-w-full divide-y divide-gray-200 text-sm">
<thead class="bg-gray-100">
<tr>
<th class="px-4 py-2 text-left font-semibold text-gray-600">Nama Meja</th>
<th class="px-4 py-2 text-left font-semibold text-gray-600">Merek</th>
<th class="px-4 py-2 text-left font-semibold text-gray-600">Status</th>
<th class="px-4 py-2 text-left font-semibold text-gray-600">Aksi</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse ($tables as $table)
<tr class="hover:bg-gray-50">
<td class="px-4 py-2">{{ $table->name }}</td>
<td class="px-4 py-2">{{ $table->brand }}</td>
<td class="px-4 py-2">
<span class="px-2 py-1 rounded-full text-xs font-medium
{{ $table->status === 'Available' ? 'bg-green-100 text-green-800' :
($table->status === 'Booked' ? 'bg-yellow-100 text-yellow-800' :
'bg-red-100 text-red-800') }}">
{{ $table->status }}
</span>
</td>
<td class="px-4 py-2">
<a href="{{ route('admin.tables.edit', $table->id) }}"
class="text-blue-600 hover:underline">Edit</a>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="px-4 py-4 text-center text-gray-500">Belum ada data meja.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="mt-4">
{{ $tables->links() }}
</div>
<div class="p-6 bg-gray-50">
<!-- Header dengan Action Button -->
<div class="flex justify-between items-center mb-6">
<h1 class="text-3xl font-bold text-gray-800">Kelola Meja</h1>
<a href="{{ route('admin.tables.create') }}"
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center transition-colors duration-300">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clip-rule="evenodd" />
</svg>
Tambah Meja Baru
</a>
</div>
<!-- Flash Message -->
@if(session('success'))
<div class="bg-green-100 border-l-4 border-green-500 text-green-700 p-4 mb-6 rounded shadow-md" role="alert">
<div class="flex items-center">
<svg class="h-5 w-5 mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
</svg>
<p>{{ session('success') }}</p>
</div>
</div>
@endif
<!-- Search and Filter -->
<div class="bg-white p-4 rounded-lg shadow-md mb-6">
<form action="{{ route('admin.tables.index') }}" method="GET" class="md:flex items-center space-y-4 md:space-y-0 md:space-x-4">
<div class="flex-grow">
<input type="text" name="search" value="{{ request('search') }}"
placeholder="Cari nama meja..."
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="md:w-1/4">
<select name="status" class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">Semua Status</option>
<option value="Available" {{ request('status') == 'Available' ? 'selected' : '' }}>Available</option>
<option value="Booked" {{ request('status') == 'Booked' ? 'selected' : '' }}>Booked</option>
<option value="Unavailable" {{ request('status') == 'Unavailable' ? 'selected' : '' }}>Unavailable</option>
</select>
</div>
<div class="flex space-x-2">
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors duration-300">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
</svg>
</button>
<a href="{{ route('admin.tables.index') }}" class="bg-gray-200 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-300 transition-colors duration-300">
Reset
</a>
</div>
</form>
</div>
<!-- Table -->
<div class="bg-white shadow-md rounded-lg overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-100">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-600 uppercase tracking-wider">Nama Meja</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-600 uppercase tracking-wider">Merek</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-600 uppercase tracking-wider">Status</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-600 uppercase tracking-wider">Aksi</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse ($tables as $table)
<tr class="hover:bg-gray-50 transition-colors duration-200">
<td class="px-6 py-4 whitespace-nowrap">
<div class="font-medium text-gray-900">{{ $table->name }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-gray-700">{{ $table->brand }}</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-3 py-1 rounded-full text-xs font-medium
{{ $table->status === 'Available' ? 'bg-green-100 text-green-800' :
($table->status === 'Booked' ? 'bg-yellow-100 text-yellow-800' : 'bg-red-100 text-red-800') }}">
{{ $table->status }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium space-x-2">
<div class="flex justify-end space-x-2">
<a href="{{ route('admin.tables.edit', $table->id) }}"
class="text-blue-600 hover:text-blue-900 bg-blue-100 px-3 py-1 rounded-md hover:bg-blue-200 transition-colors duration-300">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</a>
<form action="{{ route('admin.tables.destroy', $table->id) }}" method="POST" class="inline" onsubmit="return confirm('Apakah Anda yakin ingin menghapus meja ini?');">
@csrf
@method('DELETE')
<button type="submit" class="text-red-600 hover:text-red-900 bg-red-100 px-3 py-1 rounded-md hover:bg-red-200 transition-colors duration-300">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="px-6 py-10 text-center text-gray-500">
<div class="flex flex-col items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 text-gray-400 mb-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" />
</svg>
<p class="text-lg">Belum ada data meja.</p>
<a href="{{ route('admin.tables.create') }}" class="mt-3 text-blue-600 hover:underline">Tambah meja sekarang!</a>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<!-- Pagination -->
<div class="mt-6">
{{ $tables->withQueryString()->links() }}
</div>
</div>
@endsection

View File

@ -7,77 +7,190 @@
<title>Admin Panel - {{ config('app.name') }}</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
<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;
}
.nav-item {
transition: all 0.3s ease;
}
.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;
height: 100%;
width: 4px;
background-color: rgb(37, 99, 235);
border-radius: 0 4px 4px 0;
}
.nav-item:hover:not(.active) {
background-color: rgb(249, 250, 251);
color: rgb(55, 65, 81);
}
.dropdown-transition {
transition: all 0.2s ease-out;
}
</style>
</head>
<body x-data="{ sidebarOpen: true }" class="flex">
<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 -->
<div :class="sidebarOpen ? 'w-64' : 'w-16'" class="bg-white border-r h-screen transition-all duration-300">
<div class="flex justify-between items-center p-4">
<div class="flex items-center space-x-2">
<span class="font-bold text-lg" x-show="sidebarOpen">Admin Panel</span>
<!-- Sidebar -->
<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">
<div class="p-2 bg-blue-600 rounded-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<span class="font-bold text-lg text-gray-800" x-show="sidebarOpen">VenueApp</span>
</div>
<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>
<!-- 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>
<nav class="space-y-1">
<a href="{{ route('admin.dashboard') }}"
class="nav-item flex items-center px-3 py-2.5 rounded-lg {{ request()->routeIs('admin.dashboard') ? 'active' : '' }}">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
<span x-show="sidebarOpen">Dashboard</span>
</a>
<a href="{{ route('admin.tables.index') }}"
class="nav-item flex items-center px-3 py-2.5 rounded-lg {{ request()->routeIs('admin.tables.*') ? 'active' : '' }}">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" />
</svg>
<span x-show="sidebarOpen">Kelola Meja</span>
</a>
<a href="{{ route('admin.bookings.index') }}"
class="nav-item flex items-center px-3 py-2.5 rounded-lg {{ request()->routeIs('admin.bookings.*') ? 'active' : '' }}">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 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>
<span x-show="sidebarOpen">Daftar Booking</span>
</a>
<a href="#"
class="nav-item flex items-center px-3 py-2.5 rounded-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" 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 7a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
<span x-show="sidebarOpen">Data User</span>
</a>
</nav>
<div x-show="sidebarOpen" class="text-xs font-semibold text-gray-400 uppercase tracking-wider px-3 mb-2 mt-6">
Sistem
</div>
<nav class="space-y-1">
<a href="#"
class="nav-item flex items-center px-3 py-2.5 rounded-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
<span x-show="sidebarOpen">Notifikasi</span>
<span class="ml-auto bg-red-500 text-white px-2 py-0.5 rounded-full text-xs" x-show="sidebarOpen">3</span>
</a>
<a href="#"
class="nav-item flex items-center px-3 py-2.5 rounded-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span x-show="sidebarOpen">Pengaturan</span>
</a>
</nav>
</div>
<!-- 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, 0, 1) }}
</div>
</div>
<div x-show="sidebarOpen" class="ml-3">
<p class="text-sm font-medium text-gray-800 truncate">{{ auth()->user()->name }}</p>
<p class="text-xs text-gray-500 truncate">{{ auth()->user()->email }}</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">
<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>
<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>
<button @click="sidebarOpen = !sidebarOpen" class="text-gray-600 focus:outline-none">
<svg x-show="sidebarOpen" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
<svg x-show="!sidebarOpen" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
<!-- Navigation -->
<nav class="flex flex-col justify-between h-full mt-4">
<ul>
<li><a href="{{ route('admin.dashboard') }}" class="block px-4 py-2 hover:bg-gray-100">📊 <span
x-show="sidebarOpen">Dashboard</span></a></li>
<li><a href="{{ route('admin.tables.index') }}" class="block px-4 py-2 hover:bg-gray-100">🪑 <span
x-show="sidebarOpen">Kelola
Meja</span></a></li>
<li><a href="{{ route('admin.bookings.index') }}" class="block px-4 py-2 hover:bg-gray-100">📅 <span
x-show="sidebarOpen">Daftar
Booking</span></a></li>
<li><a href="#" class="block px-4 py-2 hover:bg-gray-100">👥 <span x-show="sidebarOpen">Data
User</span></a></li>
<li><a href="#" class="block px-4 py-2 hover:bg-gray-100">🔔 <span
x-show="sidebarOpen">Notifikasi</span></a></li>
<li><a href="#" class="block px-4 py-2 hover:bg-gray-100">⚙️ <span
x-show="sidebarOpen">Pengaturan</span></a></li>
<li>
<div class="relative mt-4 px-4" x-data="{ open: false }">
<button @click="open = !open" class="flex items-center w-full text-left">
<span class="truncate" x-show="sidebarOpen">{{ auth()->user()->name }}</span>
<svg x-show="sidebarOpen" class="ml-1 h-4 w-4" fill="none" stroke="currentColor"
stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />
</svg>
</button>
<div x-show="open" @click.outside="open = false"
class="absolute left-4 mt-2 w-48 bg-white border rounded shadow-md z-10" x-cloak>
<a href="#" class="block px-4 py-2 text-sm hover:bg-gray-100">Edit Profil</a>
<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>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Top Header -->
<header class="bg-white shadow-sm lg:hidden">
<div class="px-4 sm:px-6 lg:px-8 py-4 flex items-center justify-between">
<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:hidden">
<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>
<div class="lg:hidden">
<span class="font-semibold text-lg">{{ config('app.name') }}</span>
</div>
</li>
</ul>
</nav>
</div>
</header>
<!-- User Dropdown -->
</div>
<!-- Main content -->
<div class="flex-1 p-6 bg-gray-50 min-h-screen">
@yield('content')
<!-- Page Content -->
<main class="flex-1 overflow-x-hidden overflow-y-auto">
@yield('content')
</main>
</div>
</div>
@stack('scripts')
</body>
</html>

View File

@ -34,8 +34,18 @@
Route::middleware(['auth', 'is_admin'])->prefix('admin')->group(function () {
Route::get('/', [AdminController::class, 'index'])->name('admin.dashboard');
Route::get('/bookings', [BookingsController::class, 'index'])->name('admin.bookings.index');
Route::get('/tables', [TableController::class, 'kelolaMeja'])->name('admin.tables.index');
Route::get('/bookings/export', [BookingsController::class, 'export'])->name('admin.bookings.export');
Route::get('/bookings/{id}', [BookingsController::class, 'show'])->name('admin.bookings.show');
Route::get('/bookings/{id}/edit', [BookingsController::class, 'edit'])->name('admin.bookings.edit');
Route::put('/bookings/{id}', [BookingsController::class, 'update'])->name('admin.bookings.update');
Route::patch('/bookings/{id}/complete', [BookingsController::class, 'complete'])->name('admin.bookings.complete');
Route::patch('/bookings/{id}/cancel', [BookingsController::class, 'cancel'])->name('admin.bookings.cancel');
Route::get('/tables/{id}/edit', [TableController::class, 'editTable'])->name('admin.tables.edit');
Route::put('/tables/{id}', [TableController::class, 'updateTable'])->name('admin.tables.update');
// CRUD routes untuk manajemen meja
Route::get('/tables', [TableController::class, 'index'])->name('admin.tables.index');
Route::get('/tables/create', [TableController::class, 'create'])->name('admin.tables.create');
Route::post('/tables', [TableController::class, 'store'])->name('admin.tables.store');
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');
});