cleanings

This commit is contained in:
2024-12-14 08:34:35 +09:00
parent f19b544459
commit 0199fcc96f
27 changed files with 6302 additions and 29 deletions

View File

@@ -0,0 +1,177 @@
{% extends "admin/base_site.html" %}
{% block content %}
<style>
#table-data-preview {
max-height: 300px; /* Ограничиваем высоту предпросмотра */
overflow-y: auto; /* Прокрутка по вертикали */
overflow-x: auto; /* Прокрутка по горизонтали */
}
#table-data-preview table {
width: auto; /* Автоматическая ширина таблицы */
table-layout: auto; /* Автоматическая ширина колонок */
}
#table-data-preview th,
#table-data-preview td {
white-space: nowrap; /* Предотвращаем перенос текста */
overflow: hidden; /* Скрываем текст, выходящий за границы ячейки */
text-overflow: ellipsis; /* Добавляем многоточие для обрезанного текста */
padding: 8px; /* Внутренний отступ */
height: 40px; /* Фиксированная высота строк */
}
#table-data-preview th {
position: sticky; /* Фиксируем заголовки при прокрутке */
top: 0; /* Располагаем заголовки вверху таблицы */
background-color: #f8f9fa; /* Цвет фона заголовков */
z-index: 1; /* Заголовки перекрывают содержимое */
}
</style>
<div class="container mt-4">
<h2 class="text-center">Настройки подключения к БД</h2>
<form id="connection-form" method="post">
{% csrf_token %}
<div class="form-group mb-3">
<label for="db-name">Name</label>
<input id="db-name" class="form-control" type="text" name="name" value="{{ original.name }}" required />
</div>
<div class="form-group mb-3">
<label for="db-host">DB Host</label>
<input id="db-host" class="form-control" type="text" name="host" value="{{ original.host }}" required />
</div>
<div class="form-group mb-3">
<label for="db-port">DB Port</label>
<input id="db-port" class="form-control" type="number" name="port" value="{{ original.port }}" required />
</div>
<div class="form-group mb-3">
<label for="db-user">User</label>
<input id="db-user" class="form-control" type="text" name="user" value="{{ original.user }}" required />
</div>
<div class="form-group mb-3">
<label for="db-password">Password</label>
<input id="db-password" class="form-control" type="password" name="password" value="{{ original.password }}" />
</div>
<div class="form-group mb-3">
<label for="db-database">Database</label>
<input id="db-database" class="form-control" type="text" name="database" value="{{ original.database }}" required />
</div>
<div class="form-group mb-3">
<label for="table-selector">Таблицы</label>
<select id="table-selector" class="form-select" name="table_name">
{% if original.table_name %}
<option value="{{ original.table_name }}" selected>{{ original.table_name }}</option>
{% else %}
<option value="">-- Выберите таблицу --</option>
{% endif %}
</select>
</div>
<div class="form-group mb-3">
<label for="table-data-preview">Столбцы и данные</label>
<div id="table-data-preview" class="table-responsive">
<table class="table table-bordered">
<thead id="table-header"></thead>
<tbody id="table-body"></tbody>
</table>
</div>
</div>
<div class="form-group mb-3">
<label for="is-active">Активное подключение</label>
<input id="is-active" class="form-check-input" type="checkbox" name="is_active" {% if original.is_active %}checked{% endif %} />
</div>
<div class="form-group text-center">
<button class="btn btn-success" type="submit">Сохранить</button>
<button class="btn btn-secondary" type="button" id="close-button">Закрыть</button>
</div>
</form>
<hr>
<div id="connection-status" class="mt-4"></div>
<div class="text-center mt-3">
<button id="test-connection" class="btn btn-primary" type="button">Проверить подключение</button>
</div>
</div>
{% if original.id %}
<script>
const dbId = "{{ original.id }}";
</script>
{% else %}
<script>
const dbId = null;
document.getElementById("test-connection").style.display = "none";
alert("Сохраните запись перед выполнением проверки подключения.");
</script>
{% endif %}
<script>
// Закрыть окно
document.getElementById("close-button").addEventListener("click", function() {
window.history.back(); // Вернуться назад
});
// Проверить подключение и загрузить таблицы
document.getElementById("test-connection").addEventListener("click", function() {
if (!dbId) {
alert("ID подключения отсутствует.");
return;
}
fetch(`/admin/antifroud/externaldbsettings/test-connection/?db_id=${dbId}`)
.then(response => response.json())
.then(data => {
if (data.status === "success") {
document.getElementById("connection-status").innerHTML = `<div class="alert alert-success">${data.message}</div>`;
fetch(`/admin/antifroud/externaldbsettings/fetch-tables/?db_id=${dbId}`)
.then(response => response.json())
.then(tableData => {
if (tableData.status === "success") {
const selector = document.getElementById("table-selector");
selector.innerHTML = tableData.tables.map(table => `<option value="${table}">${table}</option>`).join("");
} else {
alert("Ошибка при загрузке таблиц: " + tableData.message);
}
});
} else {
document.getElementById("connection-status").innerHTML = `<div class="alert alert-danger">${data.message}</div>`;
}
})
.catch(error => {
alert("Ошибка при проверке подключения.");
console.error(error);
});
});
// При выборе таблицы загрузить столбцы и строки данных
document.getElementById("table-selector").addEventListener("change", function () {
const tableName = this.value;
if (!tableName) {
document.getElementById("table-header").innerHTML = "";
document.getElementById("table-body").innerHTML = "";
return;
}
fetch(`/admin/antifroud/externaldbsettings/fetch-table-data/?db_id=${dbId}&table_name=${tableName}`)
.then(response => response.json())
.then(data => {
if (data.status === "success") {
const headerRow = data.columns.map(col => `<th>${col}</th>`).join("");
document.getElementById("table-header").innerHTML = `<tr>${headerRow}</tr>`;
const rows = data.rows.map(row => {
const cells = row.map(cell => `<td>${cell}</td>`).join("");
return `<tr>${cells}</tr>`;
}).join("");
document.getElementById("table-body").innerHTML = rows;
} else {
alert("Ошибка при загрузке данных таблицы: " + data.message);
}
})
.catch(error => {
alert("Ошибка при загрузке данных таблицы.");
console.error(error);
});
});
</script>
{% endblock %}

