Добавил формы создания и пофиксил баг с пользователями

This commit is contained in:
2025-11-24 23:47:00 +03:00
parent 1c18ae96f7
commit 7879c3d9b5
19 changed files with 448 additions and 183 deletions

View File

@@ -79,26 +79,7 @@
</div>
</div>
<!-- Transponders Card -->
<div class="col-lg-6">
<div class="card h-100 shadow-sm border-0">
<div class="card-body">
<div class="d-flex align-items-center mb-3">
<div class="bg-warning bg-opacity-10 rounded-circle p-2 me-3">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-wifi text-warning" viewBox="0 0 16 16">
<path d="M6.002 3.5a5.5 5.5 0 1 1 3.996 9.5H10A5.5 5.5 0 0 1 6.002 3.5M6.002 5.5a3.5 3.5 0 1 0 3.996 5.5H10A3.5 3.5 0 0 0 6.002 5.5"/>
<path d="M10.5 12.5a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5.5.5 0 0 0-1 0 .5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5.5.5 0 0 0-1 0 .5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5 3.5 3.5 0 0 1 7 0"/>
</svg>
</div>
<h3 class="card-title mb-0">Добавление транспондеров</h3>
</div>
<p class="card-text">Добавьте список транспондеров в базу данных.</p>
<a href="{% url 'mainapp:add_trans' %}" class="btn btn-warning">
Добавить транспондеры
</a>
</div>
</div>
</div>
<!-- VCH Load Data Card -->
<div class="col-lg-6">

View File

@@ -19,6 +19,19 @@
<!-- Form fields with Bootstrap styling -->
{% include 'mainapp/components/_form_field.html' with field=form.file %}
<!-- Automatic checkbox -->
<div class="mb-3">
<div class="form-check">
{{ form.is_automatic }}
<label class="form-check-label" for="{{ form.is_automatic.id_for_label }}">
{{ form.is_automatic.label }}
</label>
{% if form.is_automatic.help_text %}
<div class="form-text">{{ form.is_automatic.help_text }}</div>
{% endif %}
</div>
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<a href="{% url 'mainapp:source_list' %}" class="btn btn-secondary me-md-2">Назад</a>
<button type="submit" class="btn btn-success">Добавить в базу</button>

View File

@@ -21,6 +21,19 @@
{% include 'mainapp/components/_form_field.html' with field=form.sat_choice %}
{% include 'mainapp/components/_form_field.html' with field=form.number_input %}
<!-- Automatic checkbox -->
<div class="mb-3">
<div class="form-check">
{{ form.is_automatic }}
<label class="form-check-label" for="{{ form.is_automatic.id_for_label }}">
{{ form.is_automatic.label }}
</label>
{% if form.is_automatic.help_text %}
<div class="form-text">{{ form.is_automatic.help_text }}</div>
{% endif %}
</div>
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<a href="{% url 'mainapp:source_list' %}" class="btn btn-secondary me-md-2">Назад</a>
<button type="submit" class="btn btn-primary">Добавить в базу</button>

View File

