- Добавлена колонка 'Тип' во все таблицы истории сборок - Для push операций отображается registry вместо платформ - Сохранение пользователя при создании push лога - Исправлена ошибка с logger в push_docker_image endpoint - Улучшено отображение истории сборок с визуальными индикаторами
249 lines
12 KiB
HTML
249 lines
12 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Роли - DevOpsLab{% endblock %}
|
||
{% block page_title %}Роли Ansible{% endblock %}
|
||
|
||
{% block header_actions %}
|
||
<a href="/roles/create" class="btn btn-primary btn-sm">
|
||
<i class="fas fa-plus me-2"></i>
|
||
Создать роль
|
||
</a>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<!-- Поиск и фильтры -->
|
||
<div class="card mb-3">
|
||
<div class="card-body">
|
||
<form method="get" action="/roles" class="row g-3">
|
||
<div class="col-12 col-md-8">
|
||
<input
|
||
type="text"
|
||
name="search"
|
||
value="{{ search }}"
|
||
placeholder="Поиск по имени или описанию..."
|
||
class="form-control"
|
||
>
|
||
</div>
|
||
<div class="col-12 col-md-4">
|
||
<div class="d-flex gap-2">
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fas fa-search me-1"></i>
|
||
Поиск
|
||
</button>
|
||
{% if search %}
|
||
<a href="/roles" class="btn btn-outline-secondary">
|
||
<i class="fas fa-times me-1"></i>
|
||
Сброс
|
||
</a>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Таблица ролей -->
|
||
<div class="card mb-3">
|
||
<div class="card-header">
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<h5 class="mb-0">Список ролей</h5>
|
||
<span class="text-muted small">
|
||
Всего: <strong>{{ total }}</strong>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
{% if roles %}
|
||
<div class="table-responsive">
|
||
<table class="table table-hover mb-0 align-middle">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th style="width: 20%;">Имя роли</th>
|
||
<th style="width: 30%;">Описание</th>
|
||
<th style="width: 20%;">Компоненты</th>
|
||
<th style="width: 20%;">Платформы</th>
|
||
<th style="width: auto; min-width: 140px;">Действия</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for role in roles %}
|
||
<tr>
|
||
<td>
|
||
<a href="/roles/{{ role.name }}" class="text-decoration-none fw-semibold text-primary">
|
||
<i class="fas fa-cube me-1"></i>
|
||
{{ role.name }}
|
||
</a>
|
||
</td>
|
||
<td>
|
||
<span class="text-muted small">
|
||
{% if role.description %}
|
||
{{ role.description[:80] }}{% if role.description|length > 80 %}...{% endif %}
|
||
{% else %}
|
||
<span class="text-muted fst-italic">Нет описания</span>
|
||
{% endif %}
|
||
</span>
|
||
</td>
|
||
<td>
|
||
<div class="d-flex flex-wrap gap-1">
|
||
{% if role.has_tasks %}
|
||
<span class="badge bg-success" title="Tasks - задачи роли">
|
||
<i class="fas fa-tasks me-1"></i>Tasks
|
||
</span>
|
||
{% endif %}
|
||
{% if role.has_defaults %}
|
||
<span class="badge bg-info" title="Defaults - значения по умолчанию">
|
||
<i class="fas fa-sliders-h me-1"></i>Defaults
|
||
</span>
|
||
{% endif %}
|
||
{% if role.has_handlers %}
|
||
<span class="badge bg-warning text-dark" title="Handlers - обработчики">
|
||
<i class="fas fa-bell me-1"></i>Handlers
|
||
</span>
|
||
{% endif %}
|
||
{% if role.has_meta %}
|
||
<span class="badge bg-primary" title="Meta - метаданные роли">
|
||
<i class="fas fa-info-circle me-1"></i>Meta
|
||
</span>
|
||
{% endif %}
|
||
{% if not role.has_tasks and not role.has_defaults and not role.has_handlers and not role.has_meta %}
|
||
<span class="badge bg-secondary">Пустая роль</span>
|
||
{% endif %}
|
||
</div>
|
||
</td>
|
||
<td>
|
||
{% if role.platforms and role.platforms|length > 0 %}
|
||
<div class="d-flex flex-wrap gap-1">
|
||
{% for platform in role.platforms[:4] %}
|
||
<span class="badge bg-dark" title="{{ platform.name }}{% if platform.versions %} ({{ platform.versions|join(', ') }}){% endif %}">
|
||
<i class="fab fa-linux me-1"></i>
|
||
{{ platform.name }}
|
||
</span>
|
||
{% endfor %}
|
||
{% if role.platforms|length > 4 %}
|
||
<span class="badge bg-secondary" title="Ещё {{ role.platforms|length - 4 }} платформ">
|
||
+{{ role.platforms|length - 4 }}
|
||
</span>
|
||
{% endif %}
|
||
</div>
|
||
{% else %}
|
||
<span class="text-muted small fst-italic">Не указаны</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
<div class="btn-group btn-group-sm" role="group">
|
||
<a
|
||
href="/roles/{{ role.name }}/test"
|
||
class="btn btn-outline-success"
|
||
title="Запустить тест"
|
||
>
|
||
<i class="fas fa-vial"></i>
|
||
</a>
|
||
<a
|
||
href="/roles/{{ role.name }}/edit"
|
||
class="btn btn-outline-primary"
|
||
title="Редактировать"
|
||
>
|
||
<i class="fas fa-edit"></i>
|
||
</a>
|
||
<a
|
||
href="/roles/{{ role.name }}"
|
||
class="btn btn-outline-secondary"
|
||
title="Детали"
|
||
>
|
||
<i class="fas fa-info-circle"></i>
|
||
</a>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% else %}
|
||
<div class="text-center py-5">
|
||
<i class="fas fa-inbox fa-3x text-muted mb-3"></i>
|
||
<p class="text-muted mb-3">Роли не найдены</p>
|
||
<a href="/roles/create" class="btn btn-primary">
|
||
<i class="fas fa-plus me-2"></i>
|
||
Создать первую роль
|
||
</a>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% if roles and total_pages > 1 %}
|
||
<div class="card-footer">
|
||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
|
||
<!-- Пагинация -->
|
||
<nav aria-label="Навигация по страницам">
|
||
<ul class="pagination mb-0">
|
||
{% if page > 1 %}
|
||
<li class="page-item">
|
||
<a class="page-link" href="?page={{ page - 1 }}{% if search %}&search={{ search }}{% endif %}">
|
||
<i class="fas fa-chevron-left"></i>
|
||
</a>
|
||
</li>
|
||
{% else %}
|
||
<li class="page-item disabled">
|
||
<span class="page-link">
|
||
<i class="fas fa-chevron-left"></i>
|
||
</span>
|
||
</li>
|
||
{% endif %}
|
||
|
||
{% for p in range(1, total_pages + 1) %}
|
||
{% if p == page %}
|
||
<li class="page-item active">
|
||
<span class="page-link">{{ p }}</span>
|
||
</li>
|
||
{% elif p == 1 or p == total_pages or (p >= page - 2 and p <= page + 2) %}
|
||
<li class="page-item">
|
||
<a class="page-link" href="?page={{ p }}{% if search %}&search={{ search }}{% endif %}">{{ p }}</a>
|
||
</li>
|
||
{% elif p == page - 3 or p == page + 3 %}
|
||
<li class="page-item disabled">
|
||
<span class="page-link">...</span>
|
||
</li>
|
||
{% endif %}
|
||
{% endfor %}
|
||
|
||
{% if page < total_pages %}
|
||
<li class="page-item">
|
||
<a class="page-link" href="?page={{ page + 1 }}{% if search %}&search={{ search }}{% endif %}">
|
||
<i class="fas fa-chevron-right"></i>
|
||
</a>
|
||
</li>
|
||
{% else %}
|
||
<li class="page-item disabled">
|
||
<span class="page-link">
|
||
<i class="fas fa-chevron-right"></i>
|
||
</span>
|
||
</li>
|
||
{% endif %}
|
||
</ul>
|
||
</nav>
|
||
|
||
<!-- Выбор количества на странице -->
|
||
<div class="d-flex align-items-center gap-2">
|
||
<span class="text-muted small">На странице:</span>
|
||
<select
|
||
class="form-select form-select-sm pagination-per-page-select"
|
||
style="width: auto;"
|
||
onchange="window.location.href = '?page=1&per_page=' + this.value + '{% if search %}&search={{ search }}{% endif %}'"
|
||
>
|
||
<option value="10" {% if per_page == 10 %}selected{% endif %}>10</option>
|
||
<option value="25" {% if per_page == 25 %}selected{% endif %}>25</option>
|
||
<option value="50" {% if per_page == 50 %}selected{% endif %}>50</option>
|
||
<option value="100" {% if per_page == 100 %}selected{% endif %}>100</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Информация о странице -->
|
||
<div class="text-muted small">
|
||
Показано {{ ((page - 1) * per_page) + 1 }} - {{ [page * per_page, total]|min }} из {{ total }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endblock %}
|