284 lines
15 KiB
HTML
284 lines
15 KiB
HTML
|
|
{% extends 'base.html' %}
|
||
|
|
|
||
|
|
{% block title %}Dashboard Administrativo{% endblock %}
|
||
|
|
|
||
|
|
{% block content %}
|
||
|
|
<div class="container">
|
||
|
|
<h1 class="mb-4">Dashboard Administrativo</h1>
|
||
|
|
|
||
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||
|
|
{% if messages %}
|
||
|
|
{% for category, message in messages %}
|
||
|
|
<div class="alert alert-{{ category }}">{{ message }}</div>
|
||
|
|
{% endfor %}
|
||
|
|
{% endif %}
|
||
|
|
{% endwith %}
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h5 class="card-title mb-0">Gerenciamento de Acessos</h5>
|
||
|
|
</div>
|
||
|
|
<div class="card-body">
|
||
|
|
<div class="table-responsive">
|
||
|
|
<table class="table table-striped">
|
||
|
|
<thead>
|
||
|
|
<tr>
|
||
|
|
<th>ID</th>
|
||
|
|
<th>Usuário</th>
|
||
|
|
<th>Email</th>
|
||
|
|
<th>Status</th>
|
||
|
|
<th>Último Login</th>
|
||
|
|
<th>Nível</th>
|
||
|
|
<th>Responsabilidades</th>
|
||
|
|
<th>Ações</th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
{% for user in users %}
|
||
|
|
{% if current_user.has_permission('system_config') or
|
||
|
|
(current_user.has_permission('manage_cr_sectors') and user.cr_id == current_user.cr_id) or
|
||
|
|
(current_user.has_permission('manage_sector_cells') and user.setor_id == current_user.setor_id) or
|
||
|
|
(current_user.has_permission('manage_cell_members') and user.celula_id == current_user.celula_id) %}
|
||
|
|
<tr>
|
||
|
|
<td>{{ user.id }}</td>
|
||
|
|
<td>{{ user.username }}</td>
|
||
|
|
<td>{{ user.email }}</td>
|
||
|
|
<td>
|
||
|
|
{% if user.ativo %}
|
||
|
|
<span class="badge bg-success">Ativo</span>
|
||
|
|
{% else %}
|
||
|
|
<span class="badge bg-danger">Inativo</span>
|
||
|
|
{% endif %}
|
||
|
|
</td>
|
||
|
|
<td>{{ user.ultimo_login.strftime('%d/%m/%Y %H:%M') if user.ultimo_login else 'Nunca' }}</td>
|
||
|
|
<td>
|
||
|
|
<span class="badge bg-info">{{ user.role }}</span>
|
||
|
|
{% if current_user.has_permission('system_config') or
|
||
|
|
(current_user.has_permission('manage_cr_sectors') and user.cr_id == current_user.cr_id) or
|
||
|
|
(current_user.has_permission('manage_sector_cells') and user.setor_id == current_user.setor_id) %}
|
||
|
|
<select class="form-select form-select-sm d-inline-block w-auto" onchange="alterarNivel({{ user.id }}, this.value)">
|
||
|
|
<option value="">Alterar Nível</option>
|
||
|
|
{% if current_user.has_permission('system_config') %}
|
||
|
|
<option value="militante_basico">Militante Básico</option>
|
||
|
|
<option value="secretario_celula">Secretário de Célula</option>
|
||
|
|
<option value="membro_setor">Membro de Setor</option>
|
||
|
|
<option value="secretario_setor">Secretário de Setor</option>
|
||
|
|
<option value="membro_cr">Membro de CR</option>
|
||
|
|
<option value="secretario_cr">Secretário de CR</option>
|
||
|
|
<option value="membro_cc">Membro do CC</option>
|
||
|
|
<option value="secretario_geral">Secretário Geral</option>
|
||
|
|
{% elif current_user.has_permission('manage_cr_sectors') %}
|
||
|
|
<option value="membro_cr">Membro de CR</option>
|
||
|
|
<option value="secretario_cr">Secretário de CR</option>
|
||
|
|
{% elif current_user.has_permission('manage_sector_cells') %}
|
||
|
|
<option value="membro_setor">Membro de Setor</option>
|
||
|
|
<option value="secretario_setor">Secretário de Setor</option>
|
||
|
|
{% endif %}
|
||
|
|
</select>
|
||
|
|
{% endif %}
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
{% if user.militante %}
|
||
|
|
{% if user.militante.quadro_orientador %}
|
||
|
|
<span class="badge bg-primary">Quadro-Orientador</span>
|
||
|
|
{% endif %}
|
||
|
|
{% if user.militante.aspirante %}
|
||
|
|
<span class="badge bg-warning">Aspirante</span>
|
||
|
|
<small class="text-muted">
|
||
|
|
(desde {{ user.militante.data_inicio_aspirante.strftime('%d/%m/%Y') }})
|
||
|
|
</small>
|
||
|
|
{% if user.militante.avaliacao_aspirante %}
|
||
|
|
<button type="button" class="btn btn-sm btn-info"
|
||
|
|
onclick="verAvaliacaoAspirante({{ user.id }})">
|
||
|
|
Ver Avaliação
|
||
|
|
</button>
|
||
|
|
{% endif %}
|
||
|
|
{% endif %}
|
||
|
|
{% if current_user.has_permission('system_config') or
|
||
|
|
(current_user.has_permission('manage_cr_sectors') and user.cr_id == current_user.cr_id) or
|
||
|
|
(current_user.has_permission('manage_sector_cells') and user.setor_id == current_user.setor_id) %}
|
||
|
|
{% if user.militante.quadro_orientador %}
|
||
|
|
<button type="button" class="btn btn-sm btn-danger"
|
||
|
|
onclick="toggleQuadroOrientador({{ user.id }}, {{ user.militante.quadro_orientador|lower }})">
|
||
|
|
Remover QO
|
||
|
|
</button>
|
||
|
|
{% else %}
|
||
|
|
<button type="button" class="btn btn-sm btn-success"
|
||
|
|
onclick="toggleQuadroOrientador({{ user.id }}, {{ user.militante.quadro_orientador|lower }})">
|
||
|
|
Tornar QO
|
||
|
|
</button>
|
||
|
|
{% endif %}
|
||
|
|
{% if user.militante.aspirante %}
|
||
|
|
{% if datetime.utcnow() - user.militante.data_inicio_aspirante >= timedelta(days=90) %}
|
||
|
|
{% if not user.militante.avaliacao_aspirante %}
|
||
|
|
<button type="button" class="btn btn-sm btn-primary"
|
||
|
|
onclick="avaliarAspirante({{ user.id }})">
|
||
|
|
Avaliar Aspirante
|
||
|
|
</button>
|
||
|
|
{% endif %}
|
||
|
|
<button type="button" class="btn btn-sm btn-danger"
|
||
|
|
onclick="toggleAspirante({{ user.id }}, {{ user.militante.aspirante|lower }})">
|
||
|
|
Remover Aspirante
|
||
|
|
</button>
|
||
|
|
{% endif %}
|
||
|
|
{% else %}
|
||
|
|
<button type="button" class="btn btn-sm btn-warning"
|
||
|
|
onclick="toggleAspirante({{ user.id }}, {{ user.militante.aspirante|lower }})">
|
||
|
|
Tornar Aspirante
|
||
|
|
</button>
|
||
|
|
{% endif %}
|
||
|
|
{% endif %}
|
||
|
|
{% endif %}
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
<div class="btn-group" role="group">
|
||
|
|
{% if current_user.has_permission('system_config') or
|
||
|
|
(current_user.has_permission('manage_cr_sectors') and user.cr_id == current_user.cr_id) or
|
||
|
|
(current_user.has_permission('manage_sector_cells') and user.setor_id == current_user.setor_id) or
|
||
|
|
(current_user.has_permission('manage_cell_members') and user.celula_id == current_user.celula_id) %}
|
||
|
|
<button type="button" class="btn btn-sm btn-primary"
|
||
|
|
onclick="resetOTP({{ user.id }})">
|
||
|
|
Gerar Novo OTP
|
||
|
|
</button>
|
||
|
|
<button type="button" class="btn btn-sm btn-warning"
|
||
|
|
onclick="resetPassword({{ user.id }})">
|
||
|
|
Resetar Senha
|
||
|
|
</button>
|
||
|
|
<button type="button" class="btn btn-sm {% if user.ativo %}btn-danger{% else %}btn-success{% endif %}"
|
||
|
|
onclick="toggleUserStatus({{ user.id }}, {{ user.ativo|lower }})">
|
||
|
|
{% if user.ativo %}Desativar{% else %}Ativar{% endif %} Login
|
||
|
|
</button>
|
||
|
|
{% endif %}
|
||
|
|
</div>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
{% endif %}
|
||
|
|
{% endfor %}
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
function resetOTP(userId) {
|
||
|
|
if (confirm('Tem certeza que deseja gerar um novo OTP para este usuário? O OTP atual será invalidado.')) {
|
||
|
|
fetch(`/usuarios/${userId}/otp/reset`, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'Content-Type': 'application/json'
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.then(response => response.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.success) {
|
||
|
|
alert('Novo OTP gerado com sucesso!');
|
||
|
|
location.reload();
|
||
|
|
} else {
|
||
|
|
alert('Erro ao gerar novo OTP: ' + data.message);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(error => {
|
||
|
|
alert('Erro ao gerar novo OTP: ' + error);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function resetPassword(userId) {
|
||
|
|
if (confirm('Tem certeza que deseja resetar a senha deste usuário? Uma nova senha será gerada e enviada por email.')) {
|
||
|
|
fetch(`/usuarios/${userId}/password/reset`, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'Content-Type': 'application/json'
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.then(response => response.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.success) {
|
||
|
|
alert('Senha resetada com sucesso! A nova senha foi enviada por email.');
|
||
|
|
} else {
|
||
|
|
alert('Erro ao resetar senha: ' + data.message);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(error => {
|
||
|
|
alert('Erro ao resetar senha: ' + error);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function toggleUserStatus(userId, currentStatus) {
|
||
|
|
const action = currentStatus ? 'desativar' : 'ativar';
|
||
|
|
if (confirm(`Tem certeza que deseja ${action} o login deste usuário?`)) {
|
||
|
|
fetch(`/usuarios/${userId}/toggle_status`, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'Content-Type': 'application/json'
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.then(response => response.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.success) {
|
||
|
|
alert(`Login ${action}do com sucesso!`);
|
||
|
|
location.reload();
|
||
|
|
} else {
|
||
|
|
alert(`Erro ao ${action} login: ` + data.message);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(error => {
|
||
|
|
alert(`Erro ao ${action} login: ` + error);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function alterarNivel(userId, novoNivel) {
|
||
|
|
if (!novoNivel) return;
|
||
|
|
|
||
|
|
if (confirm('Tem certeza que deseja alterar o nível deste usuário?')) {
|
||
|
|
fetch(`/usuarios/${userId}/alterar_nivel`, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'Content-Type': 'application/json'
|
||
|
|
},
|
||
|
|
body: JSON.stringify({ nivel: novoNivel })
|
||
|
|
})
|
||
|
|
.then(response => response.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.success) {
|
||
|
|
alert('Nível do usuário alterado com sucesso!');
|
||
|
|
location.reload();
|
||
|
|
} else {
|
||
|
|
alert('Erro ao alterar nível: ' + data.message);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(error => {
|
||
|
|
alert('Erro ao alterar nível: ' + error);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function toggleQuadroOrientador(userId, isQuadroOrientador) {
|
||
|
|
const action = isQuadroOrientador ? 'remover' : 'adicionar';
|
||
|
|
if (confirm(`Tem certeza que deseja ${action} a responsabilidade de Quadro-Orientador deste militante?`)) {
|
||
|
|
fetch(`/usuarios/${userId}/toggle_quadro_orientador`, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'Content-Type': 'application/json'
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.then(response => response.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.success) {
|
||
|
|
alert(`Responsabilidade de Quadro-Orientador ${action}da com sucesso!`);
|
||
|
|
location.reload();
|
||
|
|
} else {
|
||
|
|
alert(`Erro ao ${action} responsabilidade: ` + data.message);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(error => {
|
||
|
|
alert(`Erro ao ${action} responsabilidade: ` + error);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
{% endblock %}
|