хз
This commit is contained in:
@@ -236,6 +236,7 @@ tr.row-checkout-today { background: #fef2f2 !important; border-left: 4px solid #
|
||||
</div>
|
||||
<div class="card-body-custom">
|
||||
<div class="filter-bar">
|
||||
<input type="text" id="searchBookings" class="form-control" style="width: 200px; font-size: 0.85rem;" placeholder="Поиск по имени/телефону..." onkeyup="debounceSearch()">
|
||||
<span class="filter-label">Фильтр:</span>
|
||||
<select id="filterStatus"><option value="all">Все статусы</option><option value="новая">Новая</option><option value="оплачена">Оплачена</option><option value="зарезервирована">Зарезервирована</option><option value="заселена">Заселена</option><option value="выехала">Выехала</option><option value="отменена">Отменена</option></select>
|
||||
<select id="filterUrgent"><option value="all">Все записи</option><option value="checkin-soon">Заезд ≤ 3 дней</option><option value="checkout-today">Выезд сегодня</option></select>
|
||||
@@ -249,6 +250,14 @@ tr.row-checkout-today { background: #fef2f2 !important; border-left: 4px solid #
|
||||
<tbody id="allBookings"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pagination-container" style="display: flex; justify-content: space-between; align-items: center; margin-top: 16px; padding-top: 16px; border-top: 1px solid #e2e8f0;">
|
||||
<div id="paginationInfo" style="font-size: 0.85rem; color: #64748b;"></div>
|
||||
<div class="pagination-controls" style="display: flex; gap: 8px;">
|
||||
<button class="btn btn-outline-secondary btn-sm" id="prevPage" onclick="changePage(-1)" disabled>← Назад</button>
|
||||
<span id="pageNumbers" style="display: flex; gap: 4px;"></span>
|
||||
<button class="btn btn-outline-secondary btn-sm" id="nextPage" onclick="changePage(1)" disabled>Вперёд →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -560,15 +569,98 @@ async function loadUsers() {
|
||||
let allBookingsData = [];
|
||||
let sortDirection = 'asc';
|
||||
let bookingsLoaded = false;
|
||||
let currentPage = 1;
|
||||
let totalPages = 1;
|
||||
let searchTimeout = null;
|
||||
|
||||
async function loadBookings() {
|
||||
if (bookingsLoaded) { renderBookings(); return; }
|
||||
async function loadBookings(resetPage = true) {
|
||||
if (resetPage) currentPage = 1;
|
||||
try {
|
||||
allBookingsData = await api('/api/admin/bookings');
|
||||
const search = document.getElementById('searchBookings').value;
|
||||
const status = document.getElementById('filterStatus').value;
|
||||
const limit = 20;
|
||||
|
||||
const params = new URLSearchParams({
|
||||
page: currentPage,
|
||||
limit: limit,
|
||||
search: search,
|
||||
status: status
|
||||
});
|
||||
|
||||
const response = await api('/api/admin/bookings?' + params.toString());
|
||||
allBookingsData = response.data;
|
||||
totalPages = response.pagination.totalPages;
|
||||
currentPage = response.pagination.page;
|
||||
|
||||
bookingsLoaded = true;
|
||||
renderBookings();
|
||||
updateBookingStats();
|
||||
} catch(err) { showToast('Нет доступа к бронированиям', 'error'); }
|
||||
updatePagination();
|
||||
} catch(err) { showToast('Нет доступа к бронированиям: ' + err.message, 'error'); }
|
||||
}
|
||||
|
||||
function updatePagination() {
|
||||
const info = document.getElementById('paginationInfo');
|
||||
const prevBtn = document.getElementById('prevPage');
|
||||
const nextBtn = document.getElementById('nextPage');
|
||||
const pageNumbers = document.getElementById('pageNumbers');
|
||||
|
||||
const total = allBookingsData.length;
|
||||
const from = (currentPage - 1) * 20 + 1;
|
||||
const to = Math.min(currentPage * 20, total);
|
||||
|
||||
info.textContent = total > 0 ? `Показано ${from}-${to} из ${total}` : 'Нет записей';
|
||||
|
||||
prevBtn.disabled = currentPage <= 1;
|
||||
nextBtn.disabled = currentPage >= totalPages;
|
||||
|
||||
// Page numbers
|
||||
let pagesHtml = '';
|
||||
const maxVisible = 5;
|
||||
let start = Math.max(1, currentPage - Math.floor(maxVisible / 2));
|
||||
let end = Math.min(totalPages, start + maxVisible - 1);
|
||||
|
||||
if (end - start < maxVisible - 1) start = Math.max(1, end - maxVisible + 1);
|
||||
|
||||
if (start > 1) {
|
||||
pagesHtml += `<button class="btn btn-outline-secondary btn-sm" onclick="goToPage(1)">1</button>`;
|
||||
if (start > 2) pagesHtml += `<span style="padding: 0 4px;">...</span>`;
|
||||
}
|
||||
|
||||
for (let i = start; i <= end; i++) {
|
||||
if (i === currentPage) {
|
||||
pagesHtml += `<button class="btn btn-primary btn-sm" onclick="goToPage(${i})">${i}</button>`;
|
||||
} else {
|
||||
pagesHtml += `<button class="btn btn-outline-secondary btn-sm" onclick="goToPage(${i})">${i}</button>`;
|
||||
}
|
||||
}
|
||||
|
||||
if (end < totalPages) {
|
||||
if (end < totalPages - 1) pagesHtml += `<span style="padding: 0 4px;">...</span>`;
|
||||
pagesHtml += `<button class="btn btn-outline-secondary btn-sm" onclick="goToPage(${totalPages})">${totalPages}</button>`;
|
||||
}
|
||||
|
||||
pageNumbers.innerHTML = pagesHtml;
|
||||
}
|
||||
|
||||
function changePage(delta) {
|
||||
const newPage = currentPage + delta;
|
||||
if (newPage >= 1 && newPage <= totalPages) {
|
||||
currentPage = newPage;
|
||||
loadBookings(false);
|
||||
}
|
||||
}
|
||||
|
||||
function goToPage(page) {
|
||||
if (page >= 1 && page <= totalPages) {
|
||||
currentPage = page;
|
||||
loadBookings(false);
|
||||
}
|
||||
}
|
||||
|
||||
function debounceSearch() {
|
||||
if (searchTimeout) clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(() => loadBookings(true), 500);
|
||||
}
|
||||
|
||||
function getDaysDiff(dateStr) {
|
||||
@@ -588,14 +680,6 @@ function renderBookings() {
|
||||
const filterStatus = document.getElementById('filterStatus').value;
|
||||
const filterUrgent = document.getElementById('filterUrgent').value;
|
||||
|
||||
if (filterStatus !== 'all') rows = rows.filter(r => r.status === filterStatus);
|
||||
|
||||
if (filterUrgent === 'checkin-soon') {
|
||||
rows = rows.filter(r => { const diff = getDaysDiff(r.checkin_date); return diff >= 0 && diff <= 3 && r.status !== 'отменена' && r.status !== 'выехала'; });
|
||||
} else if (filterUrgent === 'checkout-today') {
|
||||
rows = rows.filter(r => getDaysDiff(r.checkout_date) === 0 && r.status !== 'отменена' && r.status !== 'выехала');
|
||||
}
|
||||
|
||||
rows.sort((a, b) => {
|
||||
const da = new Date(a.checkin_date), db = new Date(b.checkin_date);
|
||||
return sortDirection === 'asc' ? da - db : db - da;
|
||||
@@ -668,8 +752,8 @@ async function changeRoom(id, room_type) {
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.getElementById('filterStatus').addEventListener('change', renderBookings);
|
||||
document.getElementById('filterUrgent').addEventListener('change', renderBookings);
|
||||
document.getElementById('filterStatus').addEventListener('change', loadBookings);
|
||||
document.getElementById('filterUrgent').addEventListener('change', loadBookings);
|
||||
document.getElementById('sortAsc').style.opacity = '1';
|
||||
document.getElementById('sortDesc').style.opacity = '0.5';
|
||||
});
|
||||
@@ -889,6 +973,10 @@ async function changeDetails(id, field, value) {
|
||||
try {
|
||||
const body = {};
|
||||
if (field === 'adults' || field === 'children') {
|
||||
if (value === '' || isNaN(parseInt(value))) {
|
||||
showToast('Введите корректное число', 'error');
|
||||
return;
|
||||
}
|
||||
body[field] = parseInt(value);
|
||||
} else {
|
||||
body[field] = value;
|
||||
@@ -897,7 +985,10 @@ async function changeDetails(id, field, value) {
|
||||
allBookingsData = allBookingsData.map(b => b.id === id ? data.booking : b);
|
||||
renderBookings(); updateBookingStats(); loadDashboard();
|
||||
showToast('Данные обновлены');
|
||||
} catch(err) { showToast(err.message, 'error'); }
|
||||
} catch(err) {
|
||||
showToast(err.message || 'Ошибка при сохранении', 'error');
|
||||
loadBookings(false);
|
||||
}
|
||||
}
|
||||
|
||||
checkAuth();
|
||||
|
||||
Reference in New Issue
Block a user