Files
2025-09-22 11:54:42 +05:00

304 lines
8.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
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;