// API конфигурация const API_BASE_URL = 'http://localhost:8000/api/v1'; // Класс для работы с API class API { constructor(baseUrl) { this.baseUrl = baseUrl; } // Общий метод для HTTP запросов async request(endpoint, options = {}) { const url = `${this.baseUrl}${endpoint}`; const config = { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options }; try { const response = await fetch(url, config); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.detail || `HTTP error! status: ${response.status}`); } // Для некоторых запросов может быть пустой ответ const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } return response; } catch (error) { console.error('API request failed:', error); throw error; } } // GET запрос async get(endpoint, params = {}) { const queryString = new URLSearchParams(params).toString(); const url = queryString ? `${endpoint}?${queryString}` : endpoint; return this.request(url); } // POST запрос async post(endpoint, data) { return this.request(endpoint, { method: 'POST', body: JSON.stringify(data) }); } // PUT запрос async put(endpoint, data) { return this.request(endpoint, { method: 'PUT', body: JSON.stringify(data) }); } // DELETE запрос async delete(endpoint) { return this.request(endpoint, { method: 'DELETE' }); } // Загрузка файла async uploadFile(endpoint, formData) { return this.request(endpoint, { method: 'POST', body: formData, headers: {} // Убираем Content-Type для FormData }); } // === Методы для работы с тестами === async getTests(params = {}) { return this.get('/tests', params); } async getTestById(id) { return this.get(`/tests/${id}`); } async createTest(testData) { return this.post('/tests', testData); } async updateTest(id, testData) { return this.put(`/tests/${id}`, testData); } async deleteTest(id) { return this.delete(`/tests/${id}`); } // === Методы для работы с категориями === async getCategories() { return this.get('/test-categories'); } async getCategoryById(id) { return this.get(`/test-categories/${id}`); } async createCategory(categoryData) { return this.post('/test-categories', categoryData); } async updateCategory(id, categoryData) { return this.put(`/test-categories/${id}`, categoryData); } async deleteCategory(id) { return this.delete(`/test-categories/${id}`); } // === Методы для работы с вопросами === async getQuestionsByTest(testId) { return this.get(`/questions/test/${testId}`); } async getQuestionById(id) { return this.get(`/questions/${id}`); } async createQuestion(questionData) { return this.post('/questions', questionData); } async updateQuestion(id, questionData) { return this.put(`/questions/${id}`, questionData); } async deleteQuestion(id) { return this.delete(`/questions/${id}`); } // === Методы для работы с вариантами ответов === async getAnswerOptionsByQuestion(questionId) { return this.get(`/answer-options/question/${questionId}`); } async createAnswerOption(optionData) { return this.post('/answer-options', optionData); } async updateAnswerOption(id, optionData) { return this.put(`/answer-options/${id}`, optionData); } async deleteAnswerOption(id) { return this.delete(`/answer-options/${id}`); } // === Методы для работы с изображениями === async uploadImage(file, altText = null) { const formData = new FormData(); formData.append('file', file); if (altText) { formData.append('alt_text', altText); } return this.uploadFile('/images/upload', formData); } async getImage(imageId) { return this.request(`/images/${imageId}`, { headers: {} }); } async deleteImage(id) { return this.delete(`/images/${id}`); } // === Служебные методы === async checkHealth() { return this.get('/health'); } } // Создаем глобальный экземпляр API const api = new API(API_BASE_URL); // Утилиты для работы с API // Показать уведомление об ошибке function showApiError(error, customMessage = null) { const message = customMessage || error.message || 'Произошла ошибка при обращении к API'; showNotification(message, 'danger'); console.error('API Error:', error); } // Показать уведомление об успехе function showApiSuccess(message) { showNotification(message, 'success'); } // Показать загрузку function showLoading(show = true) { const spinner = document.getElementById('loadingSpinner'); if (spinner) { spinner.style.display = show ? 'block' : 'none'; } } // Функция для показа уведомлений function showNotification(message, type = 'info', duration = 5000) { const notificationsContainer = document.getElementById('notifications'); if (!notificationsContainer) return; const alertDiv = document.createElement('div'); alertDiv.className = `alert alert-${type} alert-dismissible fade show`; alertDiv.innerHTML = ` ${message} `; notificationsContainer.appendChild(alertDiv); // Автоматически удаляем уведомление через заданное время setTimeout(() => { if (alertDiv && alertDiv.parentNode) { alertDiv.remove(); } }, duration); } // Форматирование даты function formatDate(dateString) { if (!dateString) return '-'; const date = new Date(dateString); return date.toLocaleString('ru-RU', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); } // Получение URL изображения function getImageUrl(imageId) { return `${API_BASE_URL}/images/${imageId}`; } // Валидация данных формы function validateForm(formId) { const form = document.getElementById(formId); if (!form) return false; const requiredFields = form.querySelectorAll('[required]'); let isValid = true; requiredFields.forEach(field => { field.classList.remove('is-invalid'); if (!field.value.trim()) { field.classList.add('is-invalid'); isValid = false; } }); return isValid; } // Очистка формы function clearForm(formId) { const form = document.getElementById(formId); if (!form) return; form.reset(); // Удаляем классы валидации const fields = form.querySelectorAll('.is-invalid'); fields.forEach(field => field.classList.remove('is-invalid')); // Очищаем скрытые поля const hiddenFields = form.querySelectorAll('input[type="hidden"]'); hiddenFields.forEach(field => field.value = ''); } // Экспорт для использования в других скриптах window.api = api; window.showApiError = showApiError; window.showApiSuccess = showApiSuccess; window.showLoading = showLoading; window.showNotification = showNotification; window.formatDate = formatDate; window.getImageUrl = getImageUrl; window.validateForm = validateForm; window.clearForm = clearForm;