diff --git a/js/account.js b/js/account.js new file mode 100644 index 0000000..0b1da3f --- /dev/null +++ b/js/account.js @@ -0,0 +1,726 @@ +const AccountState = { + orders: [], + courses: [], + tutors: [], + currentOrderPage: 1, + ordersPerPage: 5, + editingOrder: null +}; + +document.addEventListener('DOMContentLoaded', () => { + initAccount(); +}); + +// Инициализировать страницу личного кабинета +async function initAccount() { + await loadAccountData(); + setupAccountEventListeners(); +} + +// Загрузить данные аккаунта с сервера +async function loadAccountData() { + try { + AccountState.orders = await apiRequest('/orders'); + AccountState.courses = await apiRequest('/courses'); + AccountState.tutors = await apiRequest('/tutors'); + renderOrders(); + updateOrdersCount(); + } catch (error) { + showNotification('Ошибка загрузки данных: ' + + error.message, 'error'); + } +} + +// Настроить обработчики событий для страницы +function setupAccountEventListeners() { + const saveOrderChanges = document.getElementById('saveOrderChanges'); + if (saveOrderChanges) { + saveOrderChanges.addEventListener('click', saveOrderEdit); + } + + const confirmDelete = document.getElementById('confirmDelete'); + if (confirmDelete) { + confirmDelete.addEventListener('click', confirmOrderDeletion); + } + + const editCourseStartDate = + document.getElementById('editCourseStartDate'); + if (editCourseStartDate) { + editCourseStartDate.addEventListener('change', + updateEditCourseTimeSlots); + } + + const editCourseStartTime = + document.getElementById('editCourseStartTime'); + if (editCourseStartTime) { + editCourseStartTime.addEventListener('change', + calculateEditCoursePrice); + } + + const editStudentsNumber = + document.getElementById('editStudentsNumber'); + if (editStudentsNumber) { + editStudentsNumber.addEventListener('input', + calculateEditCoursePrice); + } + + const editCheckboxes = ['editSupplementary', 'editPersonalized', + 'editExcursions', 'editAssessment', + 'editInteractive']; + editCheckboxes.forEach(id => { + const checkbox = document.getElementById(id); + if (checkbox) { + checkbox.addEventListener('change', + calculateEditCoursePrice); + } + }); + +} + +function updateOrdersCount() { + const countBadge = document.getElementById('ordersCount'); + if (!countBadge) return; + + const count = AccountState.orders.length; + countBadge.textContent = pluralize(count, 'заявка', + 'заявки', 'заявок'); +} + +// Отрисовать таблицу заявок +function renderOrders() { + const emptyState = document.getElementById('emptyState'); + const ordersTable = document.getElementById('ordersTable'); + const tbody = document.getElementById('ordersList'); + + if (AccountState.orders.length === 0) { + emptyState.style.display = 'block'; + ordersTable.style.display = 'none'; + return; + } + + emptyState.style.display = 'none'; + ordersTable.style.display = 'block'; + + tbody.innerHTML = ''; + + const startIdx = (AccountState.currentOrderPage - 1) * + AccountState.ordersPerPage; + const endIdx = startIdx + AccountState.ordersPerPage; + const ordersToShow = AccountState.orders.slice(startIdx, endIdx); + + ordersToShow.forEach((order, index) => { + const tr = document.createElement('tr'); + const orderNumber = startIdx + index + 1; + + let orderName = ''; + if (order.course_id && order.course_id > 0) { + const course = AccountState.courses.find( + c => c.id === order.course_id + ); + orderName = course ? course.name : + `Курс #${order.course_id}`; + } else if (order.tutor_id && order.tutor_id > 0) { + const tutor = AccountState.tutors.find( + t => t.id === order.tutor_id + ); + orderName = tutor ? `Репетитор: ${tutor.name}` : + `Репетитор #${order.tutor_id}`; + } + + const orderDate = formatDateTimeWithoutSeconds( + order.date_start, + order.time_start + ); + + tr.innerHTML = ` + ${orderNumber} + ${orderName} + ${orderDate} + ${formatPrice(order.price)} + +
+ + + +
+ + `; + + tbody.appendChild(tr); + }); + + document.querySelectorAll('.view-order-btn').forEach(btn => { + btn.addEventListener('click', (e) => { + const orderId = parseInt(e.currentTarget + .getAttribute('data-order-id')); + viewOrderDetails(orderId); + }); + }); + + document.querySelectorAll('.edit-order-btn').forEach(btn => { + btn.addEventListener('click', (e) => { + const orderId = parseInt(e.currentTarget + .getAttribute('data-order-id')); + editOrder(orderId); + }); + }); + + document.querySelectorAll('.delete-order-btn').forEach(btn => { + btn.addEventListener('click', (e) => { + const orderId = parseInt(e.currentTarget + .getAttribute('data-order-id')); + showDeleteConfirmation(orderId); + }); + }); + + renderOrdersPagination(); +} + +function renderOrdersPagination() { + const container = document.getElementById('ordersPagination'); + if (!container) return; + + container.innerHTML = ''; + + const totalPages = Math.ceil(AccountState.orders.length / + AccountState.ordersPerPage); + + if (totalPages <= 1) return; + + const createPageItem = (page, text, disabled = false) => { + const li = document.createElement('li'); + li.className = `page-item ${disabled ? 'disabled' : ''} + ${page === AccountState.currentOrderPage ? + 'active' : ''}`; + + const a = document.createElement('a'); + a.className = 'page-link'; + a.href = '#'; + a.textContent = text || page; + + if (!disabled) { + a.addEventListener('click', (e) => { + e.preventDefault(); + AccountState.currentOrderPage = page; + renderOrders(); + }); + } + + li.appendChild(a); + return li; + }; + + container.appendChild( + createPageItem(AccountState.currentOrderPage - 1, 'Назад', + AccountState.currentOrderPage === 1) + ); + + for (let i = 1; i <= totalPages; i++) { + container.appendChild(createPageItem(i, i, false)); + } + + container.appendChild( + createPageItem(AccountState.currentOrderPage + 1, 'Вперед', + AccountState.currentOrderPage === totalPages) + ); +} + +// Показать детальную информацию о заявке +function viewOrderDetails(orderId) { + const order = AccountState.orders.find(o => o.id === orderId); + if (!order) return; + + const modal = new bootstrap.Modal( + document.getElementById('orderDetailModal') + ); + const content = document.getElementById('orderDetailContent'); + + let orderTitle = ''; + let teacherInfo = ''; + + if (order.course_id && order.course_id > 0) { + const course = AccountState.courses.find( + c => c.id === order.course_id + ); + if (course) { + orderTitle = course.name; + teacherInfo = `

