commit 12.01

This commit is contained in:
Владимир
2026-01-12 14:25:15 +00:00
parent 36084ba590
commit ae5ab2554b
26 changed files with 1116 additions and 1083 deletions

View File

@@ -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>