Admin bisa tutup buka venue
This commit is contained in:
parent
6873f94b83
commit
cbbd272b15
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Venue;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class ReopenVenuesCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'venues:reopen';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Automatically reopen venues that have reached their reopen date';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Checking for venues to reopen...');
|
||||
|
||||
$venuesReopened = 0;
|
||||
|
||||
// Get all closed venues that should be reopened today
|
||||
$venues = Venue::where('status', 'close')
|
||||
->whereNotNull('reopen_date')
|
||||
->whereDate('reopen_date', '<=', Carbon::today())
|
||||
->get();
|
||||
|
||||
foreach ($venues as $venue) {
|
||||
if ($venue->checkAutoReopen()) {
|
||||
$this->info("Venue '{$venue->name}' has been automatically reopened.");
|
||||
$venuesReopened++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($venuesReopened > 0) {
|
||||
$this->info("Successfully reopened {$venuesReopened} venue(s).");
|
||||
} else {
|
||||
$this->info('No venues to reopen today.');
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,11 @@ class Kernel extends ConsoleKernel
|
|||
*/
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
// $schedule->command('inspire')->hourly();
|
||||
// Run venue reopen check every day at 00:01
|
||||
$schedule->command('venues:reopen')
|
||||
->dailyAt('00:01')
|
||||
->withoutOverlapping()
|
||||
->runInBackground();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class VenueController extends Controller
|
||||
{
|
||||
|
@ -22,6 +23,11 @@ public function index()
|
|||
return redirect()->route('admin.dashboard')->with('error', 'Anda belum memiliki venue yang ditugaskan.');
|
||||
}
|
||||
|
||||
// Check for auto reopen
|
||||
if ($venue->checkAutoReopen()) {
|
||||
session()->flash('success', 'Venue telah dibuka kembali secara otomatis!');
|
||||
}
|
||||
|
||||
return view('admin.venues.index', compact('venue'));
|
||||
}
|
||||
|
||||
|
@ -91,16 +97,27 @@ public function update(Request $request)
|
|||
$imagePath = $request->file('image')->store('venues', 'public');
|
||||
}
|
||||
|
||||
// Update venue data
|
||||
$venue->update([
|
||||
// Prepare update data
|
||||
$updateData = [
|
||||
'name' => $request->name,
|
||||
'address' => $request->address,
|
||||
'phone' => $request->phone,
|
||||
'description' => $request->description,
|
||||
'open_time' => $request->open_time,
|
||||
'close_time' => $request->close_time,
|
||||
'image' => $imagePath,
|
||||
]);
|
||||
];
|
||||
|
||||
// Only update operating hours if venue is open
|
||||
if ($venue->status === 'open') {
|
||||
$updateData['open_time'] = $request->open_time;
|
||||
$updateData['close_time'] = $request->close_time;
|
||||
} else {
|
||||
// If venue is closed, update original times
|
||||
$updateData['original_open_time'] = $request->open_time;
|
||||
$updateData['original_close_time'] = $request->close_time;
|
||||
}
|
||||
|
||||
// Update venue data
|
||||
$venue->update($updateData);
|
||||
|
||||
return redirect()->route('admin.venue.index')
|
||||
->with('success', 'Informasi venue berhasil diperbarui!');
|
||||
|
@ -111,4 +128,63 @@ public function update(Request $request)
|
|||
->withInput();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle venue status (open/close)
|
||||
*/
|
||||
public function toggleStatus(Request $request)
|
||||
{
|
||||
$venue = auth()->user()->venue;
|
||||
|
||||
if (!$venue) {
|
||||
return response()->json(['error' => 'Venue tidak ditemukan'], 404);
|
||||
}
|
||||
|
||||
try {
|
||||
if ($venue->status === 'open') {
|
||||
// Closing venue - validate required fields
|
||||
$validator = Validator::make($request->all(), [
|
||||
'close_reason' => 'required|string|max:500',
|
||||
'reopen_date' => 'required|date|after:today',
|
||||
], [
|
||||
'close_reason.required' => 'Alasan penutupan harus diisi.',
|
||||
'reopen_date.required' => 'Tanggal buka kembali harus diisi.',
|
||||
'reopen_date.date' => 'Format tanggal tidak valid.',
|
||||
'reopen_date.after' => 'Tanggal buka kembali harus setelah hari ini.',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'error' => 'Validasi gagal',
|
||||
'errors' => $validator->errors()
|
||||
], 422);
|
||||
}
|
||||
|
||||
$venue->closeVenue($request->close_reason, $request->reopen_date);
|
||||
$message = 'Venue berhasil ditutup!';
|
||||
} else {
|
||||
// Opening venue
|
||||
$venue->openVenue();
|
||||
$message = 'Venue berhasil dibuka!';
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => $message,
|
||||
'status' => $venue->status,
|
||||
'venue' => [
|
||||
'status' => $venue->status,
|
||||
'close_reason' => $venue->close_reason,
|
||||
'reopen_date' => $venue->reopen_date ? $venue->reopen_date->format('d M Y') : null,
|
||||
'open_time' => $venue->open_time ? Carbon::parse($venue->open_time)->format('H:i') : null,
|
||||
'close_time' => $venue->close_time ? Carbon::parse($venue->close_time)->format('H:i') : null,
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'error' => 'Terjadi kesalahan: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class Venue extends Model
|
||||
{
|
||||
|
@ -13,15 +14,79 @@ class Venue extends Model
|
|||
'name',
|
||||
'address',
|
||||
'image',
|
||||
'phone', // Pastikan field ini ada
|
||||
'description', // Pastikan field ini ada
|
||||
'open_time', // Pastikan field ini ada
|
||||
'close_time', // Pastikan field ini ada
|
||||
'phone',
|
||||
'description',
|
||||
'open_time',
|
||||
'close_time',
|
||||
'status',
|
||||
'close_reason',
|
||||
'reopen_date',
|
||||
'original_open_time',
|
||||
'original_close_time',
|
||||
];
|
||||
|
||||
protected $dates = [
|
||||
'reopen_date',
|
||||
];
|
||||
|
||||
public function tables()
|
||||
{
|
||||
return $this->hasMany(Table::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if venue should automatically reopen
|
||||
*/
|
||||
public function checkAutoReopen()
|
||||
{
|
||||
if ($this->status === 'close' && $this->reopen_date && Carbon::today()->gte($this->reopen_date)) {
|
||||
$this->update([
|
||||
'status' => 'open',
|
||||
'open_time' => $this->original_open_time,
|
||||
'close_time' => $this->original_close_time,
|
||||
'close_reason' => null,
|
||||
'reopen_date' => null,
|
||||
'original_open_time' => null,
|
||||
'original_close_time' => null,
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close venue with reason and reopen date
|
||||
*/
|
||||
public function closeVenue($reason, $reopenDate)
|
||||
{
|
||||
// Simpan jam operasional saat ini sebelum mengubahnya
|
||||
$currentOpenTime = $this->open_time;
|
||||
$currentCloseTime = $this->close_time;
|
||||
|
||||
$this->update([
|
||||
'status' => 'close',
|
||||
'close_reason' => $reason,
|
||||
'reopen_date' => $reopenDate,
|
||||
'original_open_time' => $currentOpenTime, // Simpan jam asli
|
||||
'original_close_time' => $currentCloseTime, // Simpan jam asli
|
||||
'open_time' => '00:00', // Set ke 00:00 setelah menyimpan original
|
||||
'close_time' => '00:00', // Set ke 00:00 setelah menyimpan original
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open venue manually
|
||||
*/
|
||||
public function openVenue()
|
||||
{
|
||||
$this->update([
|
||||
'status' => 'open',
|
||||
'open_time' => $this->original_open_time ?: $this->open_time,
|
||||
'close_time' => $this->original_close_time ?: $this->close_time,
|
||||
'close_reason' => null,
|
||||
'reopen_date' => null,
|
||||
'original_open_time' => null,
|
||||
'original_close_time' => null,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('venues', function (Blueprint $table) {
|
||||
$table->enum('status', ['open', 'close'])->default('open')->after('close_time');
|
||||
$table->text('close_reason')->nullable()->after('status');
|
||||
$table->date('reopen_date')->nullable()->after('close_reason');
|
||||
$table->time('original_open_time')->nullable()->after('reopen_date');
|
||||
$table->time('original_close_time')->nullable()->after('original_open_time');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('venues', function (Blueprint $table) {
|
||||
$table->dropColumn(['status', 'close_reason', 'reopen_date', 'original_open_time', 'original_close_time']);
|
||||
});
|
||||
}
|
||||
};
|
|
@ -9,6 +9,25 @@
|
|||
<h1 class="text-2xl font-bold text-gray-900">Kelola Venue</h1>
|
||||
<p class="text-gray-600 mt-1">Kelola informasi venue Anda</p>
|
||||
</div>
|
||||
<div class="flex space-x-3">
|
||||
<!-- Venue Status Toggle -->
|
||||
<div class="flex items-center space-x-3">
|
||||
<span class="text-sm font-medium text-gray-700">Status Venue:</span>
|
||||
<div class="relative">
|
||||
<button id="statusToggle"
|
||||
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 {{ $venue->status === 'open' ? 'bg-green-600' : 'bg-red-600' }}"
|
||||
onclick="toggleVenueStatus()">
|
||||
<span class="sr-only">Toggle venue status</span>
|
||||
<span
|
||||
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform {{ $venue->status === 'open' ? 'translate-x-6' : 'translate-x-1' }}"></span>
|
||||
</button>
|
||||
</div>
|
||||
<span id="statusText"
|
||||
class="text-sm font-medium {{ $venue->status === 'open' ? 'text-green-600' : 'text-red-600' }}">
|
||||
{{ $venue->status === 'open' ? 'Buka' : 'Tutup' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<a href="{{ route('admin.venue.edit') }}"
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-medium transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline mr-2" fill="none" viewBox="0 0 24 24"
|
||||
|
@ -20,6 +39,7 @@ class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-medium
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alert Messages -->
|
||||
@if(session('success'))
|
||||
|
@ -34,6 +54,31 @@ class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-medium
|
|||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Venue Status Alert -->
|
||||
{{-- @if($venue->status === 'close')
|
||||
<div class="mb-6 bg-red-50 border border-red-200 rounded-lg p-4">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-5 w-5 text-red-400" 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 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="text-sm font-medium text-red-800">Venue Sedang Tutup</h3>
|
||||
<div class="mt-2 text-sm text-red-700">
|
||||
<p><strong>Alasan:</strong> {{ $venue->close_reason }}</p>
|
||||
@if($venue->reopen_date)
|
||||
<p><strong>Akan buka kembali pada:</strong> {{ $venue->reopen_date->format('d M Y') }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif --}}
|
||||
|
||||
<!-- Venue Information Card -->
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div class="p-6">
|
||||
|
@ -92,17 +137,40 @@ class="w-full h-48 object-cover rounded-lg">
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status -->
|
||||
<div class="flex items-start">
|
||||
<div class="flex-shrink-0 w-32">
|
||||
<span class="text-sm font-medium text-gray-500">Status:</span>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<span
|
||||
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {{ $venue->status === 'open' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' }}">
|
||||
{{ $venue->status === 'open' ? 'Buka' : 'Tutup' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Operating Hours -->
|
||||
<div class="flex items-start">
|
||||
<div class="flex-shrink-0 w-32">
|
||||
<span class="text-sm font-medium text-gray-500">Jam Operasional:</span>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
@if($venue->status === 'open')
|
||||
<span class="text-sm text-gray-900">
|
||||
{{ $venue->open_time ? \Carbon\Carbon::parse($venue->open_time)->format('H:i') : '-' }}
|
||||
-
|
||||
{{ $venue->close_time ? \Carbon\Carbon::parse($venue->close_time)->format('H:i') : '-' }}
|
||||
</span>
|
||||
@else
|
||||
<span class="text-sm text-red-600">Tutup Sementara</span>
|
||||
@if($venue->original_open_time && $venue->original_close_time)
|
||||
<div class="text-xs text-gray-500 mt-1">
|
||||
Jam normal: {{ \Carbon\Carbon::parse($venue->original_open_time)->format('H:i') }} -
|
||||
{{ \Carbon\Carbon::parse($venue->original_close_time)->format('H:i') }}
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -168,7 +236,8 @@ class="text-sm text-gray-900">{{ $venue->updated_at->format('d M Y, H:i') }}</sp
|
|||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500">Meja Tersedia</p>
|
||||
<p class="text-2xl font-semibold text-gray-900">
|
||||
{{ $venue->tables->where('status', 'available')->count() }}</p>
|
||||
{{ $venue->tables->where('status', 'available')->count() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -188,10 +257,255 @@ class="text-sm text-gray-900">{{ $venue->updated_at->format('d M Y, H:i') }}</sp
|
|||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500">Meja Terpakai</p>
|
||||
<p class="text-2xl font-semibold text-gray-900">
|
||||
{{ $venue->tables->where('status', 'occupied')->count() }}</p>
|
||||
{{ $venue->tables->where('status', 'occupied')->count() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Close Venue Modal -->
|
||||
<div id="closeVenueModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full hidden">
|
||||
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
|
||||
<div class="mt-3">
|
||||
<div class="flex items-center">
|
||||
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100">
|
||||
<svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 text-center">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">Tutup Venue</h3>
|
||||
<div class="mt-4 text-left">
|
||||
<form id="closeVenueForm">
|
||||
<div class="mb-4">
|
||||
<label for="closeReason" class="block text-sm font-medium text-gray-700 mb-2">Alasan
|
||||
Penutupan *</label>
|
||||
<textarea id="closeReason" name="close_reason" rows="3"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="Masukkan alasan penutupan venue..." required></textarea>
|
||||
<div id="closeReasonError" class="text-red-500 text-sm mt-1 hidden"></div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="reopenDate" class="block text-sm font-medium text-gray-700 mb-2">Tanggal Buka
|
||||
Kembali *</label>
|
||||
<input type="date" id="reopenDate" name="reopen_date"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
min="{{ date('Y-m-d', strtotime('+1 day')) }}" required>
|
||||
<div id="reopenDateError" class="text-red-500 text-sm mt-1 hidden"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end space-x-3 mt-4">
|
||||
<button type="button" onclick="closeModal()"
|
||||
class="px-4 py-2 bg-gray-300 text-gray-700 rounded-md hover:bg-gray-400 transition-colors">
|
||||
Batal
|
||||
</button>
|
||||
<button type="button" onclick="confirmCloseVenue()"
|
||||
class="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors">
|
||||
Tutup Venue
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading Spinner -->
|
||||
<div id="loadingSpinner" class="fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center hidden">
|
||||
<div class="bg-white p-4 rounded-lg">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600"></div>
|
||||
<span class="text-gray-700">Memproses...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentVenueStatus = '{{ $venue->status }}';
|
||||
|
||||
function toggleVenueStatus() {
|
||||
if (currentVenueStatus === 'open') {
|
||||
// Show close venue modal
|
||||
document.getElementById('closeVenueModal').classList.remove('hidden');
|
||||
} else {
|
||||
// Open venue directly
|
||||
confirmToggleStatus();
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById('closeVenueModal').classList.add('hidden');
|
||||
// Clear form
|
||||
document.getElementById('closeVenueForm').reset();
|
||||
clearErrors();
|
||||
}
|
||||
|
||||
function clearErrors() {
|
||||
document.getElementById('closeReasonError').classList.add('hidden');
|
||||
document.getElementById('reopenDateError').classList.add('hidden');
|
||||
}
|
||||
|
||||
function confirmCloseVenue() {
|
||||
const closeReason = document.getElementById('closeReason').value.trim();
|
||||
const reopenDate = document.getElementById('reopenDate').value;
|
||||
|
||||
// Clear previous errors
|
||||
clearErrors();
|
||||
|
||||
// Validate form
|
||||
let hasError = false;
|
||||
|
||||
if (!closeReason) {
|
||||
document.getElementById('closeReasonError').textContent = 'Alasan penutupan harus diisi.';
|
||||
document.getElementById('closeReasonError').classList.remove('hidden');
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (!reopenDate) {
|
||||
document.getElementById('reopenDateError').textContent = 'Tanggal buka kembali harus diisi.';
|
||||
document.getElementById('reopenDateError').classList.remove('hidden');
|
||||
hasError = true;
|
||||
} else {
|
||||
const today = new Date();
|
||||
const selectedDate = new Date(reopenDate);
|
||||
if (selectedDate <= today) {
|
||||
document.getElementById('reopenDateError').textContent = 'Tanggal buka kembali harus setelah hari ini.';
|
||||
document.getElementById('reopenDateError').classList.remove('hidden');
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Close modal and proceed with toggle
|
||||
closeModal();
|
||||
confirmToggleStatus({
|
||||
close_reason: closeReason,
|
||||
reopen_date: reopenDate
|
||||
});
|
||||
}
|
||||
|
||||
function confirmToggleStatus(data = {}) {
|
||||
// Show loading spinner
|
||||
document.getElementById('loadingSpinner').classList.remove('hidden');
|
||||
|
||||
// Prepare request data
|
||||
const requestData = {
|
||||
_token: '{{ csrf_token() }}',
|
||||
...data
|
||||
};
|
||||
|
||||
fetch('{{ route("admin.venue.toggle-status") }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify(requestData)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Hide loading spinner
|
||||
document.getElementById('loadingSpinner').classList.add('hidden');
|
||||
|
||||
if (data.success) {
|
||||
// Update UI
|
||||
updateVenueStatusUI(data.venue);
|
||||
|
||||
// Show success message
|
||||
showAlert(data.message, 'success');
|
||||
|
||||
// Update current status
|
||||
currentVenueStatus = data.status;
|
||||
|
||||
// Reload page after 2 seconds to refresh all data
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
} else {
|
||||
if (data.errors) {
|
||||
// Handle validation errors
|
||||
let errorMessage = 'Terjadi kesalahan validasi:\n';
|
||||
Object.values(data.errors).forEach(error => {
|
||||
errorMessage += '- ' + error[0] + '\n';
|
||||
});
|
||||
showAlert(errorMessage, 'error');
|
||||
} else {
|
||||
showAlert(data.error || 'Terjadi kesalahan yang tidak diketahui', 'error');
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Hide loading spinner
|
||||
document.getElementById('loadingSpinner').classList.add('hidden');
|
||||
|
||||
console.error('Error:', error);
|
||||
showAlert('Venue ditutup sementara!', 'error');
|
||||
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
function updateVenueStatusUI(venue) {
|
||||
const toggle = document.getElementById('statusToggle');
|
||||
const statusText = document.getElementById('statusText');
|
||||
const toggleButton = toggle.querySelector('span:last-child');
|
||||
|
||||
if (venue.status === 'open') {
|
||||
toggle.classList.remove('bg-red-600');
|
||||
toggle.classList.add('bg-green-600');
|
||||
toggleButton.classList.remove('translate-x-1');
|
||||
toggleButton.classList.add('translate-x-6');
|
||||
statusText.textContent = 'Buka';
|
||||
statusText.classList.remove('text-red-600');
|
||||
statusText.classList.add('text-green-600');
|
||||
} else {
|
||||
toggle.classList.remove('bg-green-600');
|
||||
toggle.classList.add('bg-red-600');
|
||||
toggleButton.classList.remove('translate-x-6');
|
||||
toggleButton.classList.add('translate-x-1');
|
||||
statusText.textContent = 'Tutup';
|
||||
statusText.classList.remove('text-green-600');
|
||||
statusText.classList.add('text-red-600');
|
||||
}
|
||||
}
|
||||
|
||||
function showAlert(message, type) {
|
||||
// Remove existing alerts
|
||||
const existingAlerts = document.querySelectorAll('.alert-message');
|
||||
existingAlerts.forEach(alert => alert.remove());
|
||||
|
||||
// Create new alert
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = `alert-message mb-6 px-4 py-3 rounded-lg ${type === 'success' ? 'bg-green-100 border border-green-400 text-green-700' : 'bg-red-100 border border-red-400 text-red-700'
|
||||
}`;
|
||||
alertDiv.textContent = message;
|
||||
|
||||
// Insert alert after header
|
||||
const header = document.querySelector('.mb-6');
|
||||
header.parentNode.insertBefore(alertDiv, header.nextSibling);
|
||||
|
||||
// Auto remove after 5 seconds
|
||||
setTimeout(() => {
|
||||
alertDiv.remove();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Set minimum date for reopen date input
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
const minDate = tomorrow.toISOString().split('T')[0];
|
||||
document.getElementById('reopenDate').setAttribute('min', minDate);
|
||||
});
|
||||
</script>
|
||||
@endsection
|
|
@ -58,11 +58,32 @@ class="flex flex-col h-full border border-gray-400 rounded-lg overflow-hidden">
|
|||
<div class="flex-grow px-4 py-2">
|
||||
<h3 class="text-sm text-gray-400 font-semibold mb-2">Venue</h3>
|
||||
<h1 class="text-xl text-gray-800 font-semibold">{{ $venue->name }}</h1>
|
||||
@if($venue['status'] === 'open')
|
||||
{{-- Venue sedang buka - tampilkan jam operasional --}}
|
||||
<p class="text-sm text-gray-600 mt-1">
|
||||
<i class="fa-regular fa-clock"></i>
|
||||
<i class="fa-regular fa-clock text-green-500"></i>
|
||||
Buka: {{ date('H:i', strtotime($venue['open_time'])) }} -
|
||||
{{ date('H:i', strtotime($venue['close_time'])) }}
|
||||
</p>
|
||||
@else
|
||||
{{-- Venue sedang tutup - tampilkan informasi penutupan --}}
|
||||
<div class="mt-1">
|
||||
<p class="text-sm text-red-600 font-medium">
|
||||
<i class="fa-solid fa-circle-xmark text-red-500"></i>
|
||||
Tutup Sementara - {{ $venue['close_reason'] }}
|
||||
</p>
|
||||
|
||||
|
||||
@if(!empty($venue['reopen_date']))
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
<i class="fa-regular fa-calendar"></i>
|
||||
<strong>Buka kembali:</strong>
|
||||
{{ \Carbon\Carbon::parse($venue['reopen_date'])->format('d M Y') }} - Jam
|
||||
{{ date('H:i', strtotime($venue['original_open_time'])) }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
<p class="mt-10 text-gray-500 text-sm">Mulai:
|
||||
<span class="font-bold text-gray-800">Rp30,000</span>
|
||||
<span class="text-gray-400 font-thin text-sm">/ jam</span>
|
||||
|
|
|
@ -22,11 +22,22 @@ class="w-full h-full object-cover rounded-lg mb-4 mt-8" />
|
|||
|
||||
<h1 class="text-xl text-gray-800 font-semibold">{{ $venue['name'] }}</h1>
|
||||
<p class="text-sm text-gray-500">{{ $venue['location'] ?? 'Lokasi tidak tersedia' }}</p>
|
||||
@if($venue['status'] === 'open')
|
||||
{{-- Venue sedang buka - tampilkan jam operasional --}}
|
||||
<p class="text-sm text-gray-600 mt-1">
|
||||
<i class="fa-regular fa-clock"></i>
|
||||
<i class="fa-regular fa-clock text-green-500"></i>
|
||||
Jam Operasional: {{ date('H:i', strtotime($venue['open_time'])) }} -
|
||||
{{ date('H:i', strtotime($venue['close_time'])) }}
|
||||
</p>
|
||||
@else
|
||||
{{-- Venue sedang tutup - tampilkan informasi penutupan --}}
|
||||
<div class="mt-1">
|
||||
<p class="text-sm text-red-600 font-medium">
|
||||
<i class="fa-solid fa-circle-xmark text-red-500"></i>
|
||||
Tutup Sementara - {{ $venue['close_reason'] }}
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<a href="https://www.google.com/maps/search/?api=1&query={{ urlencode($venue['address']) }}" target="_blank"
|
||||
class="flex items-center bg-[url('/public/images/map.jpg')] bg-cover bg-center p-4">
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
Route::get('/venue', [AdminVenueController::class, 'index'])->name('admin.venue.index');
|
||||
Route::get('/venue/edit', [AdminVenueController::class, 'edit'])->name('admin.venue.edit');
|
||||
Route::put('/venue/update', [AdminVenueController::class, 'update'])->name('admin.venue.update');
|
||||
Route::post('/venue/toggle-status', [AdminVenueController::class, 'toggleStatus'])->name('admin.venue.toggle-status');
|
||||
|
||||
// Revenue management routes
|
||||
Route::get('/revenues', [RevenueController::class, 'index'])->name('admin.revenues.index');
|
||||
|
|
Loading…
Reference in New Issue