API функции и вспомогательные утилиты

This commit is contained in:
EDeev 2025-12-29 09:43:00 +03:00
parent f324ae26a8
commit 313c8ad373
2 changed files with 433 additions and 0 deletions

175
js/api.js Normal file
View file

@ -0,0 +1,175 @@
const API_CONFIG = {
baseUrl: 'http://exam-api-courses.std-900.ist.mospolytech.ru/api',
apiKey: '358a63a5-52ae-4ab0-800b-90f75ce5a5c2'
};
// Хранилище контактных данных в локальном хранилище
const ContactDataStorage = {
// Сохранить данные контакта для заявки
save(orderId, data) {
const storageKey = `order_contact_${orderId}`;
localStorage.setItem(storageKey, JSON.stringify(data));
},
// Получить данные контакта по ID заявки
get(orderId) {
const storageKey = `order_contact_${orderId}`;
const data = localStorage.getItem(storageKey);
return data ? JSON.parse(data) : null;
},
// Удалить данные контакта из хранилища
remove(orderId) {
const storageKey = `order_contact_${orderId}`;
localStorage.removeItem(storageKey);
}
};
// Добавить API-ключ к URL запроса
function addApiKey(url) {
const separator = url.includes('?') ? '&' : '?';
return `${url}${separator}api_key=${API_CONFIG.apiKey}`;
}
// Отправить запрос к API с обработкой ошибок
async function apiRequest(endpoint, method = 'GET', data = null) {
const url = addApiKey(`${API_CONFIG.baseUrl}${endpoint}`);
const options = {
method,
headers: {'Content-Type': 'application/json'}
};
if (data && (method === 'POST' || method === 'PUT')) {
options.body = JSON.stringify(data);
}
try {
const response = await fetch(url, options);
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || 'Ошибка сервера');
}
return result;
} catch (error) {
console.error('API Error:', error);
throw error;
}
}
// Загрузить список курсов и отобразить
async function loadCourses() {
try {
AppState.courses = await apiRequest('/courses');
AppState.filteredCourses = [...AppState.courses];
renderCourses();
} catch (error) {
showNotification('Ошибка загрузки курсов: ' +
error.message, 'error');
}
}
// Загрузить список репетиторов и отобразить
async function loadTutors() {
try {
AppState.tutors = await apiRequest('/tutors');
AppState.filteredTutors = [...AppState.tutors];
populateLanguageFilter();
renderTutors();
} catch (error) {
showNotification('Ошибка загрузки репетиторов: ' +
error.message, 'error');
}
}
// Загрузить список заявок и обновить интерфейс
async function loadOrders() {
try {
AppState.orders = await apiRequest('/orders');
renderOrders();
updateUserInfo();
} catch (error) {
showNotification('Ошибка загрузки заявок: ' +
error.message, 'error');
}
}
// Создать новую заявку и сохранить контакты
async function createOrder(orderData, contactData) {
try {
const result = await apiRequest('/orders', 'POST', orderData);
ContactDataStorage.save(result.id, contactData);
showNotification('Заявка успешно создана!', 'success');
return result;
} catch (error) {
showNotification('Ошибка создания заявки: ' +
error.message, 'error');
throw error;
}
}
// Обновить существующую заявку и контакты
async function updateOrder(orderId, orderData, contactData) {
try {
const result = await apiRequest(`/orders/${orderId}`,
'PUT', orderData);
if (contactData) {
ContactDataStorage.save(orderId, contactData);
}
showNotification('Заявка успешно обновлена!', 'success');
return result;
} catch (error) {
showNotification('Ошибка обновления заявки: ' +
error.message, 'error');
throw error;
}
}
// Удалить заявку и её контактные данные
async function deleteOrder(orderId) {
try {
const result = await apiRequest(`/orders/${orderId}`, 'DELETE');
ContactDataStorage.remove(orderId);
showNotification('Заявка успешно удалена!', 'success');
return result;
} catch (error) {
showNotification('Ошибка удаления заявки: ' +
error.message, 'error');
throw error;
}
}
// Получить заявку по ID
async function getOrder(orderId) {
try {
return await apiRequest(`/orders/${orderId}`);
} catch (error) {
showNotification('Ошибка получения заявки: ' +
error.message, 'error');
throw error;
}
}
// Получить курс по ID
async function getCourse(courseId) {
try {
return await apiRequest(`/courses/${courseId}`);
} catch (error) {
showNotification('Ошибка получения курса: ' +
error.message, 'error');
throw error;
}
}
// Получить репетитора по ID
async function getTutor(tutorId) {
try {
return await apiRequest(`/tutors/${tutorId}`);
} catch (error) {
showNotification('Ошибка получения репетитора: ' +
error.message, 'error');
throw error;
}
}

258
js/utils.js Normal file
View file