View File

@@ -0,0 +1,97 @@
{% extends "admin/change_list.html" %}
{% block content %}
<!-- Кнопка импорта -->
<div class="form-group mb-3">
<button type="submit" class="btn btn-primary" form="importHotelsForm">
Импортировать выбранные отели
</button>
</div>
<!-- Действия админки -->
{{ block.super }}
<!-- Уведомление -->
<div id="notification" class="alert alert-info d-none" role="alert">
Здесь появятся уведомления.
</div>
<!-- Форма для импорта отелей -->
<form id="importHotelsForm" method="POST">
{% csrf_token %}
{{ form.as_p }} <!-- Отображаем форму -->
</form>
{% endblock %}
{% block extrahead %}
{{ block.super }}
<!-- Подключаем Bootstrap 4 -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const importButton = document.querySelector("button[type='submit']");
const notificationElement = document.getElementById('notification');
// Слушатель для отправки формы
importButton.addEventListener('click', function(e) {
e.preventDefault(); // предотвращаем стандартное поведение кнопки
// Извлекаем выбранные отели
const checkboxes = document.querySelectorAll('input[name="hotels"]:checked');
const selectedHotels = [];
console.log("Чекбоксы:", checkboxes); // Консольная отладка
checkboxes.forEach(function(checkbox) {
selectedHotels.push(checkbox.value);
console.log("Выбранный отель:", checkbox.value); // Консольная отладка
});
// Если выбраны отели
if (selectedHotels.length > 0) {
// Преобразуем CSRF токен
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
console.log("CSRF токен:", csrfToken); // Консольная отладка
// Отправляем данные на сервер
fetch('/import_hotels/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken,
},
body: JSON.stringify({ hotels: selectedHotels })
})
.then(response => response.json())
.then(data => {
console.log("Ответ от сервера:", data); // Консольная отладка
// Показать успешное уведомление
notificationElement.classList.remove('d-none');
notificationElement.classList.add('alert-success');
notificationElement.textContent = data.message || "Отели успешно импортированы!";
})
.catch((error) => {
console.error("Ошибка при импорте:", error); // Консольная отладка
// Показать ошибку
notificationElement.classList.remove('d-none');
notificationElement.classList.add('alert-danger');
notificationElement.textContent = "Произошла ошибка при импорте отелей.";
})
.finally(() => {
// Сброс кнопки
importButton.disabled = false;
importButton.textContent = 'Импортировать выбранные отели';
});
} else {
console.log("Не выбраны отели"); // Консольная отладка
notificationElement.classList.remove('d-none');
notificationElement.classList.add('alert-warning');
notificationElement.textContent = "Пожалуйста, выберите хотя бы один отель для импорта.";
}
});
});
</script>
{% endblock %}

View File

