+ availability

This commit is contained in:
2026-01-26 09:06:06 +00:00
parent 18aad4749f
commit a448fce756
34 changed files with 1089 additions and 578 deletions

View File

@@ -0,0 +1,97 @@
<div>
@if($roomTypes->isEmpty())
<p>Нет типов номеров. <a href="{{ route('admin.room-types.create') }}">Создать тип номера</a></p>
@else
@foreach($roomTypes as $type)
<div style="margin-bottom: 25px; padding: 15px; border: 1px solid #eee; border-radius: 6px;">
<h3>{{ $type->name }}</h3>
<!-- Форма добавления периода -->
<form class="availability-form" data-room-type-id="{{ $type->id }}" style="margin-bottom: 15px;">
@csrf
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
<input type="date" name="start_date" required style="flex:1; padding:6px; border:1px solid #ccc; border-radius:4px;">
<input type="date" name="end_date" required style="flex:1; padding:6px; border:1px solid #ccc; border-radius:4px;">
</div>
<div style="margin-bottom: 10px;">
<label>
<input type="checkbox" name="is_available" value="1" checked> Доступен
</label>
</div>
<div style="margin-bottom: 10px;">
<input type="number" name="price" step="0.01" placeholder="Цена" style="width:100%; padding:6px; border:1px solid #ccc; border-radius:4px;">
</div>
<button type="submit" class="btn" style="padding:6px 12px;">Сохранить</button>
</form>
<!-- Список периодов -->
@if($type->availabilities->isEmpty())
<p>Нет периодов.</p>
@else
<table style="width:100%; font-size:0.9rem;">
<thead>
<tr>
<th>С</th>
<th>По</th>
<th>Статус</th>
<th>Цена</th>
<th>Действие</th>
</tr>
</thead>
<tbody>
@foreach($type->availabilities as $period)
<tr>
<td>{{ $period->start_date }}</td>
<td>{{ $period->end_date }}</td>
<td>
@if($period->is_available)
<span style="color:green;">Доступен</span>
@else
<span style="color:red;">Недоступен</span>
@endif
</td>
<td>{{ $period->price ? number_format($period->price, 2) : '-' }}</td>
<td>
<form method="POST" action="/admin/availability/{{ $period->id }}" style="display:inline;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger" style="padding:4px 8px; font-size:0.8rem;" onclick="return confirm('Удалить период?')">
Удалить
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
@endif
</div>
@endforeach
@endif
</div>
<script>
document.querySelectorAll('.availability-form').forEach(form => {
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const roomTypeId = form.dataset.roomTypeId;
try {
const response = await fetch(`/admin/room-types/${roomTypeId}/availability`, {
method: 'POST',
body: formData,
headers: { 'X-Requested-With': 'XMLHttpRequest' }
});
if (response.ok) {
location.reload(); // Обновить модалку
} else {
alert('Ошибка сохранения');
}
} catch (error) {
alert('Ошибка сети');
}
});
});
</script>

View File

@@ -26,7 +26,7 @@
</div>
<button type="submit" class="btn">Сохранить</button>
<a href="{{ route('admin.hotels.index') }}" class="btn" style="background: #6c757d; text-decoration: none; margin-left: 10px;">Отмена</a>
<a href="{{ route('admin.hotels.index') }}" class="btn" style="background: #6a1b9a; margin-left: 10px;">Отмена</a>
</form>
</div>
@endsection
@endsections

View File

@@ -27,7 +27,7 @@
</div>
<button type="submit" class="btn">Сохранить</button>
<a href="{{ route('admin.hotels.index') }}" class="btn" style="background: #6c757d; text-decoration: none; margin-left: 10px;">Отмена</a>
<a href="{{ route('admin.hotels.index') }}" class="btn" style="background: #6a1b9a; margin-left: 10px;">Отмена</a>
</form>
</div>
@endsection

View File

@@ -4,7 +4,7 @@
<div>
<h1>Отели</h1>
<a href="{{ route('admin.hotels.create') }}" class="btn" style="display: inline-block; margin-bottom: 20px;">Добавить отель</a>
<a href="{{ route('admin.hotels.create') }}" class="btn">Добавить отель</a>
@if($hotels->isEmpty())
<p>Нет отелей.</p>
@@ -25,14 +25,20 @@
<td>{{ $hotel->address ?? '-' }}</td>
<td>{{ $hotel->phone ?? '-' }}</td>
<td>
<a href="{{ route('admin.hotels.edit', $hotel) }}" class="btn" style="background: #6c757d; margin-right: 6px;">Редактировать</a>
<a href="{{ route('admin.hotels.edit', $hotel) }}" class="btn" style="margin-right: 5px;">
Редактировать
</a>
<form action="{{ route('admin.hotels.destroy', $hotel) }}" method="POST" style="display: inline;">
@csrf
@method('DELETE')
<button type="submit" class="btn" style="background: #aaa;" onclick="return confirm('Удалить отель?')">
<button type="submit" class="btn btn-danger" style="margin-right: 5px;" onclick="return confirm('Удалить отель?')">
Удалить
</button>
</form>
<!-- Кнопка шахматки -->
<button type="button" class="btn btn-secondary open-calendar-modal" data-hotel-id="{{ $hotel->id }}">
Календарь
</button>
</td>
</tr>
@endforeach
@@ -40,4 +46,43 @@
</table>
@endif
</div>
<!-- Модальное окно для шахматки -->
<div id="calendarModal" style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:1000;">
<div style="background:white;margin:50px auto;padding:25px;border-radius:8px;max-width:800px;box-shadow:0 4px 20px rgba(0,0,0,0.3);">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;">
<h2>Доступные даты: <span id="modalHotelName"></span></h2>
<button id="closeCalendarModal" style="background:none;border:none;font-size:1.5rem;cursor:pointer;">×</button>
</div>
<div id="calendarModalContent">Загрузка...</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const modal = document.getElementById('calendarModal');
const closeModalBtn = document.getElementById('closeCalendarModal');
document.querySelectorAll('.open-calendar-modal').forEach(button => {
button.addEventListener('click', async function () {
const hotelId = this.dataset.hotelId;
const hotelName = this.closest('tr').querySelector('td:first-child').textContent;
try {
const response = await fetch(`/admin/hotels/${hotelId}/calendar`, {
headers: { 'X-Requested-With': 'XMLHttpRequest' }
});
document.getElementById('modalHotelName').textContent = hotelName;
document.getElementById('calendarModalContent').innerHTML = await response.text();
modal.style.display = 'block';
} catch (e) {
alert('Ошибка загрузки шахматки');
}
});
});
closeModalBtn.onclick = () => modal.style.display = 'none';
window.onclick = (e) => { if (e.target === modal) modal.style.display = 'none'; };
});
</script>
@endsection