@ -0,0 +1,258 @@
// Показать уведомление с автоматическим удалением
function showNotification(message, type = 'info') {
const notificationArea = document.getElementById('notificationArea');
if (!notificationArea) return;
const alertTypes = {
'success': 'alert-success',
'error': 'alert-danger',
'warning': 'alert-warning',
'info': 'alert-info'
};
const icons = {
'success': 'bi-check-circle-fill',
'error': 'bi-exclamation-circle-fill',
'warning': 'bi-exclamation-triangle-fill',
'info': 'bi-info-circle-fill'
};
const alertClass = alertTypes[type] || 'alert-info';
const iconClass = icons[type] || 'bi-info-circle-fill';
const notification = document.createElement('div');
notification.className = `alert ${alertClass} alert-dismissible
fade show notification`;
notification.innerHTML = `
<i class="bi ${iconClass} me-2"></i>
${message}
<button type="button" class="btn-close"
data-bs-dismiss="alert"></button>
`;
notificationArea.appendChild(notification);
// Удалить уведомление через 5 секунд
setTimeout(() => {
notification.remove();
}, 5000);
}
// Форматировать цену в рублях
function formatPrice(price) {
return new Intl.NumberFormat('ru-RU').format(price) + ' ₽';
}
// Форматировать дату в формате ДД.ММ.ГГГГ
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('ru-RU', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
});
}
// Форматировать дату и время
function formatDateTime(dateString) {
const date = new Date(dateString);
return date.toLocaleString('ru-RU', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
// Обрезать время до формата ЧЧ:ММ
function formatTime(timeString) {
return timeString.substring(0, 5);
}
// Форматировать дату и время без секунд
function formatDateTimeWithoutSeconds(dateString, timeString) {
const date = new Date(dateString);
const formattedDate = date.toLocaleDateString('ru-RU', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
});
const formattedTime = formatTime(timeString);
return `${formattedDate} ${formattedTime}`;
}
// Склонение числительных по русским правилам
function pluralize(number, one, few, many) {
const mod10 = number % 10;
const mod100 = number % 100;
if (mod10 === 1 && mod100 !== 11) {
return `${number} ${one}`;
} else if (mod10 >= 2 && mod10 <= 4 &&
(mod100 < 10 || mod100 >= 20)) {
return `${number} ${few}`;
} else {
return `${number} ${many}`;
}
}
// Получить CSS класс для бейджа уровня
function getLevelBadgeClass(level) {
const levelMap = {
'Beginner': 'beginner',
'Intermediate': 'intermediate',
'Advanced': 'advanced'
};
return levelMap[level] || 'beginner';
}
// Перевести уровень на русский язык
function getLevelText(level) {
const levelMap = {
'Beginner': 'Начальный',
'Intermediate': 'Средний',
'Advanced': 'Продвинутый'
};
return levelMap[level] || level;
}
// Проверить является ли день выходным
function isWeekend(date) {
const day = date.getDay();
return day === 0 || day === 6;
}
// Проверить является ли дата праздничным днём
function isHoliday(date) {
// Праздничные дни в России на 2025-2026 годы
const holidays = [
// 2025
'2025-01-01', '2025-01-02', '2025-01-03', '2025-01-04',
'2025-01-05', '2025-01-06', '2025-01-07', '2025-01-08',
'2025-02-23', '2025-03-08', '2025-05-01', '2025-05-09',
'2025-06-12', '2025-11-04',
// 2026
'2026-01-01', '2026-01-02', '2026-01-03', '2026-01-04',
'2026-01-05', '2026-01-06', '2026-01-07', '2026-01-08',
'2026-02-23', '2026-03-08', '2026-05-01', '2026-05-09',
'2026-06-12', '2026-11-04'
];
const dateStr = date.toISOString().split('T')[0];
return holidays.includes(dateStr);
}
// Получить множитель цены для выходных
function getWeekendMultiplier(dateString) {
const date = new Date(dateString);
return (isWeekend(date) || isHoliday(date)) ? 1.5 : 1;
}
// Рассчитать надбавку за время занятия
function getTimeSurcharge(timeString) {
const hour = parseInt(timeString.split(':')[0]);
let surcharge = 0;
// Утренняя надбавка
if (hour >= 9 && hour < 12) {
surcharge += 400;
}
// Вечерняя надбавка
if (hour >= 18 && hour < 20) {
surcharge += 1000;
}
return surcharge;
}
// Рассчитать дату окончания курса
function calculateEndDate(startDateStr, weeks) {
const date = new Date(startDateStr);
date.setDate(date.getDate() + (weeks * 7));
return date;
}
// Отформатировать дату окончания курса
function formatEndDate(startDateStr, weeks) {
const endDate = calculateEndDate(startDateStr, weeks);
return formatDate(endDate.toISOString().split('T')[0]);
}
// Проверить ранняя ли это регистрация
function isEarlyRegistration(startDate) {
const today = new Date();
const start = new Date(startDate);
const diffTime = start - today;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays >= 30;
}
// Задержка выполнения функции для оптимизации
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
function generatePagination(currentPage, totalPages, containerId) {
const container = document.getElementById(containerId);
if (!container) return;
container.innerHTML = '';
if (totalPages <= 1) return;
const ul = document.createElement('ul');
ul.className = 'pagination justify-content-center';
const createPageItem = (page, text, disabled = false,
active = false) => {
const li = document.createElement('li');
li.className = `page-item ${disabled ? 'disabled' : ''}
${active ? 'active' : ''}`;
const a = document.createElement('a');
a.className = 'page-link';
a.href = '#';
a.textContent = text || page;
if (!disabled) {
a.addEventListener('click', (e) => {
e.preventDefault();
return page;
});
}
li.appendChild(a);
return li;
};
ul.appendChild(createPageItem(currentPage - 1, 'Назад',
currentPage === 1));
for (let i = 1; i <= totalPages; i++) {
if (i === 1 || i === totalPages ||
(i >= currentPage - 1 && i <= currentPage + 1)) {
ul.appendChild(createPageItem(i, i, false,
i === currentPage));
} else if (i === currentPage - 2 || i === currentPage + 2) {
const li = document.createElement('li');
li.className = 'page-item disabled';
li.innerHTML = '<span class="page-link">...</span>';
ul.appendChild(li);
}
}
ul.appendChild(createPageItem(currentPage + 1, 'Вперед',
currentPage === totalPages));
container.appendChild(ul);
}