Reservasi-Cafe/resources/views/admin/tables.blade.php

434 lines
24 KiB
PHP

@extends('layouts.admin.app')
@push('styles')
<link href="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.css" rel="stylesheet" />
<style>
.timeline-container {
max-height: 400px;
overflow-y: auto;
}
.timeline-item {
position: relative;
padding-left: 28px;
}
.timeline-item::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 2px;
background-color: #e5e7eb;
}
.timeline-item::after {
content: '';
position: absolute;
left: -4px;
top: 50%;
transform: translateY(-50%);
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #8B0000;
}
.timeline-item.available::after {
background-color: #10B981;
}
.meja-image {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 8px;
}
.meja-image-preview {
max-width: 100%;
height: 150px;
object-fit: cover;
border-radius: 4px;
margin-top: 10px;
}
</style>
@endpush
@section('content')
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold text-gray-800">Manajemen Meja</h1>
<button type="button" onclick="openModal('addTableModal')" class="bg-[#8B0000] text-white px-4 py-2 rounded-lg hover:bg-red-800">
<i class="fas fa-plus mr-2"></i>Tambah Meja
</button>
</div>
@if(session('success'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4" role="alert">
<span class="block sm:inline">{{ session('success') }}</span>
</div>
@endif
@if ($errors->any())
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach($tables as $table)
<div class="bg-white rounded-lg shadow-md overflow-hidden">
@if($table->gambar)
<img src="{{ asset('storage/' . $table->gambar) }}" alt="Meja {{ $table->nomor_meja }}" class="meja-image">
@else
<div class="bg-gray-200 h-48 flex items-center justify-center">
<span class="text-gray-500">Tidak ada gambar</span>
</div>
@endif
<div class="p-6">
<div class="flex justify-between items-start mb-4">
<div>
<h2 class="text-xl font-semibold">Meja {{ $table->nomor_meja }}</h2>
<p class="text-gray-600">{{ ucfirst($table->kategori) }}</p>
</div>
<span class="px-3 py-1 rounded-full text-sm font-semibold
{{ $table->status === 'tersedia' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' }}">
{{ $table->status === 'tersedia' ? 'Tersedia' : 'Tidak Tersedia' }}
</span>
</div>
<div class="mb-4">
<p class="text-gray-600">Kapasitas: {{ $table->kapasitas }} Orang</p>
@if($table->deskripsi)
<p class="text-gray-600 mt-2">{{ $table->deskripsi }}</p>
@endif
</div>
<div class="flex justify-between items-center">
<button onclick="checkTableSchedule({{ $table->id }})"
class="text-blue-600 hover:text-blue-800">
<i class="fas fa-clock mr-1"></i>Cek Jadwal
</button>
<div>
<button onclick="openEditModal({{ $table->id }}, {{ json_encode($table) }})" class="text-blue-600 hover:text-blue-900 mr-3">
<i class="fas fa-edit"></i>
</button>
<form action="{{ route('admin.tables.destroy', $table) }}" method="POST" class="inline">
@csrf
@method('DELETE')
<button type="submit" class="text-red-600 hover:text-red-900"
onclick="return confirm('Apakah Anda yakin ingin menghapus meja ini?')">
<i class="fas fa-trash"></i>
</button>
</form>
</div>
</div>
</div>
</div>
@endforeach
</div>
<!-- Modal Jadwal Meja -->
<div id="scheduleModal" tabindex="-1" aria-hidden="true" class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
<div class="relative w-full max-w-2xl max-h-full">
<!-- Modal content -->
<div class="relative bg-white rounded-lg shadow">
<!-- Modal header -->
<div class="flex items-start justify-between p-4 border-b rounded-t">
<h3 class="text-xl font-semibold text-gray-900">
Jadwal Meja <span id="tableNumber"></span>
</h3>
<button type="button" onclick="closeModal('scheduleModal')" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ml-auto inline-flex justify-center items-center">
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
</svg>
<span class="sr-only">Close modal</span>
</button>
</div>
<!-- Modal body -->
<div class="p-6 space-y-6">
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2">Pilih Tanggal</label>
<input type="date" id="scheduleDate"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"
onchange="updateSchedule()"
min="{{ date('Y-m-d') }}">
</div>
<div class="timeline-container">
<div id="scheduleTimeline" class="space-y-4">
<!-- Timeline items will be inserted here -->
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal Tambah Meja -->
<div id="addTableModal" tabindex="-1" aria-hidden="true" class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
<div class="relative w-full max-w-md max-h-full">
<div class="relative bg-white rounded-lg shadow">
<div class="flex items-start justify-between p-4 border-b rounded-t">
<h3 class="text-xl font-semibold text-gray-900">
Tambah Meja Baru
</h3>
<button type="button" onclick="closeModal('addTableModal')" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ml-auto inline-flex justify-center items-center">
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
</svg>
</button>
</div>
<div class="p-6 space-y-6">
<form action="{{ route('admin.tables.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="grid gap-4 mb-4">
<div>
<label for="nomor_meja" class="block mb-2 text-sm font-medium text-gray-900">Nomor Meja</label>
<input type="text" name="nomor_meja" id="nomor_meja" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" required>
</div>
<div>
<label for="kapasitas" class="block mb-2 text-sm font-medium text-gray-900">Kapasitas</label>
<input type="number" name="kapasitas" id="kapasitas" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" required min="1">
</div>
<div>
<label for="kategori" class="block mb-2 text-sm font-medium text-gray-900">Kategori</label>
<select name="kategori" id="kategori" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" required>
<option value="outdoor">Outdoor</option>
<option value="vip-outdoor">VIP Outdoor</option>
<option value="vip-indoor">VIP Indoor</option>
</select>
</div>
<div>
<label for="status" class="block mb-2 text-sm font-medium text-gray-900">Status</label>
<select name="status" id="status" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" required>
<option value="tersedia">Tersedia</option>
<option value="tidak_tersedia">Tidak Tersedia</option>
</select>
</div>
<div>
<label for="deskripsi" class="block mb-2 text-sm font-medium text-gray-900">Deskripsi</label>
<textarea name="deskripsi" id="deskripsi" rows="3" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"></textarea>
</div>
<div>
<label for="gambar" class="block mb-2 text-sm font-medium text-gray-900">Gambar Meja</label>
<input type="file" name="gambar" id="gambar" accept="image/*" onchange="previewImage(this, 'add-image-preview')" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5">
<img id="add-image-preview" class="meja-image-preview hidden" alt="Preview">
</div>
</div>
<div class="flex items-center justify-end space-x-2">
<button type="button" onclick="closeModal('addTableModal')" class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10">Batal</button>
<button type="submit" class="text-white bg-[#8B0000] hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center">Simpan</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Modal Edit Meja -->
<div id="editTableModal" tabindex="-1" aria-hidden="true" class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
<div class="relative w-full max-w-md max-h-full">
<div class="relative bg-white rounded-lg shadow">
<div class="flex items-start justify-between p-4 border-b rounded-t">
<h3 class="text-xl font-semibold text-gray-900">
Edit Meja
</h3>
<button type="button" onclick="closeModal('editTableModal')" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ml-auto inline-flex justify-center items-center">
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
</svg>
</button>
</div>
<div class="p-6 space-y-6">
<form id="editTableForm" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
<div class="grid gap-4 mb-4">
<div>
<label for="edit_nomor_meja" class="block mb-2 text-sm font-medium text-gray-900">Nomor Meja</label>
<input type="text" name="nomor_meja" id="edit_nomor_meja" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" required>
</div>
<div>
<label for="edit_kapasitas" class="block mb-2 text-sm font-medium text-gray-900">Kapasitas</label>
<input type="number" name="kapasitas" id="edit_kapasitas" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" required min="1">
</div>
<div>
<label for="edit_kategori" class="block mb-2 text-sm font-medium text-gray-900">Kategori</label>
<select name="kategori" id="edit_kategori" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" required>
<option value="outdoor">Outdoor</option>
<option value="vip-outdoor">VIP Outdoor</option>
<option value="vip-indoor">VIP Indoor</option>
</select>
</div>
<div>
<label for="edit_status" class="block mb-2 text-sm font-medium text-gray-900">Status</label>
<select name="status" id="edit_status" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" required>
<option value="tersedia" {{ $table->status === 'tersedia' ? 'selected' : '' }}>Tersedia</option>
<option value="tidak_tersedia" {{ $table->status === 'tidak_tersedia' ? 'selected' : '' }}>Tidak Tersedia</option>
</select>
</div>
<div>
<label for="edit_deskripsi" class="block mb-2 text-sm font-medium text-gray-900">Deskripsi</label>
<textarea name="deskripsi" id="edit_deskripsi" rows="3" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"></textarea>
</div>
<div>
<label for="edit_gambar" class="block mb-2 text-sm font-medium text-gray-900">Gambar Meja</label>
<input type="file" name="gambar" id="edit_gambar" accept="image/*" onchange="previewImage(this, 'edit-image-preview')" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5">
<img id="edit-image-preview" class="meja-image-preview hidden" alt="Preview">
<div id="current-image" class="mt-2"></div>
</div>
</div>
<div class="flex items-center justify-end space-x-2">
<button type="button" onclick="closeModal('editTableModal')" class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10">Batal</button>
<button type="submit" class="text-white bg-[#8B0000] hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center">Simpan Perubahan</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@push('scripts')
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.js"></script>
<script>
let currentTableId = null;
function previewImage(input, previewId) {
const preview = document.getElementById(previewId);
if (input.files && input.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
preview.src = e.target.result;
preview.classList.remove('hidden');
}
reader.readAsDataURL(input.files[0]);
}
}
function openEditModal(tableId, table) {
const form = document.getElementById('editTableForm');
form.action = `/admin/tables/${tableId}`;
document.getElementById('edit_nomor_meja').value = table.nomor_meja;
document.getElementById('edit_kapasitas').value = table.kapasitas;
document.getElementById('edit_kategori').value = table.kategori;
document.getElementById('edit_status').value = table.status;
document.getElementById('edit_deskripsi').value = table.deskripsi || '';
const currentImage = document.getElementById('current-image');
const imagePreview = document.getElementById('edit-image-preview');
if (table.gambar) {
currentImage.innerHTML = `<img src="{{ asset('storage') }}/${table.gambar}" class="meja-image-preview" alt="Current image">`;
imagePreview.classList.add('hidden');
} else {
currentImage.innerHTML = '<p class="text-gray-500">Tidak ada gambar</p>';
imagePreview.classList.add('hidden');
}
const statusSelect = document.getElementById('edit_status');
const options = statusSelect.options;
for (let i = 0; i < options.length; i++) {
if (options[i].value === table.status) {
options[i].selected = true;
break;
}
}
openModal('editTableModal');
}
function checkTableSchedule(tableId) {
currentTableId = tableId;
const table = @json($tables).find(t => t.id === tableId);
document.getElementById('tableNumber').textContent = table.nomor_meja;
document.getElementById('scheduleDate').value = new Date().toISOString().split('T')[0];
openModal('scheduleModal');
updateSchedule();
}
function updateSchedule() {
const date = document.getElementById('scheduleDate').value;
const timeline = document.getElementById('scheduleTimeline');
timeline.innerHTML = '<div class="text-center">Loading...</div>';
fetch(`/admin/tables/${currentTableId}/schedule?date=${date}`)
.then(response => response.json())
.then(data => {
if (data.success) {
const timeSlots = generateTimeSlots(data.reservations);
timeline.innerHTML = '';
timeSlots.forEach(slot => {
const itemClass = slot.isReserved ? '' : 'available';
const statusClass = slot.isReserved ? 'text-red-600' : 'text-green-600';
const status = slot.isReserved ? 'Dipesan' : 'Tersedia';
const reservationInfo = slot.isReserved ? `
<div class="text-sm text-gray-600">
<p>Pemesan: ${slot.reservation.name}</p>
<p>Jumlah: ${slot.reservation.people} Orang</p>
<p>Status: ${slot.reservation.status}</p>
</div>
` : '';
timeline.innerHTML += `
<div class="timeline-item ${itemClass} p-4 mb-4 bg-gray-50 rounded-lg">
<div class="flex justify-between items-start">
<div>
<span class="font-semibold">${slot.time}</span>
<span class="${statusClass} ml-2">${status}</span>
</div>
</div>
${reservationInfo}
</div>
`;
});
}
})
.catch(error => {
console.error('Error:', error);
timeline.innerHTML = '<div class="text-center text-red-600">Error loading schedule</div>';
});
}
function generateTimeSlots(reservations) {
const slots = [];
const startHour = 10; // 10:00
const endHour = 22; // 22:00
for (let hour = startHour; hour <= endHour; hour++) {
// Generate slots for both :00 and :30 of each hour
['00', '30'].forEach(minutes => {
const time = `${hour.toString().padStart(2, '0')}:${minutes}`;
const reservation = reservations.find(r => {
const startTime = r.start_time;
const endTime = r.end_time;
return time >= startTime && time < endTime;
});
slots.push({
time: time,
isReserved: !!reservation,
reservation: reservation
});
});
}
return slots;
}
// Update modal functions to use Flowbite's modal
function openModal(modalId) {
const modal = document.getElementById(modalId);
modal.classList.remove('hidden');
}
function closeModal(modalId) {
const modal = document.getElementById(modalId);
modal.classList.add('hidden');
}
</script>
@endpush
@endsection