add algam
This commit is contained in:
304
app.control-bot/web/static/js/api.js
Normal file
304
app.control-bot/web/static/js/api.js
Normal file
@@ -0,0 +1,304 @@
|
||||
// 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;
|
||||
Reference in New Issue
Block a user