Преподаватель: + ${course.teacher}

`; + } + } else if (order.tutor_id && order.tutor_id > 0) { + const tutor = AccountState.tutors.find( + t => t.id === order.tutor_id + ); + if (tutor) { + orderTitle = `Репетитор: ${tutor.name}`; + } + } + + const options = []; + if (order.early_registration) + options.push('Ранняя регистрация (-10%)'); + if (order.group_enrollment) + options.push('Групповая скидка (-15%)'); + if (order.intensive_course) + options.push('Интенсивный курс (+20%)'); + if (order.supplementary) + options.push('Дополнительные учебные материалы (+2000₽/студ.)'); + if (order.personalized) + options.push('Индивидуальные занятия (+1500₽/нед.)'); + if (order.excursions) + options.push('Культурные экскурсии (+25%)'); + if (order.assessment) + options.push('Оценка уровня владения языком (+300₽)'); + if (order.interactive) + options.push('Доступ к интерактивной онлайн-платформе (+50%)'); + + const optionsBadges = options.map(opt => + `${opt}` + ).join(''); + + const contactData = ContactDataStorage.get(order.id); + let contactInfo = ''; + + if (contactData) { + contactInfo = ` +
+
Контактные данные:
+

Имя: ${contactData.name}

+

Телефон: ${contactData.phone}

