diff --git a/app/Exports/BookingsExport.php b/app/Exports/BookingsExport.php new file mode 100644 index 0000000..c63e9c5 --- /dev/null +++ b/app/Exports/BookingsExport.php @@ -0,0 +1,111 @@ +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'], + ], + ], + ]; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/admin/AdminController.php b/app/Http/Controllers/admin/AdminController.php index 8afae9a..dfd6079 100644 --- a/app/Http/Controllers/admin/AdminController.php +++ b/app/Http/Controllers/admin/AdminController.php @@ -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' )); } -} +} \ No newline at end of file diff --git a/app/Http/Controllers/admin/BookingsController.php b/app/Http/Controllers/admin/BookingsController.php index a45a683..372593b 100644 --- a/app/Http/Controllers/admin/BookingsController.php +++ b/app/Http/Controllers/admin/BookingsController.php @@ -1,20 +1,124 @@ 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); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/admin/TableController.php b/app/Http/Controllers/admin/TableController.php index 11b934c..1b2f680 100644 --- a/app/Http/Controllers/admin/TableController.php +++ b/app/Http/Controllers/admin/TableController.php @@ -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.'); + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index d582c7e..8e35ca8 100644 --- a/composer.json +++ b/composer.json @@ -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": { diff --git a/composer.lock b/composer.lock index 9925425..b6988ee 100644 --- a/composer.lock +++ b/composer.lock @@ -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", diff --git a/resources/views/admin/bookings/index.blade.php b/resources/views/admin/bookings/index.blade.php index c2bc259..9145aae 100644 --- a/resources/views/admin/bookings/index.blade.php +++ b/resources/views/admin/bookings/index.blade.php @@ -1,42 +1,298 @@ @extends('layouts.admin') @section('content') -
User | -Meja | -Mulai | -Selesai | -Status | -|||||||
---|---|---|---|---|---|---|---|---|---|---|---|
{{ $booking->user->name }} | -{{ $booking->table->name }} | -{{ \Carbon\Carbon::parse($booking->start_time)->format('H:i d/m') }} | -{{ \Carbon\Carbon::parse($booking->end_time)->format('H:i d/m') }} | -- - {{ ucfirst($booking->status) }} - - | -
Belum ada data booking. | ++ + User + @if(request('sort') == 'user') + + @else + + @endif + + | ++ + Meja + @if(request('sort') == 'table') + + @else + + @endif + + | ++ + Mulai + @if(request('sort') == 'start_time') + + @else + + @endif + + | ++ + Selesai + @if(request('sort') == 'end_time') + + @else + + @endif + + | ++ + Status + @if(request('sort') == 'status') + + @else + + @endif + + | ++ Aksi + |
---|
Belum ada data booking
+Coba ubah filter atau tambahkan booking baru
+Selamat datang, {{ auth()->user()->name }}!
- -Jumlah Booking Hari Ini
-{{ $todayBookings }}
- - -Total Meja
-{{ $totalTables }}
- - -Meja Sedang Digunakan
-{{ $usedTables }}
- - -Meja Tersedia
-{{ $availableTables }}
- -Belum ada booking terbaru.
- @else -Nama User | -Meja | -Waktu | -Status | -
---|---|---|---|
{{ $booking->user->name }} | -{{ $booking->table->name }} | -{{ \Carbon\Carbon::parse($booking->start_time)->format('H:i') }} - - {{ \Carbon\Carbon::parse($booking->end_time)->format('H:i') }} | -{{ $booking->status }} | -
Selamat datang, {{ auth()->user()->name }}!
+{{ now()->format('l, d F Y') }}
+{{ now()->format('H:i') }}
+Pendapatan Hari Ini
+Rp{{ number_format($todayRevenue, 0, ',', '.') }}
+Pendapatan Bulan Ini: Rp{{ number_format($monthlyRevenue, 0, ',', '.') }}
+Booking Hari Ini
+{{ $todayBookings }}
+Pending: {{ $pendingBookings }}
+Paid: {{ $paidBookings }}
+Total Meja
+{{ $totalTables }}
+Tersedia: {{ $availableTables }}
+Digunakan: {{ $usedTables }}
+Penggunaan Meja
+{{ $totalTables > 0 ? round(($usedTables / $totalTables) * 100) : 0 }}%
+Belum ada booking terbaru.
+ @else +{{ $booking->user->name }}
+{{ \Carbon\Carbon::parse($booking->start_time)->format('H:i') }}
+{{ \Carbon\Carbon::parse($booking->start_time)->format('d/m') }}
+