1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-11 12:46:49 +00:00
webvirtcloud/instances/templates/allinstances_index_grouped.html
catborise 3aac8f51f2 feat(instances): the VM list on Bootstrap's pills, badges and menus, its actions by the user's rights
- The state filter is a row of pills, All and each state, counting the
  VMs the search finds (the chosen state aside); a state without any is
  hidden unless chosen (All never), and a status line tells screen
  readers how many VMs match. Every state keeps its pill.
- A labelled "New VM" button replaces the bare green plus; "Find IP"
  says "Find IP / MAC", which it finds.
- States are badges; a shut-off VM is grey, not red: it is not a fault.
- A VM's cell holds its name, its title, and for those who see owners
  its first owner as a badge, "+N" for the others (named for screen
  readers and on hover); the separate user column goes.
- Each row has the same button group for a superuser or the VM's
  owners, from the actions the user may use (states.allowed_actions),
  all of it disabled on a VM an administrator paused; view-only users
  get none. It holds Console, the power action
  the state calls for (Start, Resume, Shut down, or Clone for a
  template) and a menu of the others (Suspend, Force off, Power cycle),
  what does not apply now disabled. The power button is an icon,
  named on hover and for screen readers, so every group is as wide and
  they line up from row to row; the power cut confirmations stay. On a
  phone Console shows its icon only too.
- A grouped host's row has its name, a Connected or Not connected
  badge, its VM count and a labelled host memory bar; an unreachable
  host's row is table-warning. Its collapse control and the one for
  all hosts are buttons, with aria-expanded kept.
- vCPUs and memory hide on a phone in both layouts, and table cells
  keep Bootstrap's own padding there: Zephyr doubles it and a list's
  last column no longer fit the screen.
2026-10-28 13:55:46 +03:00

109 lines
7.2 KiB
HTML

