Files
MClogger/web/templates/admin/group_members.html
SimolZimol 31b45d4db4 modified: web/blueprints/group_admin.py
modified:   web/blueprints/site_admin.py
	modified:   web/roles.py
	modified:   web/templates/admin/group_members.html
2026-04-13 18:02:55 +02:00

182 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "admin/base.html" %}
{% block title %}Members {{ group.name }}{% endblock %}
{% block content %}
<div class="d-flex align-items-center gap-2 mb-4">
<a href="{{ url_for('site_admin.groups') }}" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-arrow-left"></i>
</a>
<h2 class="mb-0">Members: <span class="text-success">{{ group.name }}</span></h2>
</div>
<div class="row g-3">
<!-- Current members -->
<div class="col-md-7">
<div class="card border-secondary mb-3">
<div class="card-header"><i class="bi bi-people-fill me-2"></i>Current Members ({{ members|length }})</div>
<div class="card-body p-0">
<table class="table table-hover mb-0">
<thead><tr><th>User</th><th>Role</th><th class="text-end">Actions</th></tr></thead>
<tbody>
{% for m in members %}
<tr>
<td>{{ m.username }}</td>
<td>
{% if m.role in management_roles %}
<span class="badge bg-warning text-dark"><i class="bi bi-star-fill me-1"></i>{{ role_label(m.role) }}</span>
{% else %}
<span class="badge bg-secondary">{{ role_label(m.role) }}</span>
{% endif %}
</td>
<td class="text-end">
<form method="post" action="{{ url_for('site_admin.group_member_set_role', group_id=group.id, user_id=m.id) }}" class="d-inline-flex align-items-center gap-1">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<select name="role" class="form-select form-select-sm" style="width: 150px;">
{% for role, label in role_options %}
<option value="{{ role }}" {{ 'selected' if m.role == role }}>{{ label }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-sm btn-outline-warning" title="Set role">
<i class="bi bi-check2"></i>
</button>
</form>
<form method="post" action="{{ url_for('site_admin.group_member_remove', group_id=group.id, user_id=m.id) }}" class="d-inline"
onsubmit="return confirm('Remove {{ m.username }} from group?')">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-outline-danger" title="Remove">
<i class="bi bi-person-dash"></i>
</button>
</form>
</td>
</tr>
{% else %}
<tr><td colspan="3" class="text-muted text-center py-3">No members</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- Pending invitations -->
<div class="card border-secondary">
<div class="card-header"><i class="bi bi-envelope-paper-fill me-2"></i>Pending Invitations ({{ pending_invites|length }})</div>
<div class="card-body p-0">
<table class="table table-hover mb-0">
<thead><tr><th>User</th><th>Role</th><th>Expires</th><th class="text-end">Actions</th></tr></thead>
<tbody>
{% for invite in pending_invites %}
{% set invite_url = url_for('auth.accept_invite', token=invite.token, _external=True) %}
<tr>
<td>
<div>{{ invite.invited_username }}</div>
<div class="small text-muted">{{ invite.invited_email }}</div>
</td>
<td>
{% if invite.role in management_roles %}
<span class="badge bg-warning text-dark"><i class="bi bi-star-fill me-1"></i>{{ role_label(invite.role) }}</span>
{% else %}
<span class="badge bg-secondary">{{ role_label(invite.role) }}</span>
{% endif %}
<div class="small text-muted mt-1">Sent: {{ invite.send_count or 0 }}</div>
</td>
<td class="small text-muted">{{ invite.expires_at | fmt_dt }}</td>
<td class="text-end">
<div class="d-none" id="invite-url-{{ invite.id }}">{{ invite_url }}</div>
<button type="button" class="btn btn-sm btn-outline-primary copy-btn" data-target="#invite-url-{{ invite.id }}" title="Copy invite link">
<i class="bi bi-clipboard"></i>
</button>
<form method="post" action="{{ url_for('site_admin.group_invite_resend', group_id=group.id, invite_id=invite.id) }}" class="d-inline">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-outline-info" title="Resend">
<i class="bi bi-send"></i>
</button>
</form>
<form method="post" action="{{ url_for('site_admin.group_invite_revoke', group_id=group.id, invite_id=invite.id) }}" class="d-inline"
onsubmit="return confirm('Revoke invitation for {{ invite.invited_username }}?')">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-outline-danger" title="Revoke">
<i class="bi bi-x-lg"></i>
</button>
</form>
</td>
</tr>
{% else %}
<tr><td colspan="4" class="text-muted text-center py-3">No pending invitations</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Right column: add + invite -->
<div class="col-md-5">
<!-- Add existing user -->
<div class="card border-secondary mb-3">
<div class="card-header"><i class="bi bi-person-plus-fill me-2"></i>Add Existing User</div>
<div class="card-body">
{% if non_members %}
<form method="post" action="{{ url_for('site_admin.group_member_add', group_id=group.id) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="mb-3">
<label class="form-label">Select User</label>
<select name="user_id" class="form-select">
{% for u in non_members %}
<option value="{{ u.id }}">{{ u.username }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label class="form-label">Role</label>
<select name="role" class="form-select">
{% for role, label in role_options %}
<option value="{{ role }}" {{ 'selected' if role == 'viewer' }}>{{ label }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-success w-100">
<i class="bi bi-person-plus-fill me-1"></i>Add
</button>
</form>
{% else %}
<p class="text-muted text-center py-3">All users are already members.</p>
{% endif %}
</div>
</div>
<!-- Invite new user (site admin can assign any role including group_owner) -->
<div class="card border-secondary">
<div class="card-header"><i class="bi bi-envelope-plus-fill me-2"></i>Invite New User</div>
<div class="card-body">
<form method="post" action="{{ url_for('site_admin.group_member_invite', group_id=group.id) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="mb-3">
<label class="form-label">Username</label>
<input type="text" name="username" class="form-control" maxlength="50" required>
</div>
<div class="mb-3">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-control" maxlength="255" required>
<div class="form-text">The user will receive a link and set their own password.</div>
</div>
<div class="mb-3">
<label class="form-label">Role</label>
<select name="role" class="form-select">
{% for role, label in role_options %}
<option value="{{ role }}" {{ 'selected' if role == 'viewer' }}>{{ label }}</option>
{% endfor %}
</select>
<div class="form-text text-warning">
<i class="bi bi-shield-fill me-1"></i>As Site Admin you can assign <strong>Group Owner</strong>.
</div>
</div>
<button type="submit" class="btn btn-success w-100">
<i class="bi bi-envelope-plus-fill me-1"></i>Create Invitation
</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}