1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/admin/templates/admin/logs.html
catborise 3c6de221ca feat(admin): the admin lists as a people directory, the user form by what it allows
The Users, Groups and Removed VMs pages were bare tables: rows of
coloured icon buttons without words, five columns of small checks for
a user's role and permissions, dates broken over three lines and a
lone "+" for a new entry. The owner chose a directory layout from three
rendered options; it follows the host pages' cards:

- Each list is a card with its count and search in the header and one
  list-group item per entry, which stacks on a phone without scrolling.
- A user shows their role as a badge (Superuser, Template editor) or
  "User", Blocked as a red badge, their groups, a granted clone
  permission and live API tokens, and the last login on one line;
  role and status are searchable. Edit is the visible action, the menu
  holds View profile, Block or Unblock and Delete; Block and Delete
  are disabled on one's own account, which the server refuses anyway.
- A group shows its member and permission counts; its name opens it.
  The page's title said "Users".
- A removed VM shows its UUID, the host it left, its owners and when
  the record was made and expires.
- Logs keep their table in a card, the server search in its header,
  the count of matching rows, Done and Failed as badges, and a message
  column wide enough to read on a phone.

The user form was one long column of horizontal fields. The owner
rejected three boxed layouts and chose one led by what the user may
do: the Active switch, Template editor and Superuser as two cards,
groups as chips, the user's own permissions as switches each saying
what it allows, then the name and password and the quota folded into
details whose summaries tell their values (-1 as "any"); they open on
a new user and on an error inside them. A refused group or permission
is said beside its field. The group form follows it: the name, what
its members may do, its members as chips. Cancel goes back to the
list instead of the browser history.
2026-10-31 18:10:05 +03:00

66 lines
3.7 KiB
HTML

{% extends "base.html" %}
{% load i18n %}
{% load django_bootstrap5 %}
{% load bootstrap_icons %}
{% block title %}{% trans "Logs" %}{% endblock %}
{% block page_heading %}{% trans "Logs" %}{% endblock page_heading %}
{% block page_heading_extra %}
<a class="btn btn-outline-primary" href="{% url 'admin:logs_export' %}{% if query %}?{{ query }}{% endif %}"
title="{% trans 'Download these rows as an Excel file' %}"><span aria-hidden="true">{% bs_icon 'download' %}</span> {% trans "Excel" %}</a>
{% endblock page_heading_extra %}
{% block content %}
<div class="card mb-3">
{# the search runs on the server over every row; the download takes the same rows #}
<form method="get" class="card-header d-flex flex-wrap align-items-center gap-2" role="search">
<span class="me-auto">{% trans "Logs" %} <span class="badge text-bg-light border">{{ logs.paginator.count }}</span></span>
<input name="q" value="{{ q }}" class="form-control form-control-sm w-auto" type="search" placeholder="{% trans 'Search' %}"
aria-label="{% trans 'Search the logs' %}">
<div class="form-check mb-0">
<input id="logs-failed" class="form-check-input" type="checkbox" name="failed" value="1"{% if failed %} checked{% endif %}>
<label class="form-check-label small" for="logs-failed">{% trans "Failed only" %}</label>
</div>
<button type="submit" class="btn btn-sm btn-primary">{% trans "Search" %}</button>
</form>
{% if not logs %}
<div class="card-body text-center text-body-secondary">
{% if q or failed %}{% trans "No log rows match." %}{% else %}{% trans "There are no log rows yet." %}{% endif %}
</div>
{% else %}
<div class="table-responsive position-relative">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">{% blocktrans %}Date ({{ time_zone }}){% endblocktrans %}</th>
<th scope="col">{% trans "User" %}</th>
<th scope="col">{% trans "Host" %}</th>
<th scope="col">{% trans "Instance" %}</th>
<th scope="col">{% trans "Message" %}</th>
<th scope="col">{% trans "Result" %}</th>
<th scope="col">{% trans "IP" %}</th>
</tr>
</thead>
<tbody>
{% for log in logs %}
<tr>
<td class="small text-body-secondary">{{ log.id }}</td>
<td class="small text-nowrap">{{ log.date|date:"Y-m-d H:i:s" }}</td>
<td>{{ log.user }}</td>
<td>{{ log.host }}</td>
<td>{% if log.vm_pk %}<a href="{% url 'instances:instance' log.vm_pk %}">{{ log.instance }}</a>{% else %}{{ log.instance }}{% endif %}</td>
<td class="log-message">{{ log.message }}</td>
<td>{% if log.success is None %}<span class="small text-body-secondary">{% trans "Unknown" %}</span>{% elif log.success %}<span class="badge text-bg-light border">{% trans "Done" %}</span>{% else %}<span class="badge text-bg-danger">{% trans "Failed" %}</span>{% endif %}</td>
<td class="small font-monospace">{{ log.ip|default:"" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
{% if logs %}{% bootstrap_pagination logs extra=query %}{% endif %}
{% endblock %}