{% load i18n %}
{% load bootstrap_icons %}
<div class="table-responsive">
<table class="table table-hover align-middle" data-sortable>
<thead>
<tr>
<th scope="col" data-sort-method="none">
<button type="button" class="btn btn-sm btn-link p-0" id="hide-all-instances" onclick="hide_all_host_instances()" aria-expanded="true"
aria-label="{% trans "Collapse or expand every host" %}">{% bs_icon 'arrows-collapse' %}</button>
</th>
<th scope="col">{% trans "Name" %}</th>
<th scope="col">{% trans "Status" %}</th>
{% if app_settings.VM_DRBD_STATUS == 'True' %}
<th scope="col">{% trans "Role/Disk" %}</th>
{% endif %}
<th scope="col" class="d-none d-sm-table-cell" data-sort-method="number">{% trans "vCPUs" %}</th>
<th scope="col" class="d-none d-sm-table-cell" data-sort-method="number">{% trans "Memory" %}</th>
<th scope="col" data-sort-method="none"><span class="visually-hidden">{% trans "Actions" %}</span></th>
</tr>
</thead>
{% for compute in computes %}
<tbody class="searchable" data-host="{{ compute.name }}" id="host-vms-{{ compute.id }}">
{% if compute.instance_set.count > 0 %}
<tr class="{% if compute.status is True %}table-secondary{% else %}table-warning{% endif %}" data-sort-method="none" data-group-header>
<td>
<button type="button" class="btn btn-sm btn-link p-0" onclick="hide_host_instances('{{ compute.id }}')"
aria-expanded="true" aria-controls="host-vms-{{ compute.id }}"
aria-label="{% blocktrans with name=compute.name %}Collapse or expand {{ name }}{% endblocktrans %}">
<span id="collapse_host_instances_{{ compute.id }}" class="bi bi-chevron-up" aria-hidden="true"></span>
</button>
</td>
<td colspan="{% if app_settings.VM_DRBD_STATUS == 'True' %}3{% else %}2{% endif %}">
<div class="d-flex flex-wrap align-items-center gap-2">
<a class="fw-semibold" href="{% url 'overview' compute.id %}">{{ compute.name }}</a>
{% if compute.status is True %}
<span class="badge bg-success-subtle text-success-emphasis">{% trans "Connected" %}</span>
{% else %}
<span class="badge bg-danger-subtle text-danger-emphasis">{% trans "Not connected" %}</span>
{% endif %}
<span class="small text-body-secondary">{% blocktrans count counter=compute.instance_set.count %}{{ counter }} VM{% plural %}{{ counter }} VMs{% endblocktrans %}</span>
<span id="inst_count_badge_{{ compute.id }}" class="badge text-bg-secondary d-none">{% trans "collapsed" %}</span>
</div>
</td>
{% if compute.status is True %}
<td class="d-none d-sm-table-cell">{{ compute.cpu_count }}</td>
<td class="d-none d-sm-table-cell">{{ compute.ram_size|filesizeformat }}</td>
<td>
<div class="small text-body-secondary">{% blocktrans with percent=compute.ram_usage %}host memory {{ percent }}% in use{% endblocktrans %}</div>
<div class="progress" role="progressbar" style="--bs-progress-height: 0.3rem;" aria-label="{% trans "Host memory in use" %}"
aria-valuenow="{{ compute.ram_usage }}" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar" style="width: {{ compute.ram_usage }}%"></div>
</div>
</td>
{% else %}
<td class="d-none d-sm-table-cell"></td>
<td class="d-none d-sm-table-cell"></td>
<td></td>
{% endif %}
</tr>
{% for instance in compute.instance_set.all %}
{# searched for its name, title and owners, not its buttons #}
<tr host="{{ compute.id }}" data-search="{{ instance.name }}{% if instance.info %} {{ instance.title }}{% endif %}{% for owner in instance.userinstance_set.all %} {{ owner.user }}{% endfor %}">
<td></td>
<td>{% include 'instance_name_cell.html' with owners=True %}</td>
<td>
{% include 'instance_state.html' %}
</td>
{% if app_settings.VM_DRBD_STATUS == 'True' %}
<td>
{% if instance.drbd == "Primary/OK" or instance.drbd == "Secondary/OK" %}<span class="badge bg-success-subtle text-success-emphasis">{% else %}<span class="badge bg-danger-subtle text-danger-emphasis">{% endif %}{{ instance.drbd }}</span>
</td>
{% endif %}
<td class="d-none d-sm-table-cell" data-sort="{{ instance.info.3 }}">{{ instance.info.3 }}</td>
<td class="d-none d-sm-table-cell" data-sort="{{ instance.info.2 }}">{% if instance.info %}{% widthratio instance.info.2 1024 1 %} MiB{% endif %}</td>
<td>
{% include 'instance_actions.html' %}
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
{% endfor %}
</table>
</div>
{% block script %}
<script>
// a collapsed host's VM rows are hidden by its table body's class, so the
// search (table_filter.js, the hidden attribute) can still hide them too
function set_host_collapsed(tbody, collapsed) {
$(tbody).toggleClass('host-collapsed', collapsed);
$(tbody).find('[aria-controls]').attr('aria-expanded', !collapsed);
$(tbody).find('span[id^=collapse_host_instances_]').toggleClass('bi-chevron-down', collapsed).toggleClass('bi-chevron-up', !collapsed);
$(tbody).find('span[id^=inst_count_badge_]').toggleClass('d-none', !collapsed);
// expanded while any host is, as the next click collapses them all
$('#hide-all-instances').attr('aria-expanded', $('tbody.searchable').not('.host-collapsed').length > 0);
}
// every host the same way: collapsed, unless all already are
function hide_all_host_instances() {
var bodies = $('tbody.searchable');
var collapse = bodies.not('.host-collapsed').length > 0;
bodies.each(function () { set_host_collapsed(this, collapse); });
}
function hide_host_instances(host) {
var tbody = $('#collapse_host_instances_' + host).closest('tbody');
set_host_collapsed(tbody, !tbody.hasClass('host-collapsed'));
}
</script>
{% endblock %}