@@ -0,0 +1,33 @@
{% extends "admin/base_site.html" %}
{% load i18n static %}
{% block title %}Редактирование отеля{% endblock %}
{% block content %}
<div class="container-fluid">
<h3 class="text-dark mb-4">Редактирование отеля</h3>
<form method="post" action="{% url 'admin:save_edited_hotel' hotel.id %}">
{% csrf_token %}
<div class="form-group mb-3">
<label for="display_name" class="form-label">Отображаемое имя</label>
<input class="form-control" type="text" id="display_name" name="display_name" value="{{ hotel.display_name }}" required>
</div>
<div class="form-group mb-3">
<label for="original_name" class="form-label">Оригинальное имя</label>
<input class="form-control" type="text" id="original_name" name="original_name" value="{{ hotel.name }}" required>
</div>
<div class="form-group mb-3">
<label for="imported" class="form-label">Импортирован</label>
<select class="form-select" id="imported" name="imported">
<option value="True" {% if hotel.imported %} selected {% endif %}>Да</option>
<option value="False" {% if not hotel.imported %} selected {% endif %}>Нет</option>
</select>
</div>
<div class="form-group mb-3">
<button type="submit" class="btn btn-primary">Сохранить изменения</button>
<a href="{% url 'admin:hotel_list' %}" class="btn btn-secondary">Назад</a>
</div>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,143 @@
{% extends "admin/change_list.html" %}
{% block content %}
<!-- Кнопка импорта -->
<div class="form-group mb-3">
<button type="button" class="btn btn-primary" id="importHotelsButton">
Импортировать выбранные отели
</button>
</div>
<!-- Уведомление -->
<div id="notification" class="alert alert-info d-none" role="alert">
Здесь появятся уведомления.
</div>
<!-- Действия админки -->
{{ block.super }}
<!-- Список отелей для выбора в виде таблицы -->
<form id="importHotelsForm" method="POST">
{% csrf_token %}
<div class="table-responsive">
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<!-- Чекбокс для выбора всех отелей -->
<th><input type="checkbox" id="select-all" /></th>
<th>Внешний ID</th>
<th>Отображаемое имя</th>
<th>Имя отеля</th>
<th>Дата создания</th>
<th>Дата обновления</th>
<th>Импортирован в основную базу</th>
</tr>
</thead>
<tbody>
{% for hotel in imported_hotels %}
<tr>
<td>
<input type="checkbox" name="hotels" value="{{ hotel.id }}" id="hotel{{ hotel.id }}" class="select-row" />
</td>
<td>{{ hotel.external_id }}</td>
<td>{{ hotel.display_name }}</td>
<td>{{ hotel.name }}</td>
<td>{{ hotel.creation_date }}</td>
<td>{{ hotel.updated_at }}</td>
<td>{{ hotel.imported_to_main_db }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Здесь вы можете добавить скрытые поля или другие элементы формы, если они нужны -->
</form>
{% endblock %}
{% block extrahead %}
{{ block.super }}
<!-- Подключаем Bootstrap 4 -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Обработчик для выбора всех чекбоксов
const selectAllCheckbox = document.getElementById('select-all');
selectAllCheckbox.addEventListener('change', function() {
const checkboxes = document.querySelectorAll(".select-row");
checkboxes.forEach(function(checkbox) {
checkbox.checked = selectAllCheckbox.checked;
});
});
// Кнопка импорта
const importButton = document.getElementById('importHotelsButton');
const notificationElement = document.getElementById('notification');
importButton.addEventListener('click', function(e) {
e.preventDefault(); // предотвращаем стандартное поведение кнопки
// Даем время DOM полностью загрузиться
setTimeout(function() {
const checkboxes = document.querySelectorAll('input[name="hotels"]:checked');
const selectedHotels = [];
console.log("Чекбоксы:", checkboxes); // Отладка: выводим все выбранные чекбоксы
checkboxes.forEach(function(checkbox) {
selectedHotels.push(checkbox.value);
console.log("Выбранный отель:", checkbox.value); // Отладка: выводим ID выбранного отеля
});
if (selectedHotels.length > 0) {
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
console.log("CSRF токен:", csrfToken);
importButton.disabled = true;
importButton.textContent = 'Импортируем...';
// Отправка выбранных отелей на сервер через fetch
fetch('/import_hotels/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken,
},
body: JSON.stringify({ hotels: selectedHotels })
})
.then(response => response.json())
.then(data => {
console.log("Ответ сервера:", data);
notificationElement.classList.remove('d-none');
notificationElement.classList.add('alert-success');
notificationElement.textContent = "Отели успешно импортированы!";
checkboxes.forEach(checkbox => checkbox.checked = false); // Снимаем выделение с чекбоксов
})
.catch((error) => {
console.error("Ошибка при импорте:", error);
notificationElement.classList.remove('d-none');
notificationElement.classList.add('alert-danger');
notificationElement.textContent = "Произошла ошибка при импорте отелей.";
})
.finally(() => {
importButton.disabled = false;
importButton.textContent = 'Импортировать выбранные отели';
});
} else {
console.log("Не выбраны отели");
notificationElement.classList.remove('d-none');
notificationElement.classList.add('alert-warning');
notificationElement.textContent = "Пожалуйста, выберите хотя бы один отель для импорта.";
}
}, 100); // Задержка 100ms, чтобы дождаться рендеринга всех элементов
});
});
document.addEventListener('DOMContentLoaded', function() {
const checkboxes = document.querySelectorAll('input[name="hotels"]');
console.log("Чекбоксы на странице:", checkboxes); // Проверим, есть ли чекбоксы
});
</script>
{% endblock %}