@@ -3,7 +3,7 @@
{% load static leaflet_tags %}
{% load l10n %}
{% block title %}{% if object %}Редактировать объект: {{ object.name }}{% else %}Создать объект{% endif %}{% endblock %}
{% block title %}{% if object %}Редактировать объект: {{ object.name }}{% else %}Создать новый объект{% endif %}{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/checkbox-select-multiple.css' %}">
@@ -144,7 +144,7 @@
<div class="container-fluid px-3">
<div class="row mb-3">
<div class="col-12 d-flex justify-content-between align-items-center">
<h2>{% if object %}Редактировать объект: {{ object.name }}{% else %}Создать объект{% endif %}</h2>
<h2>{% if object %}Редактировать объект: {{ object.name }}{% else %}Создать новый объект{% endif %}</h2>
<div>
{% if user.customuser.role == 'admin' or user.customuser.role == 'moderator' %}
<button type="submit" form="objitem-form" class="btn btn-primary btn-action">Сохранить</button>
@@ -248,6 +248,7 @@
</div>
</div>
{% if object %}
<!-- Транспондер -->
<div class="form-section">
<div class="form-section-header">
@@ -339,6 +340,7 @@
</div>
{% endif %}
</div>
{% endif %}
<!-- Блок с картой -->
<div class="form-section">

View File

@@ -40,13 +40,10 @@
<!-- Action buttons bar -->
<div class="d-flex gap-2">
{% comment %} <button type="button" class="btn btn-success btn-sm" title="Добавить">
<i class="bi bi-plus-circle"></i> Добавить
</button>
<button type="button" class="btn btn-info btn-sm" title="Изменить">
<i class="bi bi-pencil"></i> Изменить
</button> {% endcomment %}
{% if user.customuser.role == 'admin' or user.customuser.role == 'moderator' %}
<a href="{% url 'mainapp:objitem_create' %}" class="btn btn-success btn-sm" title="Создать новый объект">
<i class="bi bi-plus-circle"></i> Создать
</a>
<button type="button" class="btn btn-danger btn-sm" title="Удалить"
onclick="deleteSelectedObjects()">
<i class="bi bi-trash"></i> Удалить
@@ -243,6 +240,23 @@
</div>
</div>
<!-- Automatic Filter -->
<div class="mb-2">
<label class="form-label">Автоматическая:</label>
<div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="is_automatic" id="is_automatic_1" value="1"
{% if is_automatic == '1' %}checked{% endif %}>
<label class="form-check-label" for="is_automatic_1">Да</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="is_automatic" id="is_automatic_0" value="0"
{% if is_automatic == '0' %}checked{% endif %}>
<label class="form-check-label" for="is_automatic_0">Нет</label>
</div>
</div>
</div>
<!-- Date Filter -->
<div class="mb-2">
<label class="form-label">Дата ГЛ:</label>
@@ -309,6 +323,7 @@
{% include 'mainapp/components/_table_header.html' with label="Тип точки" field="" sortable=False %}
{% include 'mainapp/components/_table_header.html' with label="Sigma" field="" sortable=False %}
{% include 'mainapp/components/_table_header.html' with label="Зеркала" field="" sortable=False %}
{% include 'mainapp/components/_table_header.html' with label="Автоматическая?" field="is_automatic" sort=sort %}
</tr>
</thead>
<tbody>
@@ -378,10 +393,11 @@
{% endif %}
</td>
<td>{{ item.mirrors }}</td>
<td>{{ item.is_automatic }}</td>
</tr>
{% empty %}
<tr>
<td colspan="22" class="text-center py-4">
<td colspan="23" class="text-center py-4">
{% if selected_satellite_id %}
Нет данных для выбранных фильтров
{% else %}
@@ -612,6 +628,7 @@
setupRadioLikeCheckboxes('has_valid');
setupRadioLikeCheckboxes('has_source_type');
setupRadioLikeCheckboxes('has_sigma');
setupRadioLikeCheckboxes('is_automatic');
// Date range quick selection functions
window.setDateRange = function (period) {

View File

@@ -212,7 +212,7 @@
<div class="card">
<div class="card-body">
<h4>Частотный план</h4>
<p class="text-muted">Визуализация транспондеров спутника по частотам. <span style="color: #0d6efd;"></span> Downlink (синий), <span style="color: #fd7e14;"></span> Uplink (оранжевый). Используйте колесико мыши для масштабирования, наведите курсор на полосу для подробной информации.</p>
<p class="text-muted">Визуализация транспондеров спутника по частотам. <span style="color: #0d6efd;"></span> Downlink (синий), <span style="color: #fd7e14;"></span> Uplink (оранжевый). Используйте колесико мыши для масштабирования, наведите курсор на полосу для подробной информации и связи с парным каналом.</p>
<div class="frequency-plan">
<div class="chart-controls">
@@ -272,19 +272,6 @@
// Transponder data from Django
const transpondersData = {{ transponders|safe }};
// Color mapping for polarizations
const polarizationColors = {
'H': '#0d6efd',
'V': '#198754',
'L': '#dc3545',
'R': '#ffc107',
'default': '#6c757d'
};
function getColor(polarization) {
return polarizationColors[polarization] || polarizationColors['default'];
}
// Chart state
let canvas, ctx, container;
let zoomLevel = 1;
@@ -505,19 +492,21 @@ function renderChart() {
if (barWidth < 1) return;
const isHovered = hoveredTransponder && hoveredTransponder.transponder.name === t.name;
// Draw downlink bar
ctx.fillStyle = downlinkColor;
ctx.fillRect(x1, downlinkBarY, barWidth, downlinkBarHeight);
// Draw border
ctx.strokeStyle = '#fff';
ctx.lineWidth = 1;
// Draw border (thicker if hovered)
ctx.strokeStyle = isHovered ? '#000' : '#fff';
ctx.lineWidth = isHovered ? 3 : 1;
ctx.strokeRect(x1, downlinkBarY, barWidth, downlinkBarHeight);
// Draw name if there's space
if (barWidth > 40) {
ctx.fillStyle = (pol === 'R') ? '#000' : '#fff';
ctx.font = '9px sans-serif';
ctx.fillStyle = '#fff';
ctx.font = isHovered ? 'bold 10px sans-serif' : '9px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(t.name, x1 + barWidth / 2, downlinkBarY + downlinkBarHeight / 2 + 3);
}
@@ -529,7 +518,8 @@ function renderChart() {
width: barWidth,
height: downlinkBarHeight,
transponder: t,
type: 'downlink'
type: 'downlink',
centerX: x1 + barWidth / 2
});
});
@@ -553,19 +543,21 @@ function renderChart() {
// Skip if too small
if (barWidth < 1) return;
const isHovered = hoveredTransponder && hoveredTransponder.transponder.name === t.name;
// Draw uplink bar
ctx.fillStyle = uplinkColor;
ctx.fillRect(x1, uplinkBarY, barWidth, uplinkBarHeight);
// Draw border
ctx.strokeStyle = '#fff';
ctx.lineWidth = 1;
// Draw border (thicker if hovered)
ctx.strokeStyle = isHovered ? '#000' : '#fff';
ctx.lineWidth = isHovered ? 3 : 1;
ctx.strokeRect(x1, uplinkBarY, barWidth, uplinkBarHeight);
// Draw name if there's space
if (barWidth > 40) {
ctx.fillStyle = '#fff';
ctx.font = '9px sans-serif';
ctx.font = isHovered ? 'bold 10px sans-serif' : '9px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(t.name, x1 + barWidth / 2, uplinkBarY + uplinkBarHeight / 2 + 3);
}
@@ -577,7 +569,8 @@ function renderChart() {
width: barWidth,
height: uplinkBarHeight,
transponder: t,
type: 'uplink'
type: 'uplink',
centerX: x1 + barWidth / 2
});
});
@@ -593,12 +586,43 @@ function renderChart() {
}
});
// Draw hover tooltip
// Draw connection line between downlink and uplink when hovering
if (hoveredTransponder) {
drawConnectionLine(hoveredTransponder);
drawTooltip(hoveredTransponder);
}
}
function drawConnectionLine(rectInfo) {
const t = rectInfo.transponder;
if (!t.uplink) return; // No uplink to connect
// Find both downlink and uplink rects for this transponder
const downlinkRect = transponderRects.find(r => r.transponder.name === t.name && r.type === 'downlink');
const uplinkRect = transponderRects.find(r => r.transponder.name === t.name && r.type === 'uplink');
if (!downlinkRect || !uplinkRect) return;
// Draw connecting line
const x1 = downlinkRect.centerX;
const y1 = downlinkRect.y + downlinkRect.height;
const x2 = uplinkRect.centerX;
const y2 = uplinkRect.y;
ctx.save();
ctx.strokeStyle = '#ffc107';
ctx.lineWidth = 2;
ctx.setLineDash([5, 3]);
ctx.globalAlpha = 0.8;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.restore();
}
function drawTooltip(rectInfo) {
const t = rectInfo.transponder;
const isUplink = rectInfo.type === 'uplink';
@@ -639,14 +663,16 @@ function drawTooltip(rectInfo) {
const mouseX = rectInfo._mouseX || canvas.width / 2;
const mouseY = rectInfo._mouseY || canvas.height / 2;
let tooltipX = mouseX + 15;
let tooltipY = mouseY + 15;
let tooltipY = mouseY - tooltipHeight - 15; // Always show above cursor
// Keep tooltip in bounds
// Keep tooltip in bounds horizontally
if (tooltipX + tooltipWidth > canvas.width) {
tooltipX = mouseX - tooltipWidth - 15;
}
if (tooltipY + tooltipHeight > canvas.height) {
tooltipY = mouseY - tooltipHeight - 15;
// If tooltip goes above canvas, show below cursor instead
if (tooltipY < 0) {
tooltipY = mouseY + 15;
}
// Draw tooltip background

View File

@@ -129,13 +129,15 @@
<div class="container-fluid px-3">
<div class="row mb-3">
<div class="col-12 d-flex justify-content-between align-items-center">
<h2>Редактировать объект #{{ object.id }}</h2>
<h2>{% if object %}Редактировать объект #{{ object.id }}{% else %}Создать новый источник{% endif %}</h2>
<div>
{% if user.customuser.role == 'admin' or user.customuser.role == 'moderator' %}
<button type="submit" form="source-form" class="btn btn-primary btn-action">Сохранить</button>
{% if object %}
<a href="{% url 'mainapp:source_delete' object.id %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}"
class="btn btn-danger btn-action">Удалить</a>
{% endif %}
{% endif %}
<a href="{% url 'mainapp:source_list' %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}"
class="btn btn-secondary btn-action">Назад</a>
</div>
@@ -331,6 +333,7 @@
</div>
</div>
{% if object %}
<!-- Привязанные объекты -->
<div class="form-section">
<div class="form-section-header">
@@ -416,6 +419,7 @@
<p class="text-muted">Нет привязанных объектов</p>
{% endif %}
</div>
{% endif %}
</form>
</div>
{% endblock %}

View File

@@ -74,6 +74,11 @@
<!-- Action buttons -->
<div class="d-flex gap-2">
{% if user.customuser.role == 'admin' or user.customuser.role == 'moderator' %}
<a href="{% url 'mainapp:source_create' %}" class="btn btn-success btn-sm" title="Создать новый источник">
<i class="bi bi-plus-circle"></i> Создать
</a>
{% endif %}
<a href="{% url 'mainapp:load_excel_data' %}" class="btn btn-primary btn-sm" title="Загрузка данных из Excel">
<i class="bi bi-file-earmark-excel"></i> Excel
</a>

View File

@@ -61,6 +61,9 @@
<a href="{% url 'mainapp:transponder_create' %}" class="btn btn-success btn-sm" title="Создать">
<i class="bi bi-plus-circle"></i> Создать
</a>
<a href="{% url 'mainapp:add_trans' %}" class="btn btn-warning btn-sm" title="Загрузить из XML">
<i class="bi bi-upload"></i> Загрузить XML
</a>
<button type="button" class="btn btn-danger btn-sm" title="Удалить"
onclick="deleteSelectedTransponders()">
<i class="bi bi-trash"></i> Удалить