+

Email: ${contactData.email}

+ ${contactData.message ? + `

Сообщение: ${contactData.message}

` + : ''} + `; + } + + let detailsHTML = `
${orderTitle}
${teacherInfo}`; + let priceLabel = ''; + + if (order.course_id && order.course_id > 0) { + detailsHTML += ` +

Дата: + ${formatDate(order.date_start)}

+

Время: ${formatTime(order.time_start)}

+

Продолжительность: + ${order.duration} часов

+

Количество студентов: + ${order.persons}

+ ${options.length > 0 ? + `

Выбранные опции:
${optionsBadges}

` + : ''} + `; + priceLabel = `Итоговая стоимость: ${formatPrice(order.price)}`; + } else if (order.tutor_id && order.tutor_id > 0) { + detailsHTML += ` +

Заявка на индивидуальные занятия с репетитором

+ `; + priceLabel = `Стоимость: ${formatPrice(order.price)}/час`; + } + + detailsHTML += ` + ${contactInfo} +
+
${priceLabel}
+ `; + + content.innerHTML = detailsHTML; + + modal.show(); +} + +// Открыть модальное окно редактирования заявки +function editOrder(orderId) { + const order = AccountState.orders.find(o => o.id === orderId); + if (!order) return; + + AccountState.editingOrder = order; + + const modal = new bootstrap.Modal( + document.getElementById('editOrderModal') + ); + + document.getElementById('editOrderId').value = order.id; + + const editCourseFields = + document.getElementById('editCourseFields'); + const editTutorFields = + document.getElementById('editTutorFields'); + + const contactData = ContactDataStorage.get(order.id); + if (contactData) { + document.getElementById('editStudentName').value = + contactData.name || ''; + document.getElementById('editStudentPhone').value = + contactData.phone || ''; + document.getElementById('editStudentEmail').value = + contactData.email || ''; + document.getElementById('editStudentMessage').value = + contactData.message || ''; + } + + if (order.course_id && order.course_id > 0) { + editCourseFields.style.display = 'block'; + editTutorFields.style.display = 'none'; + + const course = AccountState.courses.find( + c => c.id === order.course_id + ); + if (course) { + document.getElementById('editCourseId').value = + course.id; + document.getElementById('editCourseName').value = + course.name; + document.getElementById('editCourseTeacher').value = + course.teacher; + + // Получить уникальные даты + const uniqueDates = [...new Set(course.start_dates.map(dt => dt.split('T')[0]))]; + + const startDateSelect = document.getElementById('editCourseStartDate'); + startDateSelect.innerHTML = ''; + + uniqueDates.forEach(dateStr => { + const option = document.createElement('option'); + option.value = dateStr; + option.textContent = formatDate(dateStr); + startDateSelect.appendChild(option); + }); + + // Установить текущую дату и время заказа + startDateSelect.value = order.date_start; + + updateEditCourseTimeSlots(); + + // Установить время после обновления списка времён + const timeSelect = document.getElementById('editCourseStartTime'); + if (order.time_start && timeSelect) { + timeSelect.value = order.time_start; + } + + const durationText = `${course.total_length} недель, + ${course.week_length} ч/нед`; + document.getElementById('editCourseDuration').value = + durationText; + + document.getElementById('editStudentsNumber').value = + order.persons; + + document.getElementById('editEarlyRegistration').checked = + order.early_registration; + document.getElementById('editGroupEnrollment').checked = + order.group_enrollment; + document.getElementById('editIntensiveCourse').checked = + order.intensive_course; + + document.getElementById('editSupplementary').checked = + order.supplementary; + document.getElementById('editPersonalized').checked = + order.personalized; + document.getElementById('editExcursions').checked = + order.excursions; + document.getElementById('editAssessment').checked = + order.assessment; + document.getElementById('editInteractive').checked = + order.interactive; + } + } else if (order.tutor_id && order.tutor_id > 0) { + editCourseFields.style.display = 'none'; + editTutorFields.style.display = 'block'; + + const tutor = AccountState.tutors.find( + t => t.id === order.tutor_id + ); + if (tutor) { + document.getElementById('editTutorId').value = tutor.id; + document.getElementById('editTutorName').value = tutor.name; + } + } + + modal.show(); + + setTimeout(() => { + if (order.course_id && order.course_id > 0) { + calculateEditCoursePrice(); + } else if (order.tutor_id && order.tutor_id > 0) { + const tutor = AccountState.tutors.find( + t => t.id === order.tutor_id + ); + const price = tutor ? tutor.price_per_hour : order.price; + document.getElementById('editTotalPrice').textContent = price; + } + }, 100); +} + +function updateEditCourseTimeSlots() { + const selectedDate = + document.getElementById('editCourseStartDate').value; + const timeSelect = document.getElementById('editCourseStartTime'); + + if (!selectedDate) { + timeSelect.disabled = true; + timeSelect.innerHTML = + ''; + document.getElementById('editCourseEndDate').value = ''; + return; + } + + timeSelect.disabled = false; + timeSelect.innerHTML = ''; + + const courseId = parseInt( + document.getElementById('editCourseId').value + ); + const course = AccountState.courses.find(c => c.id === courseId); + + if (course) { + const duration = course.week_length; + + // Фильтруем времена для выбранной даты + const timesForDate = course.start_dates + .filter(dt => dt.split('T')[0] === selectedDate) + .map(dt => dt.split('T')[1].substring(0, 5)); + + timesForDate.forEach(timeStr => { + const startHour = parseInt(timeStr.split(':')[0]); + const startMinute = parseInt(timeStr.split(':')[1]); + const endHour = startHour + duration; + const endTimeStr = `${String(endHour).padStart(2, '0')}:${String(startMinute).padStart(2, '0')}`; + + const surcharge = getTimeSurcharge(timeStr); + let surchargeText = surcharge > 0 ? ` (+${surcharge}₽)` : ''; + + const option = document.createElement('option'); + option.value = timeStr; + option.textContent = `${timeStr} - ${endTimeStr}${surchargeText}`; + timeSelect.appendChild(option); + }); + + // Устанавливаем дату окончания курса + const endDate = formatEndDate(selectedDate, course.total_length); + document.getElementById('editCourseEndDate').value = endDate; + } + + calculateEditCoursePrice(); +} + +// Рассчитать итоговую цену при редактировании +function calculateEditCoursePrice() { + const courseId = parseInt( + document.getElementById('editCourseId').value + ); + const course = AccountState.courses.find(c => c.id === courseId); + + if (!course) return; + + const startDate = + document.getElementById('editCourseStartDate').value; + const startTime = + document.getElementById('editCourseStartTime').value; + const persons = parseInt( + document.getElementById('editStudentsNumber').value + ) || 1; + + if (!startDate || !startTime) { + document.getElementById('editTotalPrice').textContent = '0'; + document.getElementById('editCourseEndDate').value = ''; + return; + } + + // Устанавливаем дату окончания курса + const endDate = formatEndDate(startDate, course.total_length); + document.getElementById('editCourseEndDate').value = endDate; + + const courseFeePerHour = course.course_fee_per_hour; + const totalHours = course.total_length * course.week_length; + const weekendMultiplier = getWeekendMultiplier(startDate); + const timeSurcharge = getTimeSurcharge(startTime); + + let basePrice = (courseFeePerHour * totalHours * + weekendMultiplier + timeSurcharge) * persons; + + const earlyReg = isEarlyRegistration(startDate); + const groupEnroll = persons >= 5; + const intensive = course.week_length > 20; + + document.getElementById('editEarlyRegistration').checked = earlyReg; + document.getElementById('editGroupEnrollment').checked = + groupEnroll; + document.getElementById('editIntensiveCourse').checked = intensive; + + if (earlyReg) basePrice *= 0.9; + if (groupEnroll) basePrice *= 0.85; + if (intensive) basePrice *= 1.2; + + if (document.getElementById('editSupplementary').checked) { + basePrice += 2000 * persons; + } + + if (document.getElementById('editPersonalized').checked) { + basePrice += 1500 * course.total_length; + } + + if (document.getElementById('editExcursions').checked) { + basePrice *= 1.25; + } + + if (document.getElementById('editAssessment').checked) { + basePrice += 300; + } + + if (document.getElementById('editInteractive').checked) { + basePrice *= 1.5; + } + + document.getElementById('editTotalPrice').textContent = + Math.round(basePrice); +} + +// Сохранить изменения заявки на сервер +async function saveOrderEdit() { + const form = document.getElementById('editOrderForm'); + if (!form.checkValidity()) { + form.reportValidity(); + return; + } + + const orderId = parseInt( + document.getElementById('editOrderId').value + ); + const order = AccountState.editingOrder; + + const contactData = { + name: document.getElementById('editStudentName').value, + phone: document.getElementById('editStudentPhone').value, + email: document.getElementById('editStudentEmail').value, + message: document.getElementById('editStudentMessage').value + }; + + let orderData = { + price: parseInt( + document.getElementById('editTotalPrice').textContent + ) + }; + + if (order.course_id && order.course_id > 0) { + const courseId = parseInt( + document.getElementById('editCourseId').value + ); + const course = AccountState.courses.find( + c => c.id === courseId + ); + + const startDate = + document.getElementById('editCourseStartDate').value + .split('T')[0]; + const startTime = + document.getElementById('editCourseStartTime').value; + const persons = parseInt( + document.getElementById('editStudentsNumber').value + ); + + orderData = { + ...orderData, + course_id: courseId, + tutor_id: 0, + date_start: startDate, + time_start: startTime, + duration: course.total_length * course.week_length, + persons: persons, + early_registration: + document.getElementById('editEarlyRegistration').checked, + group_enrollment: + document.getElementById('editGroupEnrollment').checked, + intensive_course: + document.getElementById('editIntensiveCourse').checked, + supplementary: + document.getElementById('editSupplementary').checked, + personalized: + document.getElementById('editPersonalized').checked, + excursions: + document.getElementById('editExcursions').checked, + assessment: + document.getElementById('editAssessment').checked, + interactive: + document.getElementById('editInteractive').checked + }; + } else if (order.tutor_id && order.tutor_id > 0) { + const tutorId = parseInt( + document.getElementById('editTutorId').value + ); + const tutor = AccountState.tutors.find(t => t.id === tutorId); + + // Используем текущую дату как плейсхолдер или сохраняем существующую + const dateStr = order.date_start || new Date().toISOString().split('T')[0]; + const timeStr = order.time_start || '10:00'; + + orderData = { + ...orderData, + tutor_id: tutorId, + course_id: 0, + date_start: dateStr, + time_start: timeStr, + duration: 1, + persons: 1, + price: tutor ? tutor.price_per_hour : order.price, + early_registration: false, + group_enrollment: false, + intensive_course: false, + supplementary: false, + personalized: false, + excursions: false, + assessment: false, + interactive: false + }; + } + + try { + await updateOrder(orderId, orderData, contactData); + const modal = bootstrap.Modal.getInstance( + document.getElementById('editOrderModal') + ); + modal.hide(); + await loadAccountData(); + } catch (error) { + console.error('Error updating order:', error); + } +} + +function showDeleteConfirmation(orderId) { + const modal = new bootstrap.Modal( + document.getElementById('deleteOrderModal') + ); + document.getElementById('deleteOrderId').value = orderId; + modal.show(); +} + +// Подтвердить удаление заявки +async function confirmOrderDeletion() { + const orderId = parseInt( + document.getElementById('deleteOrderId').value + ); + + try { + await deleteOrder(orderId); + const modal = bootstrap.Modal.getInstance( + document.getElementById('deleteOrderModal') + ); + modal.hide(); + await loadAccountData(); + } catch (error) { + console.error('Error deleting order:', error); + } +} + +function updateUserInfo() { + updateOrdersCount(); +} \ No newline at end of file