mirror of
https://github.com/adrigongv23/G26---Telemetry-Software.git
synced 2026-08-25 11:33:17 +02:00
Gestión usuarios añadida
This commit is contained in:
parent
319a95cc91
commit
97d38a086c
8 changed files with 284 additions and 3 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -21,6 +21,7 @@ env/
|
|||
|
||||
# Claude Code workspace
|
||||
.claude/
|
||||
CLAUDE.md
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
|
|
|
|||
|
|
@ -28,4 +28,7 @@ urlpatterns = [
|
|||
path('logout/', auth_views.LogoutView.as_view(next_page='login'), name='logout'),
|
||||
path('mi-perfil/', users_views.mi_perfil, name='mi_perfil'),
|
||||
path('miembros/', users_views.listado_miembros, name='listado_miembros'),
|
||||
path('gestion/usuarios/', users_views.gestion_usuarios, name='gestion_usuarios'),
|
||||
path('gestion/usuarios/<int:pk>/editar/', users_views.editar_usuario, name='editar_usuario'),
|
||||
path('gestion/usuarios/<int:pk>/eliminar/', users_views.eliminar_usuario, name='eliminar_usuario'),
|
||||
]
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
Gestión
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="gestionDropdown">
|
||||
<li><a class="dropdown-item" href="#">Gestión de Usuarios</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'gestion_usuarios' %}">Gestión de Usuarios</a></li>
|
||||
<li><a class="dropdown-item" href="#">Gestión de Temporadas</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
|
|
|||
70
Plataform_Web/templates/editar_usuario.html
Normal file
70
Plataform_Web/templates/editar_usuario.html
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Editar Usuario | Gades Manager{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-3 flex-shrink-0">
|
||||
<div class="col-12">
|
||||
<div class="bg-dark text-white p-3 rounded-3 shadow-sm" style="border-left: 5px solid #0d6efd;">
|
||||
<h1 class="display-6 fw-bold mb-1">Editar Usuario</h1>
|
||||
<p class="text-white-50 mb-0">{{ usuario.username }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4" style="overflow-y: auto;">
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body p-4">
|
||||
<h5 class="card-title fw-bold mb-3">Datos del Usuario</h5>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.username.id_for_label }}" class="form-label fw-semibold">{{ form.username.label }}</label>
|
||||
{{ form.username }}
|
||||
{% for error in form.username.errors %}
|
||||
<div class="text-danger small mt-1">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col-6 mb-3">
|
||||
<label for="{{ form.first_name.id_for_label }}" class="form-label fw-semibold">{{ form.first_name.label }}</label>
|
||||
{{ form.first_name }}
|
||||
</div>
|
||||
<div class="col-6 mb-3">
|
||||
<label for="{{ form.last_name.id_for_label }}" class="form-label fw-semibold">{{ form.last_name.label }}</label>
|
||||
{{ form.last_name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.email.id_for_label }}" class="form-label fw-semibold">{{ form.email.label }}</label>
|
||||
{{ form.email }}
|
||||
{% for error in form.email.errors %}
|
||||
<div class="text-danger small mt-1">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col-6 mb-3">
|
||||
<label for="{{ form.rol.id_for_label }}" class="form-label fw-semibold">{{ form.rol.label }}</label>
|
||||
{{ form.rol }}
|
||||
</div>
|
||||
<div class="col-6 mb-3">
|
||||
<label for="{{ form.especialidad.id_for_label }}" class="form-label fw-semibold">{{ form.especialidad.label }}</label>
|
||||
{{ form.especialidad }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary fw-bold px-4">Guardar Cambios</button>
|
||||
<a href="{% url 'gestion_usuarios' %}" class="btn btn-outline-secondary px-4">Cancelar</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
125
Plataform_Web/templates/gestion_usuarios.html
Normal file
125
Plataform_Web/templates/gestion_usuarios.html
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Gestión de Usuarios | Gades Manager{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-3 flex-shrink-0">
|
||||
<div class="col-12">
|
||||
<div class="bg-dark text-white p-3 rounded-3 shadow-sm" style="border-left: 5px solid #0d6efd;">
|
||||
<h1 class="display-6 fw-bold mb-1">Gestión de Usuarios</h1>
|
||||
<p class="text-white-50 mb-0">Administra las cuentas del sistema: edita o elimina usuarios.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4" style="overflow-y: auto;">
|
||||
|
||||
{% if messages %}
|
||||
<div class="col-12">
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-success py-2 small mb-0">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="col-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
|
||||
<form method="get" class="row g-2 align-items-end mb-4">
|
||||
<div class="col-12 col-md-3">
|
||||
<label class="form-label small text-secondary mb-1">Rol</label>
|
||||
<select name="rol" class="form-select">
|
||||
<option value="">Todos los roles</option>
|
||||
{% for value, label in rol_choices %}
|
||||
<option value="{{ value }}" {% if filtro_rol == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-3">
|
||||
<label class="form-label small text-secondary mb-1">Área Técnica</label>
|
||||
<select name="especialidad" class="form-select">
|
||||
<option value="">Todas las áreas</option>
|
||||
{% for value, label in especialidad_choices %}
|
||||
<option value="{{ value }}" {% if filtro_especialidad == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-4">
|
||||
<label class="form-label small text-secondary mb-1">Apellido</label>
|
||||
<input type="text" name="apellido" class="form-control" placeholder="Buscar por apellido..." value="{{ filtro_apellido }}">
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-2 d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary fw-bold flex-grow-1">Buscar</button>
|
||||
{% if filtro_rol or filtro_especialidad or filtro_apellido %}
|
||||
<a href="{% url 'gestion_usuarios' %}" class="btn btn-outline-secondary" title="Limpiar filtros">×</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>Usuario</th>
|
||||
<th>Nombre</th>
|
||||
<th>Apellidos</th>
|
||||
<th>Email</th>
|
||||
<th>Área Técnica</th>
|
||||
<th>Rol</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for usuario in usuarios %}
|
||||
<tr>
|
||||
<td>{{ usuario.username }}</td>
|
||||
<td>{{ usuario.first_name }}</td>
|
||||
<td>{{ usuario.last_name }}</td>
|
||||
<td>{{ usuario.email }}</td>
|
||||
<td>{{ usuario.get_especialidad_display|default:"—" }}</td>
|
||||
<td>{{ usuario.get_rol_display }}</td>
|
||||
<td class="d-flex gap-2">
|
||||
<a href="{% url 'editar_usuario' usuario.pk %}" class="btn btn-sm btn-outline-primary">Editar</a>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" data-bs-toggle="modal" data-bs-target="#eliminarModal{{ usuario.pk }}">Eliminar</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<div class="modal fade" id="eliminarModal{{ usuario.pk }}" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Confirmar eliminación</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
¿Seguro que quieres eliminar al usuario <strong>{{ usuario.username }}</strong>? Esta acción no se puede deshacer.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||
<form method="post" action="{% url 'eliminar_usuario' usuario.pk %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-danger fw-bold">Eliminar</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="7" class="text-center text-secondary py-4">No se han encontrado usuarios con los filtros seleccionados.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
7
Plataform_Web/users/decorators.py
Normal file
7
Plataform_Web/users/decorators.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||
|
||||
|
||||
def require_rol(*roles):
|
||||
def decorator(view_func):
|
||||
return login_required(user_passes_test(lambda u: u.rol in roles)(view_func))
|
||||
return decorator
|
||||
|
|
@ -18,3 +18,25 @@ class PerfilForm(forms.ModelForm):
|
|||
'last_name': 'Apellidos',
|
||||
'email': 'Correo electrónico',
|
||||
}
|
||||
|
||||
|
||||
class EditarUsuarioForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CustomUser
|
||||
fields = ['username', 'first_name', 'last_name', 'email', 'rol', 'especialidad']
|
||||
widgets = {
|
||||
'username': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'first_name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'last_name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'email': forms.EmailInput(attrs={'class': 'form-control'}),
|
||||
'rol': forms.Select(attrs={'class': 'form-select'}),
|
||||
'especialidad': forms.Select(attrs={'class': 'form-select'}),
|
||||
}
|
||||
labels = {
|
||||
'username': 'Usuario',
|
||||
'first_name': 'Nombre',
|
||||
'last_name': 'Apellidos',
|
||||
'email': 'Correo electrónico',
|
||||
'rol': 'Rol en el equipo',
|
||||
'especialidad': 'Área técnica',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from django.shortcuts import render, redirect
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.forms import PasswordChangeForm
|
||||
from django.contrib.auth import update_session_auth_hash
|
||||
from django.contrib import messages
|
||||
from .forms import PerfilForm
|
||||
from django.views.decorators.http import require_POST
|
||||
from .decorators import require_rol
|
||||
from .forms import PerfilForm, EditarUsuarioForm
|
||||
from .models import CustomUser
|
||||
|
||||
|
||||
|
|
@ -64,3 +66,54 @@ def listado_miembros(request):
|
|||
'filtro_apellido': apellido,
|
||||
}
|
||||
return render(request, 'listado_miembros.html', context)
|
||||
|
||||
|
||||
@require_rol('directiva')
|
||||
def gestion_usuarios(request):
|
||||
usuarios = CustomUser.objects.all().order_by('last_name', 'first_name')
|
||||
|
||||
rol = request.GET.get('rol', '')
|
||||
especialidad = request.GET.get('especialidad', '')
|
||||
apellido = request.GET.get('apellido', '')
|
||||
|
||||
if rol:
|
||||
usuarios = usuarios.filter(rol=rol)
|
||||
if especialidad:
|
||||
usuarios = usuarios.filter(especialidad=especialidad)
|
||||
if apellido:
|
||||
usuarios = usuarios.filter(last_name__icontains=apellido)
|
||||
|
||||
context = {
|
||||
'usuarios': usuarios,
|
||||
'rol_choices': CustomUser.ROL_CHOICES,
|
||||
'especialidad_choices': CustomUser.ESPECIALIDAD_CHOICES,
|
||||
'filtro_rol': rol,
|
||||
'filtro_especialidad': especialidad,
|
||||
'filtro_apellido': apellido,
|
||||
}
|
||||
return render(request, 'gestion_usuarios.html', context)
|
||||
|
||||
|
||||
@require_rol('directiva')
|
||||
def editar_usuario(request, pk):
|
||||
usuario = get_object_or_404(CustomUser, pk=pk)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = EditarUsuarioForm(request.POST, instance=usuario)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, 'El usuario se ha actualizado correctamente.')
|
||||
return redirect('gestion_usuarios')
|
||||
else:
|
||||
form = EditarUsuarioForm(instance=usuario)
|
||||
|
||||
return render(request, 'editar_usuario.html', {'form': form, 'usuario': usuario})
|
||||
|
||||
|
||||
@require_rol('directiva')
|
||||
@require_POST
|
||||
def eliminar_usuario(request, pk):
|
||||
usuario = get_object_or_404(CustomUser, pk=pk)
|
||||
usuario.delete()
|
||||
messages.success(request, 'El usuario se ha eliminado correctamente.')
|
||||
return redirect('gestion_usuarios')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue