feat: implementa sistema de responsabilidades e instâncias - Adiciona responsabilidades de Finanças e Imprensa para todas as instâncias - Cria templates genéricos para gerenciamento de instâncias - Implementa sistema de permissões baseado em RBAC - Adiciona status de Aspirante com avaliação obrigatória - Atualiza documentação com novas regras e responsabilidades - Cria testes para validação das permissões - Adiciona migração para novos campos no banco de dados

This commit is contained in:
LS
2025-04-03 15:58:07 -03:00
parent 8dac8dc234
commit cbaf227e58
37 changed files with 4305 additions and 953 deletions

View File

@@ -0,0 +1,51 @@
{% extends 'base.html' %}
{% block title %}Alterar Senha{% endblock %}
{% block content %}
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3 class="card-title">Alterar Senha</h3>
</div>
<div class="card-body">
{% 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 %}
<form method="POST" action="{{ url_for('alterar_senha') }}">
<div class="mb-3">
<label for="senha_atual" class="form-label">Senha Atual</label>
<input type="password" class="form-control" id="senha_atual" name="senha_atual" required>
</div>
<div class="mb-3">
<label for="nova_senha" class="form-label">Nova Senha</label>
<input type="password" class="form-control" id="nova_senha" name="nova_senha" required>
<small class="text-muted">
A senha deve ter no mínimo 8 caracteres e conter letras e números.
</small>
</div>
<div class="mb-3">
<label for="confirmar_senha" class="form-label">Confirmar Nova Senha</label>
<input type="password" class="form-control" id="confirmar_senha" name="confirmar_senha" required>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">Alterar Senha</button>
<a href="{{ url_for('home') }}" class="btn btn-secondary">Cancelar</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -65,42 +65,60 @@
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('home') }}">Início</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('listar_militantes') }}">Militantes</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('listar_pagamentos') }}">Pagamentos</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('listar_materiais') }}">Materiais</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('listar_relatorios_vendas') }}">Vendas</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown">
Relatórios
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ url_for('listar_relatorios_cotas') }}">Relatórios de Cotas</a></li>
<li><a class="dropdown-item" href="{{ url_for('listar_relatorios_vendas') }}">Relatórios de Vendas</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown">
Configurações
</a>
<ul class="dropdown-menu">
</ul>
</li>
{% if current_user is defined and current_user.is_authenticated %}
{% if current_user.is_admin %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('dashboard_admin') }}">Dashboard Admin</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('home') }}">Início</a>
</li>
{% if current_user.has_permission('view_cell_data') %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('listar_militantes') }}">Militantes</a>
</li>
{% endif %}
{% if current_user.has_permission('view_cell_reports') %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('listar_pagamentos') }}">Pagamentos</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('listar_materiais') }}">Materiais</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('listar_relatorios_vendas') }}">Vendas</a>
</li>
{% endif %}
{% if current_user.has_permission('view_cell_reports') or current_user.has_permission('view_sector_reports') or current_user.has_permission('view_cr_reports') %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown">
Relatórios
</a>
<ul class="dropdown-menu">
{% if current_user.has_permission('view_cell_reports') %}
<li><a class="dropdown-item" href="{{ url_for('listar_relatorios_cotas') }}">Relatórios de Cotas</a></li>
<li><a class="dropdown-item" href="{{ url_for('listar_relatorios_vendas') }}">Relatórios de Vendas</a></li>
{% endif %}
</ul>
</li>
{% endif %}
{% endif %}
{% endif %}
</ul>
<ul class="navbar-nav">
{% if current_user is defined and current_user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('logout') }}">Sair</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('login') }}">Login</a>
</li>
{% endif %}
</ul>
</div>
</div>
@@ -111,5 +129,28 @@
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Verificar status da sessão a cada 5 minutos
function checkSession() {
fetch('/check_session')
.then(response => response.json())
.then(data => {
if (data.expired) {
window.location.href = '/login';
}
})
.catch(error => console.error('Erro ao verificar sessão:', error));
}
// Verificar a cada 5 minutos
setInterval(checkSession, 5 * 60 * 1000);
// Verificar também quando a página ganha foco
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'visible') {
checkSession();
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,111 @@
{% extends 'base.html' %}
{% block title %}Criar {{ tipo_instancia }}{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="mb-4">Criar {{ tipo_instancia }}</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 %}
<form method="POST" class="needs-validation" novalidate>
<div class="row">
<div class="col-md-6 mb-3">
<label for="nome" class="form-label">Nome</label>
<input type="text" class="form-control" id="nome" name="nome" required>
<div class="invalid-feedback">
Por favor, insira o nome da {{ tipo_instancia }}.
</div>
</div>
{% if tipo_instancia != 'Célula' %}
<div class="col-md-6 mb-3">
<label for="instancia_superior_id" class="form-label">{{ instancia_superior }}</label>
<select class="form-select" id="instancia_superior_id" name="instancia_superior_id" required>
<option value="">Selecione uma {{ instancia_superior }}</option>
{% for superior in instancias_superiores %}
<option value="{{ superior.id }}">{{ superior.nome }}</option>
{% endfor %}
</select>
<div class="invalid-feedback">
Por favor, selecione uma {{ instancia_superior }}.
</div>
</div>
{% endif %}
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="responsavel_geral_id" class="form-label">Responsável Geral</label>
<select class="form-select" id="responsavel_geral_id" name="responsavel_geral_id" required>
<option value="">Selecione o responsável geral</option>
{% for militante in militantes %}
<option value="{{ militante.id }}">{{ militante.nome }}</option>
{% endfor %}
</select>
<div class="invalid-feedback">
Por favor, selecione o responsável geral.
</div>
</div>
<div class="col-md-6 mb-3">
<label for="responsavel_financas_id" class="form-label">Responsável de Finanças</label>
<select class="form-select" id="responsavel_financas_id" name="responsavel_financas_id">
<option value="">Selecione o responsável de finanças</option>
{% for militante in militantes %}
<option value="{{ militante.id }}">{{ militante.nome }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="responsavel_imprensa_id" class="form-label">Responsável de Imprensa</label>
<select class="form-select" id="responsavel_imprensa_id" name="responsavel_imprensa_id">
<option value="">Selecione o responsável de imprensa</option>
{% for militante in militantes %}
<option value="{{ militante.id }}">{{ militante.nome }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="d-flex justify-content-between mt-4">
<button type="submit" class="btn btn-primary">Criar</button>
<a href="{{ url_for('listar_' + tipo_instancia.lower() + 's') }}" class="btn btn-secondary">Cancelar</a>
</div>
</form>
</div>
</div>
</div>
<script>
// Validação do formulário
(function () {
'use strict'
var forms = document.querySelectorAll('.needs-validation')
Array.prototype.slice.call(forms)
.forEach(function (form) {
form.addEventListener('submit', function (event) {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
}, false)
})
})()
</script>
{% endblock %}

View File

@@ -0,0 +1,106 @@
{% extends 'base.html' %}
{% block title %}Criar Militante{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="mb-4">Criar Militante</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 %}
<form method="POST" class="needs-validation" novalidate>
<div class="row">
<div class="col-md-6 mb-3">
<label for="nome" class="form-label">Nome</label>
<input type="text" class="form-control" id="nome" name="nome" required>
<div class="invalid-feedback">
Por favor, insira o nome do militante.
</div>
</div>
<div class="col-md-6 mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
<div class="invalid-feedback">
Por favor, insira um email válido.
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="celula_id" class="form-label">Célula</label>
<select class="form-select" id="celula_id" name="celula_id" required>
<option value="">Selecione uma célula</option>
{% for celula in celulas %}
<option value="{{ celula.id }}">{{ celula.nome }}</option>
{% endfor %}
</select>
<div class="invalid-feedback">
Por favor, selecione uma célula.
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 mb-3">
<label class="form-label">Responsabilidades</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="responsavel_financas" name="responsabilidades" value="{{ Militante.RESPONSAVEL_FINANCAS }}">
<label class="form-check-label" for="responsavel_financas">
Responsável de Finanças
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="responsavel_imprensa" name="responsabilidades" value="{{ Militante.RESPONSAVEL_IMPRENSA }}">
<label class="form-check-label" for="responsavel_imprensa">
Responsável de Imprensa
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="quadro_orientador" name="responsabilidades" value="{{ Militante.QUADRO_ORIENTADOR }}">
<label class="form-check-label" for="quadro_orientador">
Quadro-Orientador
</label>
</div>
</div>
</div>
<div class="d-flex justify-content-between mt-4">
<button type="submit" class="btn btn-primary">Criar</button>
<a href="{{ url_for('listar_militantes') }}" class="btn btn-secondary">Cancelar</a>
</div>
</form>
</div>
</div>
</div>
<script>
// Validação do formulário
(function () {
'use strict'
var forms = document.querySelectorAll('.needs-validation')
Array.prototype.slice.call(forms)
.forEach(function (form) {
form.addEventListener('submit', function (event) {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
}, false)
})
})()
</script>
{% endblock %}

284
templates/dashboard.html Normal file
View File

@@ -0,0 +1,284 @@
{% 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 %}

View File

@@ -0,0 +1,83 @@
{% 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 mb-4">
<div class="card-header">
<h5 class="card-title mb-0">Gerenciamento de Usuários</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>Admin</th>
<th>OTP Configurado</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{% for usuario in usuarios %}
<tr>
<td>{{ usuario.id }}</td>
<td>{{ usuario.username }}</td>
<td>{{ usuario.email }}</td>
<td>
{% if usuario.is_admin %}
<span class="badge bg-success">Sim</span>
{% else %}
<span class="badge bg-secondary">Não</span>
{% endif %}
</td>
<td>
{% if usuario.otp_secret %}
<span class="badge bg-success">Sim</span>
{% else %}
<span class="badge bg-danger">Não</span>
{% endif %}
</td>
<td>
<form action="{{ url_for('reset_otp', user_id=usuario.id) }}" method="POST" class="d-inline">
<button type="submit" class="btn btn-warning btn-sm"
onclick="return confirm('Tem certeza que deseja resetar o OTP deste usuário?')">
Resetar OTP
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Ações Rápidas</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="{{ url_for('novo_usuario') }}" class="btn btn-primary">
Criar Novo Usuário
</a>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,111 @@
{% extends 'base.html' %}
{% block title %}Editar {{ tipo_instancia }}{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="mb-4">Editar {{ tipo_instancia }}</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 %}
<form method="POST" class="needs-validation" novalidate>
<div class="row">
<div class="col-md-6 mb-3">
<label for="nome" class="form-label">Nome</label>
<input type="text" class="form-control" id="nome" name="nome" value="{{ instancia.nome }}" required>
<div class="invalid-feedback">
Por favor, insira o nome da {{ tipo_instancia }}.
</div>
</div>
{% if tipo_instancia != 'Célula' %}
<div class="col-md-6 mb-3">
<label for="instancia_superior_id" class="form-label">{{ instancia_superior }}</label>
<select class="form-select" id="instancia_superior_id" name="instancia_superior_id" required>
<option value="">Selecione uma {{ instancia_superior }}</option>
{% for superior in instancias_superiores %}
<option value="{{ superior.id }}" {% if superior.id == instancia.instancia_superior_id %}selected{% endif %}>{{ superior.nome }}</option>
{% endfor %}
</select>
<div class="invalid-feedback">
Por favor, selecione uma {{ instancia_superior }}.
</div>
</div>
{% endif %}
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="responsavel_geral_id" class="form-label">Responsável Geral</label>
<select class="form-select" id="responsavel_geral_id" name="responsavel_geral_id" required>
<option value="">Selecione o responsável geral</option>
{% for militante in militantes %}
<option value="{{ militante.id }}" {% if militante.id == instancia.responsavel_geral_id %}selected{% endif %}>{{ militante.nome }}</option>
{% endfor %}
</select>
<div class="invalid-feedback">
Por favor, selecione o responsável geral.
</div>
</div>
<div class="col-md-6 mb-3">
<label for="responsavel_financas_id" class="form-label">Responsável de Finanças</label>
<select class="form-select" id="responsavel_financas_id" name="responsavel_financas_id">
<option value="">Selecione o responsável de finanças</option>
{% for militante in militantes %}
<option value="{{ militante.id }}" {% if militante.id == instancia.responsavel_financas_id %}selected{% endif %}>{{ militante.nome }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="responsavel_imprensa_id" class="form-label">Responsável de Imprensa</label>
<select class="form-select" id="responsavel_imprensa_id" name="responsavel_imprensa_id">
<option value="">Selecione o responsável de imprensa</option>
{% for militante in militantes %}
<option value="{{ militante.id }}" {% if militante.id == instancia.responsavel_imprensa_id %}selected{% endif %}>{{ militante.nome }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="d-flex justify-content-between mt-4">
<button type="submit" class="btn btn-primary">Salvar</button>
<a href="{{ url_for('listar_' + tipo_instancia.lower() + 's') }}" class="btn btn-secondary">Cancelar</a>
</div>
</form>
</div>
</div>
</div>
<script>
// Validação do formulário
(function () {
'use strict'
var forms = document.querySelectorAll('.needs-validation')
Array.prototype.slice.call(forms)
.forEach(function (form) {
form.addEventListener('submit', function (event) {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
}, false)
})
})()
</script>
{% endblock %}

View File

@@ -27,99 +27,107 @@
</div>
<div class="col-md-6 mb-3">
<label for="cpf" class="form-label">CPF</label>
<input type="text" class="form-control" id="cpf" name="cpf" value="{{ militante.cpf }}" required>
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" value="{{ militante.email }}" required>
<div class="invalid-feedback">
Por favor, insira o CPF do militante.
Por favor, insira um email válido.
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="cpf" class="form-label">CPF</label>
<input type="text" class="form-control" id="cpf" name="cpf" value="{{ militante.cpf }}" required>
<div class="invalid-feedback">
Por favor, insira o CPF do militante.
</div>
</div>
<div class="col-md-6 mb-3">
<label for="titulo_eleitoral" class="form-label">Título Eleitoral</label>
<input type="text" class="form-control" id="titulo_eleitoral" name="titulo_eleitoral" value="{{ militante.titulo_eleitoral }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="data_nascimento" class="form-label">Data de Nascimento</label>
<input type="date" class="form-control" id="data_nascimento" name="data_nascimento" value="{{ militante.data_nascimento.strftime('%Y-%m-%d') }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="data_entrada_oci" class="form-label">Data de Entrada na OCI</label>
<input type="date" class="form-control" id="data_entrada_oci" name="data_entrada_oci" value="{{ militante.data_entrada_oci.strftime('%Y-%m-%d') }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="data_efetivacao_oci" class="form-label">Data de Efetivação na OCI</label>
<input type="date" class="form-control" id="data_efetivacao_oci" name="data_efetivacao_oci" value="{{ militante.data_efetivacao_oci.strftime('%Y-%m-%d') }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="telefone1" class="form-label">Telefone 1</label>
<input type="text" class="form-control" id="telefone1" name="telefone1" value="{{ militante.telefone1 }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="telefone2" class="form-label">Telefone 2</label>
<input type="text" class="form-control" id="telefone2" name="telefone2" value="{{ militante.telefone2 }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="profissao" class="form-label">Profissão</label>
<input type="text" class="form-control" id="profissao" name="profissao" value="{{ militante.profissao }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="regime_trabalho" class="form-label">Regime de Trabalho</label>
<input type="text" class="form-control" id="regime_trabalho" name="regime_trabalho" value="{{ militante.regime_trabalho }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="empresa" class="form-label">Empresa</label>
<input type="text" class="form-control" id="empresa" name="empresa" value="{{ militante.empresa }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="contratante" class="form-label">Contratante</label>
<input type="text" class="form-control" id="contratante" name="contratante" value="{{ militante.contratante }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="instituicao_ensino" class="form-label">Instituição de Ensino</label>
<input type="text" class="form-control" id="instituicao_ensino" name="instituicao_ensino" value="{{ militante.instituicao_ensino }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="tipo_instituicao" class="form-label">Tipo de Instituição</label>
<input type="text" class="form-control" id="tipo_instituicao" name="tipo_instituicao" value="{{ militante.tipo_instituicao }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="sindicato" class="form-label">Sindicato</label>
<input type="text" class="form-control" id="sindicato" name="sindicato" value="{{ militante.sindicato }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="cargo_sindical" class="form-label">Cargo Sindical</label>
<input type="text" class="form-control" id="cargo_sindical" name="cargo_sindical" value="{{ militante.cargo_sindical }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="dirigente_sindical" name="dirigente_sindical" {% if militante.dirigente_sindical %}checked{% endif %}>
@@ -128,14 +136,14 @@
</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="central_sindical" class="form-label">Central Sindical</label>
<input type="text" class="form-control" id="central_sindical" name="central_sindical" value="{{ militante.central_sindical }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="setor_id" class="form-label">Setor</label>
<select class="form-select" id="setor_id" name="setor_id" required>
@@ -148,7 +156,9 @@
Por favor, selecione um setor.
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="celula_id" class="form-label">Célula</label>
<select class="form-select" id="celula_id" name="celula_id" required>
@@ -163,6 +173,30 @@
</div>
</div>
<div class="row">
<div class="col-md-12 mb-3">
<label class="form-label">Responsabilidades</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="responsavel_financas" name="responsabilidades" value="{{ Militante.RESPONSAVEL_FINANCAS }}" {% if militante.responsabilidades & Militante.RESPONSAVEL_FINANCAS %}checked{% endif %}>
<label class="form-check-label" for="responsavel_financas">
Responsável de Finanças
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="responsavel_imprensa" name="responsabilidades" value="{{ Militante.RESPONSAVEL_IMPRENSA }}" {% if militante.responsabilidades & Militante.RESPONSAVEL_IMPRENSA %}checked{% endif %}>
<label class="form-check-label" for="responsavel_imprensa">
Responsável de Imprensa
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="quadro_orientador" name="responsabilidades" value="{{ Militante.QUADRO_ORIENTADOR }}" {% if militante.responsabilidades & Militante.QUADRO_ORIENTADOR %}checked{% endif %}>
<label class="form-check-label" for="quadro_orientador">
Quadro-Orientador
</label>
</div>
</div>
</div>
<div class="d-flex justify-content-between mt-4">
<button type="submit" class="btn btn-primary">Salvar</button>
<a href="{{ url_for('listar_militantes') }}" class="btn btn-secondary">Cancelar</a>

View File

@@ -17,82 +17,16 @@
{% endwith %}
<div class="row">
{% for link in links %}
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">Militantes</h5>
</div>
<div class="card-body">
<p class="card-text">Gerencie os militantes da organização.</p>
<a href="{{ url_for('listar_militantes') }}" class="btn btn-primary">Listar Militantes</a>
<a href="{{ url_for('novo_militante') }}" class="btn btn-success">Novo Militante</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">Pagamentos</h5>
</div>
<div class="card-body">
<p class="card-text">Gerencie os pagamentos dos militantes.</p>
<a href="{{ url_for('listar_pagamentos') }}" class="btn btn-primary">Listar Pagamentos</a>
<a href="{{ url_for('novo_pagamento') }}" class="btn btn-success">Novo Pagamento</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">Materiais</h5>
</div>
<div class="card-body">
<p class="card-text">Gerencie os materiais da organização.</p>
<a href="{{ url_for('listar_materiais') }}" class="btn btn-primary">Listar Materiais</a>
<a href="{{ url_for('novo_material') }}" class="btn btn-success">Novo Material</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">Vendas</h5>
</div>
<div class="card-body">
<p class="card-text">Gerencie as vendas de materiais.</p>
<a href="{{ url_for('listar_relatorios_vendas') }}" class="btn btn-primary">Listar Vendas</a>
<a href="{{ url_for('novo_relatorio_vendas') }}" class="btn btn-success">Nova Venda</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">Relatórios</h5>
</div>
<div class="card-body">
<p class="card-text">Gerencie os relatórios da organização.</p>
<a href="{{ url_for('listar_relatorios_cotas') }}" class="btn btn-primary">Relatórios de Cotas</a>
<a href="{{ url_for('listar_relatorios_vendas') }}" class="btn btn-primary">Relatórios de Vendas</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">Configurações</h5>
</div>
<div class="card-body">
<p class="card-text">Gerencie as configurações da organização.</p>
<a href="{{ url_for('novo_usuario') }}" class="btn btn-primary">Novo Usuário</a>
<h5 class="card-title">{{ link.text }}</h5>
<a href="{{ link.url }}" class="btn btn-primary">Acessar</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>

View File

@@ -0,0 +1,79 @@
{% extends 'base.html' %}
{% block title %}Lista de {{ tipo_instancia }}s{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="mb-4">Lista de {{ tipo_instancia }}s</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="d-flex justify-content-between mb-4">
<a href="{{ url_for('criar_' + tipo_instancia.lower()) }}" class="btn btn-primary">Nova {{ tipo_instancia }}</a>
</div>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Nome</th>
{% if tipo_instancia != 'Célula' %}
<th>{{ instancia_superior }}</th>
{% endif %}
<th>Responsável Geral</th>
<th>Responsável de Finanças</th>
<th>Responsável de Imprensa</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{% for instancia in instancias %}
<tr>
<td>{{ instancia.nome }}</td>
{% if tipo_instancia != 'Célula' %}
<td>{{ instancia.instancia_superior.nome }}</td>
{% endif %}
<td>{{ instancia.responsavel_geral.nome }}</td>
<td>
{% if instancia.responsavel_financas %}
{{ instancia.responsavel_financas.nome }}
{% else %}
-
{% endif %}
</td>
<td>
{% if instancia.responsavel_imprensa %}
{{ instancia.responsavel_imprensa.nome }}
{% else %}
-
{% endif %}
</td>
<td>
<a href="{{ url_for('editar_' + tipo_instancia.lower(), id=instancia.id) }}" class="btn btn-sm btn-warning">Editar</a>
<button type="button" class="btn btn-sm btn-danger" onclick="confirmarExclusao({{ instancia.id }})">Excluir</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
function confirmarExclusao(id) {
if (confirm('Tem certeza que deseja excluir esta {{ tipo_instancia }}?')) {
window.location.href = "{{ url_for('excluir_' + tipo_instancia.lower(), id=0) }}".replace('0', id);
}
}
</script>
{% endblock %}

View File

@@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% block title %}Listar Militantes{% endblock %}
{% block title %}Lista de Militantes{% endblock %}
{% block content %}
<div class="container">
@@ -17,67 +17,42 @@
{% endwith %}
<div class="d-flex justify-content-between mb-4">
<a href="{{ url_for('novo_militante') }}" class="btn btn-success">Novo Militante</a>
<a href="{{ url_for('home') }}" class="btn btn-outline-primary">Início</a>
<a href="{{ url_for('criar_militante') }}" class="btn btn-primary">Novo Militante</a>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover">
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Nome</th>
<th>CPF</th>
<th>Título Eleitoral</th>
<th>Data de Nascimento</th>
<th>Data de Entrada</th>
<th>Data de Efetivação</th>
<th>Telefone 1</th>
<th>Telefone 2</th>
<th>Profissão</th>
<th>Regime de Trabalho</th>
<th>Empresa</th>
<th>Contratante</th>
<th>Instituição de Ensino</th>
<th>Tipo de Instituição</th>
<th>Sindicato</th>
<th>Cargo Sindical</th>
<th>Dirigente Sindical</th>
<th>Central Sindical</th>
<th>Setor</th>
<th>Email</th>
<th>Célula</th>
<th>Responsabilidades</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{% for militante in militantes %}
<tr>
<td>{{ militante.id }}</td>
<td>{{ militante.nome }}</td>
<td>{{ militante.cpf }}</td>
<td>{{ militante.titulo_eleitoral }}</td>
<td>{{ militante.data_nascimento.strftime('%d/%m/%Y') }}</td>
<td>{{ militante.data_entrada_oci.strftime('%d/%m/%Y') }}</td>
<td>{{ militante.data_efetivacao_oci.strftime('%d/%m/%Y') }}</td>
<td>{{ militante.telefone1 }}</td>
<td>{{ militante.telefone2 }}</td>
<td>{{ militante.profissao }}</td>
<td>{{ militante.regime_trabalho }}</td>
<td>{{ militante.empresa }}</td>
<td>{{ militante.contratante }}</td>
<td>{{ militante.instituicao_ensino }}</td>
<td>{{ militante.tipo_instituicao }}</td>
<td>{{ militante.sindicato }}</td>
<td>{{ militante.cargo_sindical }}</td>
<td>{{ 'Sim' if militante.dirigente_sindical else 'Não' }}</td>
<td>{{ militante.central_sindical }}</td>
<td>{{ militante.setor.nome }}</td>
<td>{{ militante.celula.nome }}</td>
<td>
<a href="{{ url_for('editar_militante', id=militante.id) }}" class="btn btn-primary btn-sm">Editar</a>
<a href="{{ url_for('deletar_militante', id=militante.id) }}" class="btn btn-danger btn-sm" onclick="return confirm('Tem certeza que deseja excluir este militante?')">Excluir</a>
</td>
</tr>
<tr>
<td>{{ militante.nome }}</td>
<td>{{ militante.email }}</td>
<td>{{ militante.celula.nome }}</td>
<td>
{% if militante.responsabilidades & Militante.RESPONSAVEL_FINANCAS %}
<span class="badge bg-primary">Finanças</span>
{% endif %}
{% if militante.responsabilidades & Militante.RESPONSAVEL_IMPRENSA %}
<span class="badge bg-info">Imprensa</span>
{% endif %}
{% if militante.responsabilidades & Militante.QUADRO_ORIENTADOR %}
<span class="badge bg-success">Quadro-Orientador</span>
{% endif %}
</td>
<td>
<a href="{{ url_for('editar_militante', id=militante.id) }}" class="btn btn-sm btn-warning">Editar</a>
<button type="button" class="btn btn-sm btn-danger" onclick="confirmarExclusao({{ militante.id }})">Excluir</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
@@ -85,4 +60,12 @@
</div>
</div>
</div>
<script>
function confirmarExclusao(id) {
if (confirm('Tem certeza que deseja excluir este militante?')) {
window.location.href = "{{ url_for('excluir_militante', id=0) }}".replace('0', id);
}
}
</script>
{% endblock %}

View File

@@ -4,43 +4,38 @@
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
<div class="card mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3 class="text-center">Login</h3>
<h3 class="card-title">Login</h3>
</div>
<div class="card-body">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">
{{ message }}
{% if category == 'danger' %}
<br>
<small>Se o problema persistir, contate o administrador.</small>
{% endif %}
</div>
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST">
<form method="POST" action="{{ url_for('login') }}">
<div class="mb-3">
<label for="username" class="form-label">Usuário</label>
<input type="text" class="form-control" id="username" name="username" required
value="{{ request.form.username }}">
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Senha</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-3">
<label for="otp" class="form-label">Código OTP</label>
<input type="text" class="form-control" id="otp" name="otp" required
pattern="[0-9]{6}" title="Digite o código de 6 dígitos">
<small class="form-text text-muted">Digite o código de 6 dígitos do seu aplicativo autenticador</small>
<label for="otp_code" class="form-label">Código OTP</label>
<input type="text" class="form-control" id="otp_code" name="otp_code" required>
<small class="text-muted">Digite o código gerado pelo seu aplicativo autenticador</small>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Entrar</button>
</div>

View File

@@ -22,6 +22,12 @@
<input type="text" class="form-control" id="nome" name="nome" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email:</label>
<input type="email" class="form-control" id="email" name="email" required>
<small class="form-text text-muted">Este email será usado para login e comunicação do sistema</small>
</div>
<div class="mb-3">
<label for="cpf" class="form-label">CPF:</label>
<input type="text" class="form-control" id="cpf" name="cpf" required>
@@ -115,25 +121,67 @@
</div>
<div class="mb-3">
<label for="setor_id" class="form-label">Setor:</label>
<select class="form-select" id="setor_id" name="setor_id" required>
<option value="">Selecione o setor</option>
{% for setor in setores %}
<option value="{{ setor.id }}">{{ setor.nome }}</option>
<label for="cr_id" class="form-label">Comitê Regional:</label>
<select class="form-select" id="cr_id" name="cr_id" required>
<option value="">Selecione o CR</option>
{% for cr in crs %}
<option value="{{ cr.id }}">{{ cr.nome }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="celula_id" class="form-label">Célula:</label>
<select class="form-select" id="celula_id" name="celula_id" required>
<option value="">Selecione a célula</option>
{% for celula in celulas %}
<option value="{{ celula.id }}">{{ celula.nome }}</option>
<label for="setor_id" class="form-label">Setor:</label>
<select class="form-select" id="setor_id" name="setor_id" required>
<option value="">Selecione o setor</option>
{% for setor in setores %}
<option value="{{ setor.id }}" data-cr="{{ setor.cr_id }}">{{ setor.nome }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label class="form-label">Célula:</label>
{% if pode_criar_celula %}
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="celula_opcao" id="celula_existente" value="existente" checked>
<label class="form-check-label" for="celula_existente">
Usar célula existente
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="celula_opcao" id="celula_nova" value="nova">
<label class="form-check-label" for="celula_nova">
Criar nova célula
</label>
</div>
</div>
{% endif %}
<div id="celula_existente_container">
<select class="form-select" id="celula_id" name="celula_id" required>
<option value="">Selecione a célula</option>
{% for celula in celulas %}
<option value="{{ celula.id }}" data-setor="{{ celula.setor_id }}">{{ celula.nome }}</option>
{% endfor %}
</select>
</div>
{% if pode_criar_celula %}
<div id="celula_nova_container" style="display: none;">
<div class="mb-3">
<label for="nova_celula_nome" class="form-label">Nome da Nova Célula:</label>
<input type="text" class="form-control" id="nova_celula_nome" name="nova_celula_nome">
</div>
<div class="mb-3">
<label for="nova_celula_quadro_orientador" class="form-label">Quadro Orientador:</label>
<input type="text" class="form-control" id="nova_celula_quadro_orientador" name="nova_celula_quadro_orientador">
</div>
</div>
{% endif %}
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">Registrar</button>
<a href="{{ url_for('listar_militantes') }}" class="btn btn-secondary">Voltar</a>
@@ -143,5 +191,66 @@
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Função para atualizar as células baseado no setor selecionado
function atualizarCelulas() {
const setorId = document.getElementById('setor_id').value;
const celulaSelect = document.getElementById('celula_id');
// Limpar opções existentes
celulaSelect.innerHTML = '<option value="">Selecione a célula</option>';
// Adicionar apenas células do setor selecionado
document.querySelectorAll('#celula_id option').forEach(option => {
if (option.dataset.setor === setorId) {
celulaSelect.appendChild(option.cloneNode(true));
}
});
}
// Função para atualizar os setores baseado no CR selecionado
function atualizarSetores() {
const crId = document.getElementById('cr_id').value;
const setorSelect = document.getElementById('setor_id');
// Limpar opções existentes
setorSelect.innerHTML = '<option value="">Selecione o setor</option>';
// Adicionar apenas setores do CR selecionado
document.querySelectorAll('#setor_id option').forEach(option => {
if (option.dataset.cr === crId) {
setorSelect.appendChild(option.cloneNode(true));
}
});
// Atualizar células após mudar o setor
atualizarCelulas();
}
// Event listeners
document.getElementById('cr_id').addEventListener('change', atualizarSetores);
document.getElementById('setor_id').addEventListener('change', atualizarCelulas);
// Toggle entre célula existente e nova
const celulaExistente = document.getElementById('celula_existente');
const celulaNova = document.getElementById('celula_nova');
const celulaExistenteContainer = document.getElementById('celula_existente_container');
const celulaNovaContainer = document.getElementById('celula_nova_container');
if (celulaExistente && celulaNova) {
celulaExistente.addEventListener('change', function() {
celulaExistenteContainer.style.display = 'block';
celulaNovaContainer.style.display = 'none';
});
celulaNova.addEventListener('change', function() {
celulaExistenteContainer.style.display = 'none';
celulaNovaContainer.style.display = 'block';
});
}
});
</script>
{% endblock %}

View File

@@ -1,74 +0,0 @@
{% extends 'base.html' %}
{% block title %}Novo Usuário{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-8 offset-md-2">
<h1 class="mb-4">Novo Usuário</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 %}
<form method="post" class="mb-4">
<div class="mb-3">
<label for="militante_id" class="form-label">Militante:</label>
<select class="form-select" id="militante_id" name="militante_id" required>
<option value="">Selecione o militante</option>
{% for militante in militantes %}
<option value="{{ militante.id }}">{{ militante.nome }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="setor_id" class="form-label">Setor:</label>
<select class="form-select" id="setor_id" name="setor_id" required>
<option value="">Selecione o setor</option>
{% for setor in setores %}
<option value="{{ setor.id }}">{{ setor.nome }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="comite_id" class="form-label">Comitê Central:</label>
<select class="form-select" id="comite_id" name="comite_id" required>
<option value="">Selecione o comitê</option>
{% for comite in comites %}
<option value="{{ comite.id }}">{{ comite.nome }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="username" class="form-label">Nome de Usuário:</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Senha:</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-3">
<label for="confirm_password" class="form-label">Confirmar Senha:</label>
<input type="password" class="form-control" id="confirm_password" name="confirm_password" required>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">Registrar</button>
<a href="{{ url_for('listar_usuarios') }}" class="btn btn-secondary">Voltar</a>
<a href="{{ url_for('home') }}" class="btn btn-outline-primary">Início</a>
</div>
</form>
</div>
</div>
</div>
{% endblock %}