commit 12.01
This commit is contained in:
@@ -142,11 +142,8 @@
|
||||
<label>Длительность</label>
|
||||
<input type="text" id="duration" readonly placeholder="Выберите услугу">
|
||||
</div>
|
||||
</div>
|
||||
<button class="book-btn" id="bookBtn" onclick="bookService()" disabled>
|
||||
Выбрать время →
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<!-- Календарь -->
|
||||
<div class="calendar-section">
|
||||
@@ -173,46 +170,242 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let services = [];
|
||||
let selectedServiceId = null;
|
||||
let selectedDate = null;
|
||||
let currentMonth = new Date().getMonth();
|
||||
let currentYear = new Date().getFullYear();
|
||||
let availableSlots = [];
|
||||
let token = localStorage.getItem('token');
|
||||
let services = [];
|
||||
let selectedServiceId = null;
|
||||
let selectedDate = null;
|
||||
let currentMonth = new Date().getMonth();
|
||||
let currentYear = new Date().getFullYear();
|
||||
let availableSlots = [];
|
||||
let token = localStorage.getItem('token');
|
||||
let selectedSlot = null;
|
||||
|
||||
// Проверка авторизации
|
||||
if (!token) {
|
||||
window.location.href = 'register-login.html';
|
||||
throw new Error('Нужна авторизация');
|
||||
}
|
||||
updateHeader();
|
||||
// Проверка авторизации
|
||||
if (!token) {
|
||||
window.location.href = 'register-login.html';
|
||||
}
|
||||
|
||||
// 1. Загрузить услуги при загрузке страницы
|
||||
window.onload = async function() {
|
||||
await loadServices();
|
||||
initCalendar();
|
||||
};
|
||||
// Загрузка страницы
|
||||
window.onload = async function() {
|
||||
// Убедимся, что элементы существуют
|
||||
if (!document.getElementById('serviceSelect')) {
|
||||
console.error('Элемент #serviceSelect не найден');
|
||||
return;
|
||||
}
|
||||
|
||||
// Загрузка услуг из API (только isactive=true)
|
||||
async function loadServices() {
|
||||
try {
|
||||
const response = await fetch('/api/admin/services', {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
services = data.filter(service => service.isactive);
|
||||
const select = document.getElementById('serviceSelect');
|
||||
|
||||
select.innerHTML = '<option value="">Выберите услугу</option>';
|
||||
for (let service of services) {
|
||||
select.innerHTML += `<option value="${service.id}" data-duration="${service.durationminutes}">${service.name} (${service.durationminutes} мин) - ${service.price}₽</option>`;
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Ошибка загрузки услуг');
|
||||
try {
|
||||
await loadServices();
|
||||
initCalendar();
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки:', error);
|
||||
alert('Не удалось загрузить данные');
|
||||
}
|
||||
};
|
||||
|
||||
// ✅ 1. ЗАГРУЗКА УСЛУГ (публичный API)
|
||||
async function loadServices() {
|
||||
try {
|
||||
const response = await fetch('/api/services');
|
||||
const data = await response.json();
|
||||
|
||||
services = data.filter(service => service.is_active); // ← is_active, не isactive
|
||||
|
||||
const select = document.getElementById('serviceSelect');
|
||||
select.innerHTML = '<option value="">Выберите услугу</option>';
|
||||
|
||||
for (let service of services) {
|
||||
const option = document.createElement('option');
|
||||
option.value = service.id;
|
||||
option.textContent = `${service.name} (${service.duration_minutes} мин) - ${service.price}₽`;
|
||||
select.appendChild(option);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error('Ошибка загрузки услуг: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. При выборе услуги — показываем длительность
|
||||
document.getElementById('serviceSelect').onchange = function() {
|
||||
const serviceId = this.value;
|
||||
if (!serviceId) {
|
||||
document.getElementById('duration').value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// Изменение услуги
|
||||
//document.getElementById('serviceSelect').onchange = function()//
|
||||
selectedServiceId = parseInt(serviceId);
|
||||
const service = services.find(s => s.id == serviceId);
|
||||
document.getElementById('duration').value = service ? service.duration_minutes + ' минут' : '';
|
||||
};
|
||||
|
||||
// 3. Календарь
|
||||
function initCalendar() {
|
||||
renderCalendar();
|
||||
}
|
||||
|
||||
function renderCalendar() {
|
||||
const monthYear = document.getElementById('monthYear');
|
||||
const calendarDays = document.getElementById('calendarDays');
|
||||
|
||||
if (!monthYear || !calendarDays) {
|
||||
console.error('Элементы календаря не найдены');
|
||||
return;
|
||||
}
|
||||
|
||||
const months = ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'];
|
||||
monthYear.textContent = `${months[currentMonth]} ${currentYear}`;
|
||||
|
||||
calendarDays.innerHTML = '';
|
||||
|
||||
const firstDay = new Date(currentYear, currentMonth, 1).getDay();
|
||||
const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
|
||||
|
||||
// Добавляем "серые" дни предыдущего месяца
|
||||
for (let i = 0; i < (firstDay === 0 ? 6 : firstDay - 1); i++) {
|
||||
const dayEl = document.createElement('div');
|
||||
dayEl.className = 'calendar-day other-month';
|
||||
calendarDays.appendChild(dayEl);
|
||||
}
|
||||
|
||||
// Добавляем дни текущего месяца
|
||||
const today = new Date();
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
const dayEl = document.createElement('div');
|
||||
dayEl.className = 'calendar-day';
|
||||
dayEl.textContent = day;
|
||||
|
||||
const dateStr = `${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
||||
const dateObj = new Date(dateStr);
|
||||
|
||||
if (dateObj < today.setHours(0,0,0,0)) {
|
||||
dayEl.classList.add('disabled');
|
||||
} else {
|
||||
dayEl.onclick = () => selectDate(dateStr);
|
||||
}
|
||||
|
||||
calendarDays.appendChild(dayEl);
|
||||
}
|
||||
}
|
||||
|
||||
function prevMonth() {
|
||||
if (currentMonth === 0) {
|
||||
currentMonth = 11;
|
||||
currentYear--;
|
||||
} else {
|
||||
currentMonth--;
|
||||
}
|
||||
renderCalendar();
|
||||
}
|
||||
|
||||
function nextMonth() {
|
||||
if (currentMonth === 11) {
|
||||
currentMonth = 0;
|
||||
currentYear++;
|
||||
} else {
|
||||
currentMonth++;
|
||||
}
|
||||
renderCalendar();
|
||||
}
|
||||
|
||||
// 4. Выбор даты → загрузка слотов
|
||||
async function selectDate(dateStr) {
|
||||
if (!selectedServiceId) {
|
||||
alert('Сначала выберите услугу');
|
||||
return;
|
||||
}
|
||||
|
||||
selectedDate = dateStr;
|
||||
document.querySelectorAll('.calendar-day').forEach(el => el.classList.remove('selected'));
|
||||
event.target.classList.add('selected');
|
||||
|
||||
try {
|
||||
const url = `/api/availability?service_id=${selectedServiceId}&date=${dateStr}`;
|
||||
const response = await fetch(url);
|
||||
availableSlots = await response.json();
|
||||
|
||||
showSlots(availableSlots);
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки слотов:', error);
|
||||
alert('Не удалось загрузить доступное время');
|
||||
}
|
||||
}
|
||||
|
||||
function showSlots(slots) {
|
||||
const slotsSection = document.getElementById('slotsSection');
|
||||
const slotsGrid = document.getElementById('slotsGrid');
|
||||
const confirmBtn = document.getElementById('confirmBookingBtn');
|
||||
|
||||
slotsSection.style.display = 'block';
|
||||
slotsGrid.innerHTML = '';
|
||||
|
||||
if (slots.length === 0) {
|
||||
slotsGrid.innerHTML = '<div class="no-slots">В этот день нет доступных слотов</div>';
|
||||
confirmBtn.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
selectedSlot = null; // ← Сбросьте перед новым выбором
|
||||
|
||||
slots.forEach(slot => {
|
||||
const slotEl = document.createElement('div');
|
||||
slotEl.className = 'slot';
|
||||
slotEl.textContent = `${slot.start} - ${slot.end}`;
|
||||
slotEl.onclick = () => {
|
||||
document.querySelectorAll('.slot').forEach(s => s.classList.remove('selected'));
|
||||
slotEl.classList.add('selected');
|
||||
selectedSlot = slot; // ← Присвойте значение
|
||||
confirmBtn.style.display = 'block';
|
||||
};
|
||||
slotsGrid.appendChild(slotEl);
|
||||
});
|
||||
}
|
||||
|
||||
// 5. Подтверждение бронирования
|
||||
async function confirmBooking() {
|
||||
if (!selectedServiceId || !selectedDate || !selectedSlot) {
|
||||
alert('Выберите всё: услугу, дату и время');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/bookings', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
service_id: selectedServiceId,
|
||||
employee_id: selectedSlot.employee_id,
|
||||
date: selectedDate,
|
||||
start_time: selectedSlot.start
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert('✅ Бронирование успешно создано!');
|
||||
window.location.href = 'my-bookings.html';
|
||||
} else {
|
||||
const err = await response.json();
|
||||
alert('Ошибка: ' + (err.message || 'Не удалось создать бронь'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка бронирования:', error);
|
||||
alert('Ошибка сети');
|
||||
}
|
||||
}
|
||||
|
||||
function updateHeader() {
|
||||
if (token) {
|
||||
document.getElementById('profileBtn').style.display = 'inline-block';
|
||||
document.getElementById('logoutBtn').style.display = 'inline-block';
|
||||
}
|
||||
}
|
||||
|
||||
function logout() {
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = 'index.html';
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user