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