feat: Melhorias de segurança e interface - Segurança: Implementação de CSRF token em formulários, validação no backend, proteção AJAX - QR Code: Preservação do otp_secret, evita geração desnecessária - Interface: Correções visuais, padronização de cores, melhorias em formulários
This commit is contained in:
483
app.py
483
app.py
@@ -21,6 +21,7 @@ from functions.database import (
|
|||||||
EmailMilitante,
|
EmailMilitante,
|
||||||
init_database,
|
init_database,
|
||||||
EstadoMilitante,
|
EstadoMilitante,
|
||||||
|
Endereco,
|
||||||
)
|
)
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import sessionmaker, joinedload
|
from sqlalchemy.orm import sessionmaker, joinedload
|
||||||
@@ -48,6 +49,7 @@ from flask_login import LoginManager, UserMixin, login_user, logout_user, login_
|
|||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
|
from flask_wtf.csrf import CSRFProtect
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
@@ -55,6 +57,9 @@ app = Flask(__name__)
|
|||||||
app.secret_key = os.getenv('SECRET_KEY', secrets.token_hex(16))
|
app.secret_key = os.getenv('SECRET_KEY', secrets.token_hex(16))
|
||||||
bootstrap = Bootstrap5(app)
|
bootstrap = Bootstrap5(app)
|
||||||
|
|
||||||
|
# Configurar CSRF Protection
|
||||||
|
csrf = CSRFProtect(app)
|
||||||
|
|
||||||
# Configurar Flask-Login
|
# Configurar Flask-Login
|
||||||
login_manager = LoginManager()
|
login_manager = LoginManager()
|
||||||
login_manager.init_app(app)
|
login_manager.init_app(app)
|
||||||
@@ -310,79 +315,108 @@ def home():
|
|||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
# Rota para criar um novo militante
|
# Rota para criar um novo militante
|
||||||
@app.route('/militantes/criar', methods=['GET', 'POST'])
|
@app.route("/militantes/criar", methods=["POST"])
|
||||||
@require_login
|
@require_login
|
||||||
@require_permission('gerenciar_militantes')
|
@require_permission('gerenciar_militantes')
|
||||||
def criar_militante():
|
def criar_militante():
|
||||||
if request.method == 'POST':
|
"""Cria um novo militante"""
|
||||||
|
db = get_db_connection()
|
||||||
try:
|
try:
|
||||||
nome = request.form['nome']
|
# Validar CPF
|
||||||
email = request.form['email']
|
cpf = request.form.get('cpf')
|
||||||
cpf = request.form['cpf']
|
if not validar_cpf(cpf):
|
||||||
titulo_eleitoral = request.form['titulo_eleitoral']
|
return jsonify({
|
||||||
data_nascimento = datetime.strptime(request.form['data_nascimento'], '%Y-%m-%d').date() if request.form['data_nascimento'] else None
|
'status': 'error',
|
||||||
data_entrada_oci = datetime.strptime(request.form['data_entrada_oci'], '%Y-%m-%d').date() if request.form['data_entrada_oci'] else None
|
'message': 'CPF inválido'
|
||||||
data_efetivacao_oci = datetime.strptime(request.form['data_efetivacao_oci'], '%Y-%m-%d').date() if request.form['data_efetivacao_oci'] else None
|
}), 400
|
||||||
telefone1 = request.form['telefone1']
|
|
||||||
telefone2 = request.form['telefone2']
|
|
||||||
profissao = request.form['profissao']
|
|
||||||
regime_trabalho = request.form['regime_trabalho']
|
|
||||||
empresa = request.form['empresa']
|
|
||||||
contratante = request.form['contratante']
|
|
||||||
instituicao_ensino = request.form['instituicao_ensino']
|
|
||||||
tipo_instituicao = request.form['tipo_instituicao']
|
|
||||||
sindicato = request.form['sindicato']
|
|
||||||
cargo_sindical = request.form['cargo_sindical']
|
|
||||||
dirigente_sindical = 'dirigente_sindical' in request.form
|
|
||||||
central_sindical = request.form['central_sindical']
|
|
||||||
setor_id = request.form['setor_id']
|
|
||||||
celula_id = request.form['celula_id']
|
|
||||||
|
|
||||||
# Processar responsabilidades
|
# Verificar se já existe militante com este CPF
|
||||||
responsabilidades = 0
|
militante_existente = db.query(Militante).filter(Militante.cpf == cpf).first()
|
||||||
if 'responsabilidades' in request.form:
|
if militante_existente:
|
||||||
for responsabilidade in request.form.getlist('responsabilidades'):
|
return jsonify({
|
||||||
responsabilidades |= int(responsabilidade)
|
'status': 'error',
|
||||||
|
'message': 'CPF já cadastrado'
|
||||||
|
}), 400
|
||||||
|
|
||||||
militante = Militante(
|
# Criar endereço
|
||||||
nome=nome,
|
endereco = Endereco(
|
||||||
email=email,
|
cep=request.form.get('cep'),
|
||||||
cpf=cpf,
|
estado=request.form.get('estado'),
|
||||||
titulo_eleitoral=titulo_eleitoral,
|
cidade=request.form.get('cidade'),
|
||||||
data_nascimento=data_nascimento,
|
bairro=request.form.get('bairro'),
|
||||||
data_entrada_oci=data_entrada_oci,
|
logradouro=request.form.get('logradouro'),
|
||||||
data_efetivacao_oci=data_efetivacao_oci,
|
numero=request.form.get('numero'),
|
||||||
telefone1=telefone1,
|
complemento=request.form.get('complemento')
|
||||||
telefone2=telefone2,
|
|
||||||
profissao=profissao,
|
|
||||||
regime_trabalho=regime_trabalho,
|
|
||||||
empresa=empresa,
|
|
||||||
contratante=contratante,
|
|
||||||
instituicao_ensino=instituicao_ensino,
|
|
||||||
tipo_instituicao=tipo_instituicao,
|
|
||||||
sindicato=sindicato,
|
|
||||||
cargo_sindical=cargo_sindical,
|
|
||||||
dirigente_sindical=dirigente_sindical,
|
|
||||||
central_sindical=central_sindical,
|
|
||||||
setor_id=setor_id,
|
|
||||||
celula_id=celula_id,
|
|
||||||
responsabilidades=responsabilidades
|
|
||||||
)
|
)
|
||||||
|
db.add(endereco)
|
||||||
|
db.flush() # Gerar ID do endereço
|
||||||
|
|
||||||
db_session.add(militante)
|
# Criar militante
|
||||||
db_session.commit()
|
militante = Militante(
|
||||||
|
# Dados Básicos
|
||||||
|
nome=request.form.get('nome'),
|
||||||
|
cpf=cpf,
|
||||||
|
titulo_eleitoral=request.form.get('titulo_eleitoral'),
|
||||||
|
data_nascimento=datetime.strptime(request.form.get('data_nascimento'), '%Y-%m-%d') if request.form.get('data_nascimento') else None,
|
||||||
|
data_entrada_oci=datetime.strptime(request.form.get('data_entrada_oci'), '%Y-%m-%d') if request.form.get('data_entrada_oci') else None,
|
||||||
|
data_efetivacao_oci=datetime.strptime(request.form.get('data_efetivacao_oci'), '%Y-%m-%d') if request.form.get('data_efetivacao_oci') else None,
|
||||||
|
|
||||||
flash('Militante criado com sucesso!', 'success')
|
# Contato
|
||||||
return redirect(url_for('listar_militantes'))
|
telefone1=request.form.get('telefone1'),
|
||||||
|
telefone2=request.form.get('telefone2'),
|
||||||
|
endereco_id=endereco.id,
|
||||||
|
|
||||||
|
# Profissional
|
||||||
|
profissao=request.form.get('profissao'),
|
||||||
|
regime_trabalho=request.form.get('regime_trabalho'),
|
||||||
|
empresa=request.form.get('empresa'),
|
||||||
|
contratante=request.form.get('contratante'),
|
||||||
|
|
||||||
|
# Acadêmico
|
||||||
|
instituicao_ensino=request.form.get('instituicao_ensino'),
|
||||||
|
tipo_instituicao=request.form.get('tipo_instituicao'),
|
||||||
|
|
||||||
|
# Sindical
|
||||||
|
sindicato=request.form.get('sindicato'),
|
||||||
|
cargo_sindical=request.form.get('cargo_sindical'),
|
||||||
|
central_sindical=request.form.get('central_sindical'),
|
||||||
|
dirigente_sindical=request.form.get('dirigente_sindical') == 'on',
|
||||||
|
|
||||||
|
# Organização
|
||||||
|
estado=EstadoMilitante(request.form.get('estado', 'ATIVO')),
|
||||||
|
celula_id=request.form.get('celula_id', type=int),
|
||||||
|
responsabilidades=request.form.get('responsabilidades', type=int, default=0),
|
||||||
|
|
||||||
|
# Por padrão, todo novo militante é aspirante
|
||||||
|
aspirante=True,
|
||||||
|
data_inicio_aspirante=datetime.now()
|
||||||
|
)
|
||||||
|
db.add(militante)
|
||||||
|
db.flush() # Gerar ID do militante
|
||||||
|
|
||||||
|
# Criar email principal
|
||||||
|
email = EmailMilitante(
|
||||||
|
email=request.form.get('email'),
|
||||||
|
principal=True,
|
||||||
|
militante_id=militante.id
|
||||||
|
)
|
||||||
|
db.add(email)
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'status': 'success',
|
||||||
|
'message': 'Militante criado com sucesso!'
|
||||||
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db_session.rollback()
|
db.rollback()
|
||||||
flash(f'Erro ao criar militante: {str(e)}', 'danger')
|
return jsonify({
|
||||||
return redirect(url_for('criar_militante'))
|
'status': 'error',
|
||||||
|
'message': f'Erro ao criar militante: {str(e)}'
|
||||||
setores = Setor.query.all()
|
}), 500
|
||||||
celulas = Celula.query.all()
|
finally:
|
||||||
return render_template('criar_militante.html', setores=setores, celulas=celulas)
|
db.close()
|
||||||
|
|
||||||
# Rota para listar militantes
|
# Rota para listar militantes
|
||||||
@app.route("/militantes")
|
@app.route("/militantes")
|
||||||
@@ -904,88 +938,150 @@ def listar_relatorios_vendas():
|
|||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
# Rota para editar militante
|
# Rota para editar militante
|
||||||
@app.route("/militantes/editar/<int:id>", methods=["GET", "POST"])
|
@app.route("/militantes/editar/<int:militante_id>", methods=["POST"])
|
||||||
@login_required
|
@require_login
|
||||||
@session_timeout
|
@require_permission('gerenciar_militantes')
|
||||||
def editar_militante(id):
|
def editar_militante(militante_id):
|
||||||
db = get_db_connection()
|
"""Edita um militante existente"""
|
||||||
try:
|
print(f"Iniciando edição do militante {militante_id}")
|
||||||
militante = db.query(Militante).get(id)
|
print(f"Dados recebidos: {request.form}")
|
||||||
if not militante:
|
|
||||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
|
||||||
return jsonify({'status': 'error', 'message': 'Militante não encontrado'}), 404
|
|
||||||
flash('Militante não encontrado', 'danger')
|
|
||||||
return redirect(url_for('listar_militantes'))
|
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
nome = request.form.get("nome")
|
db = get_db_connection()
|
||||||
cpf = request.form.get("cpf")
|
|
||||||
email = request.form.get("email")
|
|
||||||
telefone = request.form.get("telefone")
|
|
||||||
endereco = request.form.get("endereco")
|
|
||||||
filiado = request.form.get("filiado") == "on"
|
|
||||||
|
|
||||||
# Validar CPF
|
|
||||||
if not validar_cpf(cpf):
|
|
||||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
|
||||||
return jsonify({'status': 'error', 'message': 'CPF inválido'}), 400
|
|
||||||
flash('CPF inválido', 'danger')
|
|
||||||
return render_template('editar_militante.html', militante=militante)
|
|
||||||
|
|
||||||
# Verificar se já existe outro militante com este CPF
|
|
||||||
militante_existente = db.query(Militante).filter(
|
|
||||||
Militante.cpf == cpf,
|
|
||||||
Militante.id != id
|
|
||||||
).first()
|
|
||||||
if militante_existente:
|
|
||||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
|
||||||
return jsonify({'status': 'error', 'message': 'CPF já cadastrado para outro militante'}), 400
|
|
||||||
flash('CPF já cadastrado para outro militante', 'danger')
|
|
||||||
return render_template('editar_militante.html', militante=militante)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
militante.nome = nome
|
militante = db.query(Militante).options(
|
||||||
militante.cpf = cpf
|
joinedload(Militante.endereco),
|
||||||
militante.email = email
|
joinedload(Militante.emails)
|
||||||
militante.telefone = telefone
|
).get(militante_id)
|
||||||
militante.endereco = endereco
|
|
||||||
militante.filiado = filiado
|
if not militante:
|
||||||
db.commit()
|
print(f"Militante {militante_id} não encontrado")
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'Militante não encontrado'
|
||||||
|
}), 404
|
||||||
|
|
||||||
|
print(f"Militante encontrado: {militante.nome}")
|
||||||
|
|
||||||
|
# Dados Básicos
|
||||||
|
militante.nome = request.form.get('nome')
|
||||||
|
militante.cpf = request.form.get('cpf')
|
||||||
|
militante.titulo_eleitoral = request.form.get('titulo_eleitoral')
|
||||||
|
militante.data_nascimento = datetime.strptime(request.form.get('data_nascimento'), '%Y-%m-%d') if request.form.get('data_nascimento') else None
|
||||||
|
militante.data_entrada_oci = datetime.strptime(request.form.get('data_entrada_oci'), '%Y-%m-%d') if request.form.get('data_entrada_oci') else None
|
||||||
|
militante.data_efetivacao_oci = datetime.strptime(request.form.get('data_efetivacao_oci'), '%Y-%m-%d') if request.form.get('data_efetivacao_oci') else None
|
||||||
|
|
||||||
|
print("Dados básicos atualizados")
|
||||||
|
|
||||||
|
# Contato
|
||||||
|
militante.telefone1 = request.form.get('telefone1')
|
||||||
|
militante.telefone2 = request.form.get('telefone2')
|
||||||
|
|
||||||
|
# Email (atualizar o principal)
|
||||||
|
email = request.form.get('email')
|
||||||
|
if email:
|
||||||
|
if militante.emails:
|
||||||
|
militante.emails[0].endereco_email = email
|
||||||
|
else:
|
||||||
|
novo_email = EmailMilitante(
|
||||||
|
endereco_email=email,
|
||||||
|
militante_id=militante.id,
|
||||||
|
principal=True
|
||||||
|
)
|
||||||
|
db.add(novo_email)
|
||||||
|
|
||||||
|
print("Dados de contato atualizados")
|
||||||
|
|
||||||
|
# Endereço
|
||||||
|
if not militante.endereco:
|
||||||
|
militante.endereco = Endereco()
|
||||||
|
db.add(militante.endereco)
|
||||||
|
|
||||||
|
militante.endereco.cep = request.form.get('cep')
|
||||||
|
militante.endereco.estado = request.form.get('estado')
|
||||||
|
militante.endereco.cidade = request.form.get('cidade')
|
||||||
|
militante.endereco.bairro = request.form.get('bairro')
|
||||||
|
militante.endereco.rua = request.form.get('rua')
|
||||||
|
militante.endereco.numero = request.form.get('numero')
|
||||||
|
militante.endereco.complemento = request.form.get('complemento')
|
||||||
|
|
||||||
|
print("Dados de endereço atualizados")
|
||||||
|
|
||||||
|
# Profissional
|
||||||
|
militante.profissao = request.form.get('profissao')
|
||||||
|
militante.regime_trabalho = request.form.get('regime_trabalho')
|
||||||
|
militante.empresa = request.form.get('empresa')
|
||||||
|
militante.contratante = request.form.get('contratante')
|
||||||
|
|
||||||
|
# Acadêmico
|
||||||
|
militante.instituicao_ensino = request.form.get('instituicao_ensino')
|
||||||
|
militante.tipo_instituicao = request.form.get('tipo_instituicao')
|
||||||
|
|
||||||
|
# Sindical
|
||||||
|
militante.sindicato = request.form.get('sindicato')
|
||||||
|
militante.cargo_sindical = request.form.get('cargo_sindical')
|
||||||
|
militante.central_sindical = request.form.get('central_sindical')
|
||||||
|
militante.dirigente_sindical = request.form.get('dirigente_sindical') == 'on'
|
||||||
|
|
||||||
|
print("Dados profissionais e sindicais atualizados")
|
||||||
|
|
||||||
|
# Organização
|
||||||
|
estado_str = request.form.get('estado', 'ATIVO').lower()
|
||||||
|
print(f"Estado recebido: {estado_str}")
|
||||||
|
|
||||||
|
# Converter o estado para o enum
|
||||||
|
try:
|
||||||
|
militante.estado = EstadoMilitante[estado_str.upper()]
|
||||||
|
print(f"Estado convertido: {militante.estado}")
|
||||||
|
except (KeyError, ValueError) as e:
|
||||||
|
print(f"Erro ao converter estado: {str(e)}")
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
'message': f'Estado inválido: {estado_str}'
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
# Tratar celula_id corretamente
|
||||||
|
celula_id = request.form.get('celula_id')
|
||||||
|
if celula_id:
|
||||||
|
try:
|
||||||
|
militante.celula_id = int(celula_id)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
militante.celula_id = None
|
||||||
|
else:
|
||||||
|
militante.celula_id = None
|
||||||
|
|
||||||
|
# Tratar responsabilidades corretamente
|
||||||
|
responsabilidades = request.form.getlist('responsabilidades')
|
||||||
|
if responsabilidades:
|
||||||
|
try:
|
||||||
|
militante.responsabilidades = sum(int(r) for r in responsabilidades)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
militante.responsabilidades = 0
|
||||||
|
else:
|
||||||
|
militante.responsabilidades = 0
|
||||||
|
|
||||||
|
print("Dados organizacionais atualizados")
|
||||||
|
|
||||||
|
# Se o estado mudou para DESLIGADO, registrar data e motivo
|
||||||
|
if militante.estado == EstadoMilitante.DESLIGADO:
|
||||||
|
militante.data_desligamento = datetime.now()
|
||||||
|
militante.motivo_desligamento = request.form.get('motivo_desligamento')
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
print("Alterações salvas com sucesso")
|
||||||
|
|
||||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 'success',
|
'status': 'success',
|
||||||
'message': 'Militante atualizado com sucesso',
|
'message': f'Militante {militante.nome} atualizado com sucesso!'
|
||||||
'militante': {
|
|
||||||
'id': militante.id,
|
|
||||||
'nome': militante.nome,
|
|
||||||
'cpf': militante.cpf,
|
|
||||||
'email': militante.email,
|
|
||||||
'telefone': militante.telefone,
|
|
||||||
'endereco': militante.endereco,
|
|
||||||
'filiado': militante.filiado
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
flash('Militante atualizado com sucesso!', 'success')
|
|
||||||
return redirect(url_for('listar_militantes'))
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(f"Erro ao atualizar militante: {str(e)}")
|
||||||
db.rollback()
|
db.rollback()
|
||||||
print(f"Erro ao atualizar militante: {e}")
|
return jsonify({
|
||||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
'status': 'error',
|
||||||
return jsonify({'status': 'error', 'message': 'Erro ao atualizar militante'}), 500
|
'message': f'Erro ao atualizar militante: {str(e)}'
|
||||||
flash('Erro ao atualizar militante', 'danger')
|
}), 500
|
||||||
return render_template('editar_militante.html', militante=militante)
|
|
||||||
|
|
||||||
return render_template('editar_militante.html', militante=militante)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Erro ao editar militante: {e}")
|
|
||||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
|
||||||
return jsonify({'status': 'error', 'message': 'Erro ao carregar militante'}), 500
|
|
||||||
flash('Erro ao carregar militante', 'danger')
|
|
||||||
return redirect(url_for('listar_militantes'))
|
|
||||||
finally:
|
finally:
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
@@ -1459,6 +1555,98 @@ def excluir_assinatura(id):
|
|||||||
finally:
|
finally:
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
@app.route("/militantes/<int:militante_id>/dados")
|
||||||
|
@require_login
|
||||||
|
@require_permission('gerenciar_militantes')
|
||||||
|
def get_militante_dados(militante_id):
|
||||||
|
"""Retorna os dados completos de um militante"""
|
||||||
|
print(f"Buscando dados do militante {militante_id}")
|
||||||
|
db = get_db_connection()
|
||||||
|
try:
|
||||||
|
militante = db.query(Militante).options(
|
||||||
|
joinedload(Militante.endereco),
|
||||||
|
joinedload(Militante.emails),
|
||||||
|
joinedload(Militante.celula)
|
||||||
|
).get(militante_id)
|
||||||
|
|
||||||
|
if not militante:
|
||||||
|
print(f"Militante {militante_id} não encontrado")
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'Militante não encontrado'
|
||||||
|
}), 404
|
||||||
|
|
||||||
|
print(f"Militante {militante_id} encontrado: {militante.nome}")
|
||||||
|
|
||||||
|
# Formatar datas para o formato YYYY-MM-DD
|
||||||
|
data_nascimento = militante.data_nascimento.strftime('%Y-%m-%d') if militante.data_nascimento else None
|
||||||
|
data_entrada = militante.data_entrada_oci.strftime('%Y-%m-%d') if militante.data_entrada_oci else None
|
||||||
|
data_efetivacao = militante.data_efetivacao_oci.strftime('%Y-%m-%d') if militante.data_efetivacao_oci else None
|
||||||
|
|
||||||
|
# Buscar o primeiro email do militante
|
||||||
|
email_principal = None
|
||||||
|
if militante.emails and len(militante.emails) > 0:
|
||||||
|
email_principal = militante.emails[0].endereco_email
|
||||||
|
|
||||||
|
dados = {
|
||||||
|
'id': militante.id,
|
||||||
|
'nome': militante.nome,
|
||||||
|
'cpf': militante.cpf,
|
||||||
|
'titulo_eleitoral': militante.titulo_eleitoral,
|
||||||
|
'data_nascimento': data_nascimento,
|
||||||
|
'data_entrada_oci': data_entrada,
|
||||||
|
'data_efetivacao_oci': data_efetivacao,
|
||||||
|
|
||||||
|
# Contato
|
||||||
|
'telefone1': militante.telefone1,
|
||||||
|
'telefone2': militante.telefone2,
|
||||||
|
'email': email_principal,
|
||||||
|
|
||||||
|
# Endereço
|
||||||
|
'endereco': {
|
||||||
|
'cep': militante.endereco.cep if militante.endereco else None,
|
||||||
|
'estado': militante.endereco.estado if militante.endereco else None,
|
||||||
|
'cidade': militante.endereco.cidade if militante.endereco else None,
|
||||||
|
'bairro': militante.endereco.bairro if militante.endereco else None,
|
||||||
|
'rua': militante.endereco.rua if militante.endereco else None,
|
||||||
|
'numero': militante.endereco.numero if militante.endereco else None,
|
||||||
|
'complemento': militante.endereco.complemento if militante.endereco else None
|
||||||
|
} if militante.endereco else None,
|
||||||
|
|
||||||
|
# Profissional
|
||||||
|
'profissao': militante.profissao,
|
||||||
|
'regime_trabalho': militante.regime_trabalho,
|
||||||
|
'empresa': militante.empresa,
|
||||||
|
'contratante': militante.contratante,
|
||||||
|
|
||||||
|
# Acadêmico
|
||||||
|
'instituicao_ensino': militante.instituicao_ensino,
|
||||||
|
'tipo_instituicao': militante.tipo_instituicao,
|
||||||
|
|
||||||
|
# Sindical
|
||||||
|
'sindicato': militante.sindicato,
|
||||||
|
'cargo_sindical': militante.cargo_sindical,
|
||||||
|
'central_sindical': militante.central_sindical,
|
||||||
|
'dirigente_sindical': militante.dirigente_sindical,
|
||||||
|
|
||||||
|
# Organização
|
||||||
|
'estado': militante.estado.value if militante.estado else 'ATIVO',
|
||||||
|
'celula_id': militante.celula_id,
|
||||||
|
'responsabilidades': militante.responsabilidades
|
||||||
|
}
|
||||||
|
|
||||||
|
print(f"Dados formatados: {dados}")
|
||||||
|
return jsonify(dados)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erro ao buscar dados do militante {militante_id}: {str(e)}")
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
'message': f'Erro ao buscar dados do militante: {str(e)}'
|
||||||
|
}), 500
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
# ... existing code ...
|
# ... existing code ...
|
||||||
@@ -1474,13 +1662,6 @@ def init_system():
|
|||||||
print("Inicializando banco de dados...")
|
print("Inicializando banco de dados...")
|
||||||
init_database()
|
init_database()
|
||||||
|
|
||||||
# Criar admin
|
|
||||||
create_admin_user()
|
|
||||||
|
|
||||||
# Criar usuários de teste
|
|
||||||
create_test_users()
|
|
||||||
|
|
||||||
# Verificar configuração
|
|
||||||
db = get_db_connection()
|
db = get_db_connection()
|
||||||
try:
|
try:
|
||||||
# Verificar admin
|
# Verificar admin
|
||||||
@@ -1489,10 +1670,15 @@ def init_system():
|
|||||||
print("\nAdmin configurado:")
|
print("\nAdmin configurado:")
|
||||||
print(f"Username: admin")
|
print(f"Username: admin")
|
||||||
print(f"Senha: admin123")
|
print(f"Senha: admin123")
|
||||||
if os.path.exists('admin_qr.png'):
|
if admin.otp_secret:
|
||||||
print("OTP: Usando configuração existente do arquivo admin_qr.png")
|
print("OTP: Usando configuração existente")
|
||||||
else:
|
else:
|
||||||
|
admin.otp_secret = pyotp.random_base32()
|
||||||
|
db.commit()
|
||||||
print("OTP: Nova configuração gerada")
|
print("OTP: Nova configuração gerada")
|
||||||
|
else:
|
||||||
|
# Criar admin se não existir
|
||||||
|
create_admin_user()
|
||||||
|
|
||||||
# Verificar usuários de teste
|
# Verificar usuários de teste
|
||||||
test_users = ['aligner', 'tester', 'deployer']
|
test_users = ['aligner', 'tester', 'deployer']
|
||||||
@@ -1503,14 +1689,19 @@ def init_system():
|
|||||||
print(f"Username: {username}")
|
print(f"Username: {username}")
|
||||||
print(f"Senha: Test123!@#")
|
print(f"Senha: Test123!@#")
|
||||||
if user.otp_secret:
|
if user.otp_secret:
|
||||||
print("OTP: Configurado")
|
print("OTP: Usando configuração existente")
|
||||||
else:
|
else:
|
||||||
print("OTP: Não configurado")
|
user.otp_secret = pyotp.random_base32()
|
||||||
|
db.commit()
|
||||||
|
print("OTP: Nova configuração gerada")
|
||||||
|
else:
|
||||||
|
# Criar usuário de teste se não existir
|
||||||
|
create_test_users()
|
||||||
finally:
|
finally:
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
print("\nInstruções:")
|
print("\nInstruções:")
|
||||||
print("1. Configure o OTP usando o QR code gerado")
|
print("1. Configure o OTP usando o QR code gerado (apenas para novos usuários)")
|
||||||
print("2. Faça login com as credenciais fornecidas")
|
print("2. Faça login com as credenciais fornecidas")
|
||||||
print("3. Altere a senha no primeiro login")
|
print("3. Altere a senha no primeiro login")
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ def create_admin_user():
|
|||||||
|
|
||||||
if admin:
|
if admin:
|
||||||
print("\n=== Usuário Admin Encontrado ===")
|
print("\n=== Usuário Admin Encontrado ===")
|
||||||
|
if not admin.otp_secret:
|
||||||
|
print("Gerando novo segredo OTP...")
|
||||||
|
admin.generate_otp_secret()
|
||||||
|
db.commit()
|
||||||
else:
|
else:
|
||||||
print("\n=== Criando Novo Usuário Admin ===")
|
print("\n=== Criando Novo Usuário Admin ===")
|
||||||
# Criar novo usuário admin
|
# Criar novo usuário admin
|
||||||
@@ -70,8 +74,16 @@ def create_admin_user():
|
|||||||
db.add(admin)
|
db.add(admin)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
# Gerar QR code uma única vez
|
# Gerar QR code apenas se solicitado ou se for novo usuário
|
||||||
|
if not os.path.exists('admin_qr.png'):
|
||||||
qr_path, otp_uri = generate_qr_code(admin)
|
qr_path, otp_uri = generate_qr_code(admin)
|
||||||
|
print("\n=== QR Code Gerado ===")
|
||||||
|
print(f"QR Code salvo em: {qr_path}")
|
||||||
|
print(f"URI do OTP: {otp_uri}")
|
||||||
|
else:
|
||||||
|
print("\n=== QR Code Existente ===")
|
||||||
|
print("Usando QR Code existente em: admin_qr.png")
|
||||||
|
qr_path = 'admin_qr.png'
|
||||||
|
|
||||||
# Mostrar informações
|
# Mostrar informações
|
||||||
print("\n=== Informações do Admin ===")
|
print("\n=== Informações do Admin ===")
|
||||||
@@ -80,10 +92,6 @@ def create_admin_user():
|
|||||||
print(f"Senha: admin123")
|
print(f"Senha: admin123")
|
||||||
print(f"Segredo OTP: {admin.otp_secret}")
|
print(f"Segredo OTP: {admin.otp_secret}")
|
||||||
|
|
||||||
print("\n=== QR Code Gerado ===")
|
|
||||||
print(f"QR Code salvo em: {qr_path}")
|
|
||||||
print(f"URI do OTP: {otp_uri}")
|
|
||||||
|
|
||||||
# Gerar código atual para verificação
|
# Gerar código atual para verificação
|
||||||
totp = pyotp.TOTP(admin.otp_secret)
|
totp = pyotp.TOTP(admin.otp_secret)
|
||||||
current_code = totp.now()
|
current_code = totp.now()
|
||||||
|
|||||||
@@ -56,17 +56,15 @@ def create_test_users():
|
|||||||
)
|
)
|
||||||
user.set_password(user_data['password'])
|
user.set_password(user_data['password'])
|
||||||
user.tipo = "ADMIN" if user_data['is_admin'] else "USUARIO"
|
user.tipo = "ADMIN" if user_data['is_admin'] else "USUARIO"
|
||||||
db.add(user)
|
|
||||||
db.commit()
|
|
||||||
|
|
||||||
# Se for o usuário teste, usar o mesmo OTP do admin
|
# Se for o usuário teste, usar o mesmo OTP do admin
|
||||||
if user_data['username'] == 'teste' and admin_otp_secret:
|
if user_data['username'] == 'teste' and admin_otp_secret:
|
||||||
user.otp_secret = admin_otp_secret
|
user.otp_secret = admin_otp_secret
|
||||||
db.commit()
|
|
||||||
else:
|
else:
|
||||||
# Gerar novo OTP para outros usuários
|
# Gerar novo OTP para outros usuários
|
||||||
otp_secret = pyotp.random_base32()
|
user.otp_secret = pyotp.random_base32()
|
||||||
user.otp_secret = otp_secret
|
|
||||||
|
db.add(user)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
# Atribuir role de Secretário Geral para o usuário teste
|
# Atribuir role de Secretário Geral para o usuário teste
|
||||||
@@ -77,6 +75,17 @@ def create_test_users():
|
|||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
print(f"Usuário {user_data['username']} criado com sucesso!")
|
print(f"Usuário {user_data['username']} criado com sucesso!")
|
||||||
|
|
||||||
|
# Gerar QR code para o novo usuário
|
||||||
|
qr_path = f"{user_data['username']}_qr.png"
|
||||||
|
if not os.path.exists(qr_path):
|
||||||
|
totp = pyotp.TOTP(user.otp_secret)
|
||||||
|
qr = qrcode.QRCode(version=1, box_size=10, border=5)
|
||||||
|
qr.add_data(totp.provisioning_uri(user.email, issuer_name="Sistema de Controles"))
|
||||||
|
qr.make(fit=True)
|
||||||
|
img = qr.make_image(fill_color="black", back_color="white")
|
||||||
|
img.save(qr_path)
|
||||||
|
print(f"QR Code gerado para {user_data['username']} em: {qr_path}")
|
||||||
else:
|
else:
|
||||||
print(f"Usuário {user_data['username']} já existe")
|
print(f"Usuário {user_data['username']} já existe")
|
||||||
|
|
||||||
@@ -85,6 +94,22 @@ def create_test_users():
|
|||||||
user.otp_secret = admin_otp_secret
|
user.otp_secret = admin_otp_secret
|
||||||
db.commit()
|
db.commit()
|
||||||
print(f"OTP do usuário teste atualizado para o mesmo do admin")
|
print(f"OTP do usuário teste atualizado para o mesmo do admin")
|
||||||
|
elif not user.otp_secret:
|
||||||
|
# Se não tiver OTP, gerar um novo
|
||||||
|
user.otp_secret = pyotp.random_base32()
|
||||||
|
db.commit()
|
||||||
|
print(f"Novo OTP gerado para {user_data['username']}")
|
||||||
|
|
||||||
|
# Gerar QR code
|
||||||
|
qr_path = f"{user_data['username']}_qr.png"
|
||||||
|
if not os.path.exists(qr_path):
|
||||||
|
totp = pyotp.TOTP(user.otp_secret)
|
||||||
|
qr = qrcode.QRCode(version=1, box_size=10, border=5)
|
||||||
|
qr.add_data(totp.provisioning_uri(user.email, issuer_name="Sistema de Controles"))
|
||||||
|
qr.make(fit=True)
|
||||||
|
img = qr.make_image(fill_color="black", back_color="white")
|
||||||
|
img.save(qr_path)
|
||||||
|
print(f"QR Code gerado para {user_data['username']} em: {qr_path}")
|
||||||
|
|
||||||
# Verificar se o usuário teste tem a role de Secretário Geral
|
# Verificar se o usuário teste tem a role de Secretário Geral
|
||||||
if user_data['username'] == 'teste':
|
if user_data['username'] == 'teste':
|
||||||
|
|||||||
@@ -7,6 +7,13 @@
|
|||||||
--green: #198754;
|
--green: #198754;
|
||||||
--cyan: #0dcaf0;
|
--cyan: #0dcaf0;
|
||||||
--yellow: #ffc107;
|
--yellow: #ffc107;
|
||||||
|
--primary-color: #dc3545;
|
||||||
|
--primary-hover: #bb2d3b;
|
||||||
|
--text-color: #333;
|
||||||
|
--text-muted: #6c757d;
|
||||||
|
--bg-hover: #f8f9fa;
|
||||||
|
--tab-active-color: var(--primary-color);
|
||||||
|
--tab-hover-color: rgba(220, 53, 69, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tabelas */
|
/* Tabelas */
|
||||||
@@ -242,3 +249,170 @@
|
|||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tabs */
|
||||||
|
.nav-tabs {
|
||||||
|
border-bottom: 2px solid var(--border-color);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs .nav-link,
|
||||||
|
.nav-tabs .nav-link:focus,
|
||||||
|
.nav-tabs .nav-link:hover,
|
||||||
|
.nav-tabs .nav-link.active {
|
||||||
|
color: var(--primary-color) !important;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
margin-bottom: -2px;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
font-weight: 500;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs .nav-link:hover {
|
||||||
|
background-color: var(--tab-hover-color);
|
||||||
|
border-bottom: 2px solid var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs .nav-link.active {
|
||||||
|
font-weight: 600;
|
||||||
|
background-color: var(--tab-hover-color);
|
||||||
|
border-bottom: 2px solid var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs .nav-link i {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane {
|
||||||
|
animation: fadeIn 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(5px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsividade das abas */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.nav-tabs {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs .nav-link {
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilo para botões com largura fixa */
|
||||||
|
.btn-fixed-width {
|
||||||
|
min-width: 120px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
text-align: center;
|
||||||
|
height: 38px;
|
||||||
|
line-height: 1.5;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-fixed-width i {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilo para o backdrop com blur em todos os modais */
|
||||||
|
.modal-backdrop.show {
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilo para o botão de fechar dos modais */
|
||||||
|
.btn-close {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0.5rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
filter: invert(1) grayscale(100%) brightness(200%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close:hover {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos do Modal */
|
||||||
|
.modal-header {
|
||||||
|
background-color: #343a40;
|
||||||
|
color: #fff;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header i {
|
||||||
|
color: #fff;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header .btn-close {
|
||||||
|
filter: invert(1) grayscale(100%) brightness(200%);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header .btn-close:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos globais de formulário */
|
||||||
|
.form-control:focus,
|
||||||
|
.form-select:focus,
|
||||||
|
.form-check-input:focus,
|
||||||
|
.btn:focus,
|
||||||
|
.btn-check:focus + .btn {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:hover,
|
||||||
|
.form-select:hover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input group com foco */
|
||||||
|
.input-group .form-control:focus,
|
||||||
|
.input-group .form-select:focus {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkbox e radio */
|
||||||
|
.form-check-input:checked {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Date picker */
|
||||||
|
input[type="date"]:focus {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
|
||||||
|
}
|
||||||
@@ -61,6 +61,8 @@ function filtrarMilitantes() {
|
|||||||
|
|
||||||
// Configurar eventos quando o DOM estiver carregado
|
// Configurar eventos quando o DOM estiver carregado
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
console.log('DOM carregado, configurando eventos...');
|
||||||
|
|
||||||
// Configurar pesquisa
|
// Configurar pesquisa
|
||||||
const searchInput = document.getElementById('searchInput');
|
const searchInput = document.getElementById('searchInput');
|
||||||
if (searchInput) {
|
if (searchInput) {
|
||||||
@@ -109,7 +111,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('DOM carregado');
|
console.log('Configurando modal de edição...');
|
||||||
|
|
||||||
// Configuração do modal de edição
|
// Configuração do modal de edição
|
||||||
const modalEditarMilitante = document.getElementById('modalEditarMilitante');
|
const modalEditarMilitante = document.getElementById('modalEditarMilitante');
|
||||||
@@ -118,6 +120,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
if (modalEditarMilitante) {
|
if (modalEditarMilitante) {
|
||||||
console.log('Modal encontrado, configurando eventos...');
|
console.log('Modal encontrado, configurando eventos...');
|
||||||
|
|
||||||
|
// Criar instância do modal Bootstrap
|
||||||
|
const modalInstance = new bootstrap.Modal(modalEditarMilitante);
|
||||||
|
console.log('Instância do modal criada:', modalInstance);
|
||||||
|
|
||||||
modalEditarMilitante.addEventListener('show.bs.modal', function(event) {
|
modalEditarMilitante.addEventListener('show.bs.modal', function(event) {
|
||||||
console.log('Modal sendo exibido');
|
console.log('Modal sendo exibido');
|
||||||
const button = event.relatedTarget;
|
const button = event.relatedTarget;
|
||||||
@@ -129,33 +135,155 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const militanteId = button.getAttribute('data-militante-id');
|
const militanteId = button.getAttribute('data-militante-id');
|
||||||
|
const militanteNome = button.getAttribute('data-militante-nome');
|
||||||
console.log('ID do militante:', militanteId);
|
console.log('ID do militante:', militanteId);
|
||||||
|
console.log('Nome do militante:', militanteNome);
|
||||||
|
|
||||||
// Dados do militante
|
if (!militanteId) {
|
||||||
const dados = {
|
console.error('ID do militante não encontrado no botão!');
|
||||||
nome: button.getAttribute('data-militante-nome'),
|
return;
|
||||||
cpf: button.getAttribute('data-militante-cpf'),
|
}
|
||||||
email: button.getAttribute('data-militante-email'),
|
|
||||||
telefone: button.getAttribute('data-militante-telefone'),
|
|
||||||
endereco: button.getAttribute('data-militante-endereco'),
|
|
||||||
filiado: button.getAttribute('data-militante-filiado')
|
|
||||||
};
|
|
||||||
console.log('Dados do militante:', dados);
|
|
||||||
|
|
||||||
// Preencher campos
|
// Atualizar o título do modal com o nome do militante
|
||||||
document.getElementById('editNome').value = dados.nome || '';
|
const modalTitle = modalEditarMilitante.querySelector('.modal-title');
|
||||||
document.getElementById('editCpf').value = dados.cpf || '';
|
if (modalTitle) {
|
||||||
document.getElementById('editEmail').value = dados.email || '';
|
modalTitle.innerHTML = `<i class="fas fa-user-edit me-2"></i>Editar ${militanteNome}`;
|
||||||
document.getElementById('editTelefone').value = dados.telefone || '';
|
}
|
||||||
document.getElementById('editEndereco').value = dados.endereco || '';
|
|
||||||
document.getElementById('editFiliado').checked = dados.filiado === 'True';
|
|
||||||
|
|
||||||
// Configurar formulário
|
// Configurar formulário
|
||||||
const form = document.getElementById('formEditarMilitante');
|
const form = document.getElementById('formEditarMilitante');
|
||||||
if (form) {
|
if (form) {
|
||||||
form.action = `/militantes/editar/${militanteId}`;
|
form.action = `/militantes/editar/${militanteId}`;
|
||||||
console.log('Action do formulário:', form.action);
|
const idInput = document.getElementById('edit_militante_id');
|
||||||
|
if (idInput) {
|
||||||
|
idInput.value = militanteId;
|
||||||
}
|
}
|
||||||
|
console.log('Action do formulário:', form.action);
|
||||||
|
console.log('ID do militante no formulário:', militanteId);
|
||||||
|
} else {
|
||||||
|
console.error('Formulário não encontrado!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mostrar loading
|
||||||
|
const modalBody = modalEditarMilitante.querySelector('.modal-body');
|
||||||
|
if (modalBody) {
|
||||||
|
modalBody.style.opacity = '0.5';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscar dados completos do militante via AJAX
|
||||||
|
console.log(`Fazendo requisição para /militantes/${militanteId}/dados`);
|
||||||
|
|
||||||
|
// Obter o CSRF token
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
||||||
|
console.log('CSRF token:', csrfToken);
|
||||||
|
|
||||||
|
fetch(`/militantes/${militanteId}/dados`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'X-CSRFToken': csrfToken
|
||||||
|
},
|
||||||
|
credentials: 'same-origin'
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
console.log('Resposta recebida:', response);
|
||||||
|
if (!response.ok) {
|
||||||
|
return response.json().then(data => {
|
||||||
|
throw new Error(data.message || `HTTP error! status: ${response.status}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(dados => {
|
||||||
|
console.log('Dados do militante recebidos:', dados);
|
||||||
|
|
||||||
|
if (!dados) {
|
||||||
|
throw new Error('Dados do militante não encontrados');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Preenchendo dados básicos...');
|
||||||
|
// Dados Básicos
|
||||||
|
document.getElementById('edit_nome').value = dados.nome || '';
|
||||||
|
document.getElementById('edit_cpf').value = dados.cpf || '';
|
||||||
|
document.getElementById('edit_titulo_eleitoral').value = dados.titulo_eleitoral || '';
|
||||||
|
document.getElementById('edit_data_nascimento').value = dados.data_nascimento || '';
|
||||||
|
document.getElementById('edit_data_entrada').value = dados.data_entrada_oci || '';
|
||||||
|
document.getElementById('edit_data_efetivacao').value = dados.data_efetivacao_oci || '';
|
||||||
|
console.log('Dados básicos preenchidos');
|
||||||
|
|
||||||
|
console.log('Preenchendo dados de contato...');
|
||||||
|
// Contato
|
||||||
|
document.getElementById('edit_telefone1').value = dados.telefone1 || '';
|
||||||
|
document.getElementById('edit_telefone2').value = dados.telefone2 || '';
|
||||||
|
document.getElementById('edit_email').value = dados.email || '';
|
||||||
|
console.log('Dados de contato preenchidos');
|
||||||
|
|
||||||
|
console.log('Preenchendo dados de endereço...');
|
||||||
|
// Endereço
|
||||||
|
if (dados.endereco) {
|
||||||
|
document.getElementById('edit_cep').value = dados.endereco.cep || '';
|
||||||
|
document.getElementById('edit_estado').value = dados.endereco.estado || '';
|
||||||
|
document.getElementById('edit_cidade').value = dados.endereco.cidade || '';
|
||||||
|
document.getElementById('edit_bairro').value = dados.endereco.bairro || '';
|
||||||
|
document.getElementById('edit_logradouro').value = dados.endereco.rua || '';
|
||||||
|
document.getElementById('edit_numero').value = dados.endereco.numero || '';
|
||||||
|
document.getElementById('edit_complemento').value = dados.endereco.complemento || '';
|
||||||
|
}
|
||||||
|
console.log('Dados de endereço preenchidos');
|
||||||
|
|
||||||
|
console.log('Preenchendo dados profissionais...');
|
||||||
|
// Profissional
|
||||||
|
document.getElementById('edit_profissao').value = dados.profissao || '';
|
||||||
|
document.getElementById('edit_regime_trabalho').value = dados.regime_trabalho || '';
|
||||||
|
document.getElementById('edit_empresa').value = dados.empresa || '';
|
||||||
|
document.getElementById('edit_contratante').value = dados.contratante || '';
|
||||||
|
console.log('Dados profissionais preenchidos');
|
||||||
|
|
||||||
|
console.log('Preenchendo dados acadêmicos...');
|
||||||
|
// Acadêmico
|
||||||
|
document.getElementById('edit_instituicao_ensino').value = dados.instituicao_ensino || '';
|
||||||
|
document.getElementById('edit_tipo_instituicao').value = dados.tipo_instituicao || '';
|
||||||
|
console.log('Dados acadêmicos preenchidos');
|
||||||
|
|
||||||
|
console.log('Preenchendo dados sindicais...');
|
||||||
|
// Sindical
|
||||||
|
document.getElementById('edit_sindicato').value = dados.sindicato || '';
|
||||||
|
document.getElementById('edit_cargo_sindical').value = dados.cargo_sindical || '';
|
||||||
|
document.getElementById('edit_central_sindical').value = dados.central_sindical || '';
|
||||||
|
document.getElementById('edit_dirigente_sindical').checked = dados.dirigente_sindical || false;
|
||||||
|
console.log('Dados sindicais preenchidos');
|
||||||
|
|
||||||
|
console.log('Preenchendo dados organizacionais...');
|
||||||
|
// Organização
|
||||||
|
document.getElementById('edit_estado_militante').value = dados.estado || 'ATIVO';
|
||||||
|
document.getElementById('edit_celula').value = dados.celula_id || '';
|
||||||
|
|
||||||
|
// Responsabilidades
|
||||||
|
const responsabilidades = dados.responsabilidades || 0;
|
||||||
|
document.querySelectorAll('input[name="responsabilidades"]').forEach(checkbox => {
|
||||||
|
const valor = parseInt(checkbox.value);
|
||||||
|
checkbox.checked = (responsabilidades & valor) !== 0;
|
||||||
|
});
|
||||||
|
console.log('Dados organizacionais preenchidos');
|
||||||
|
|
||||||
|
console.log('Todos os dados preenchidos com sucesso!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erro ao preencher os campos:', error);
|
||||||
|
throw new Error('Erro ao preencher os dados do militante: ' + error.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Erro ao buscar dados do militante:', error);
|
||||||
|
alert('Erro ao carregar dados do militante: ' + error.message);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
if (modalBody) {
|
||||||
|
modalBody.style.opacity = '1';
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Envio do formulário de edição via AJAX
|
// Envio do formulário de edição via AJAX
|
||||||
@@ -163,53 +291,76 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
if (formEditarMilitante) {
|
if (formEditarMilitante) {
|
||||||
formEditarMilitante.addEventListener('submit', function(e) {
|
formEditarMilitante.addEventListener('submit', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
console.log('Enviando formulário de edição...');
|
||||||
|
|
||||||
const formData = new FormData(this);
|
const formData = new FormData(this);
|
||||||
|
console.log('Dados do formulário:', Object.fromEntries(formData));
|
||||||
|
|
||||||
fetch(this.action, {
|
// Adicionar responsabilidades selecionadas
|
||||||
|
const responsabilidades = Array.from(this.querySelectorAll('input[name="responsabilidades"]:checked'))
|
||||||
|
.map(cb => parseInt(cb.value))
|
||||||
|
.reduce((a, b) => a + b, 0);
|
||||||
|
formData.set('responsabilidades', responsabilidades);
|
||||||
|
|
||||||
|
// Obter o CSRF token
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
||||||
|
console.log('CSRF token:', csrfToken);
|
||||||
|
|
||||||
|
// Obter o ID do militante do campo hidden
|
||||||
|
const militanteId = document.getElementById('edit_militante_id').value;
|
||||||
|
console.log('ID do militante:', militanteId);
|
||||||
|
|
||||||
|
if (!militanteId) {
|
||||||
|
console.error('ID do militante não encontrado!');
|
||||||
|
mostrarAlerta('Erro: ID do militante não encontrado', 'danger');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Garantir que o campo de endereço está correto
|
||||||
|
const logradouro = formData.get('logradouro');
|
||||||
|
if (logradouro) {
|
||||||
|
formData.set('rua', logradouro);
|
||||||
|
formData.delete('logradouro');
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(`/militantes/editar/${militanteId}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData,
|
body: formData,
|
||||||
headers: {
|
headers: {
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
}
|
'X-CSRFToken': csrfToken
|
||||||
|
},
|
||||||
|
credentials: 'same-origin'
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
console.log('Resposta recebida:', response);
|
||||||
|
if (!response.ok) {
|
||||||
|
return response.json().then(data => {
|
||||||
|
throw new Error(data.message || `HTTP error! status: ${response.status}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
console.log('Resposta processada:', data);
|
||||||
if (data.status === 'success') {
|
if (data.status === 'success') {
|
||||||
// Fechar o modal
|
// Fechar o modal
|
||||||
bootstrap.Modal.getInstance(modalEditarMilitante).hide();
|
bootstrap.Modal.getInstance(modalEditarMilitante).hide();
|
||||||
|
|
||||||
// Atualizar a lista
|
|
||||||
location.reload();
|
|
||||||
|
|
||||||
// Mostrar mensagem de sucesso
|
// Mostrar mensagem de sucesso
|
||||||
const alertDiv = document.createElement('div');
|
mostrarAlerta(data.message, 'success');
|
||||||
alertDiv.className = 'alert alert-success alert-dismissible fade show';
|
|
||||||
alertDiv.innerHTML = `
|
// Recarregar a página após um breve delay
|
||||||
${data.message}
|
setTimeout(() => {
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
location.reload();
|
||||||
`;
|
}, 1500);
|
||||||
document.querySelector('.container').insertBefore(alertDiv, document.querySelector('.container').firstChild);
|
|
||||||
} else {
|
} else {
|
||||||
// Mostrar erro
|
throw new Error(data.message || 'Erro ao salvar dados');
|
||||||
const alertDiv = document.createElement('div');
|
|
||||||
alertDiv.className = 'alert alert-danger alert-dismissible fade show';
|
|
||||||
alertDiv.innerHTML = `
|
|
||||||
${data.message}
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
||||||
`;
|
|
||||||
document.querySelector('.modal-body').insertBefore(alertDiv, formEditarMilitante);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Erro:', error);
|
console.error('Erro ao enviar formulário:', error);
|
||||||
const alertDiv = document.createElement('div');
|
mostrarAlerta(`Erro ao salvar dados: ${error.message}`, 'danger');
|
||||||
alertDiv.className = 'alert alert-danger alert-dismissible fade show';
|
|
||||||
alertDiv.innerHTML = `
|
|
||||||
Erro ao atualizar militante. Tente novamente.
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
||||||
`;
|
|
||||||
document.querySelector('.modal-body').insertBefore(alertDiv, formEditarMilitante);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -334,32 +485,31 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Máscara para CPF
|
// Máscara para CPF
|
||||||
const cpfInput = document.getElementById('cpf');
|
const cpfInputs = document.querySelectorAll('input[name="cpf"]');
|
||||||
if (cpfInput) {
|
cpfInputs.forEach(input => {
|
||||||
cpfInput.addEventListener('input', function(e) {
|
input.addEventListener('input', function(e) {
|
||||||
let value = e.target.value.replace(/\D/g, '');
|
let value = e.target.value.replace(/\D/g, '');
|
||||||
if (value.length <= 11) {
|
if (value.length <= 11) {
|
||||||
value = value.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, '$1.$2.$3-$4');
|
value = value.replace(/(\d{3})(\d)/, '$1.$2');
|
||||||
|
value = value.replace(/(\d{3})(\d)/, '$1.$2');
|
||||||
|
value = value.replace(/(\d{3})(\d{1,2})$/, '$1-$2');
|
||||||
e.target.value = value;
|
e.target.value = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
// Máscara para telefone
|
// Máscara para telefone
|
||||||
const telefoneInput = document.getElementById('telefone');
|
const telefoneInputs = document.querySelectorAll('input[name="telefone1"], input[name="telefone2"]');
|
||||||
if (telefoneInput) {
|
telefoneInputs.forEach(input => {
|
||||||
telefoneInput.addEventListener('input', function(e) {
|
input.addEventListener('input', function(e) {
|
||||||
let value = e.target.value.replace(/\D/g, '');
|
let value = e.target.value.replace(/\D/g, '');
|
||||||
if (value.length <= 11) {
|
if (value.length <= 11) {
|
||||||
if (value.length === 11) {
|
value = value.replace(/(\d{2})(\d)/, '($1) $2');
|
||||||
value = value.replace(/(\d{2})(\d{5})(\d{4})/, '($1) $2-$3');
|
value = value.replace(/(\d{4,5})(\d{4})$/, '$1-$2');
|
||||||
} else {
|
|
||||||
value = value.replace(/(\d{2})(\d{4})(\d{4})/, '($1) $2-$3');
|
|
||||||
}
|
|
||||||
e.target.value = value;
|
e.target.value = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
// Limpar formulário e alertas quando o modal for fechado
|
// Limpar formulário e alertas quando o modal for fechado
|
||||||
const modalNovoMilitante = document.getElementById('modalNovoMilitante');
|
const modalNovoMilitante = document.getElementById('modalNovoMilitante');
|
||||||
@@ -444,4 +594,71 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configurar máscaras de input
|
||||||
|
// CEP
|
||||||
|
const cepInputs = document.querySelectorAll('input[name="cep"]');
|
||||||
|
cepInputs.forEach(input => {
|
||||||
|
input.addEventListener('input', function(e) {
|
||||||
|
let value = e.target.value.replace(/\D/g, '');
|
||||||
|
if (value.length <= 8) {
|
||||||
|
value = value.replace(/(\d{5})(\d)/, '$1-$2');
|
||||||
|
e.target.value = value;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Buscar endereço pelo CEP
|
||||||
|
input.addEventListener('blur', function(e) {
|
||||||
|
const cep = e.target.value.replace(/\D/g, '');
|
||||||
|
if (cep.length === 8) {
|
||||||
|
fetch(`https://viacep.com.br/ws/${cep}/json/`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.erro) {
|
||||||
|
const form = input.closest('form');
|
||||||
|
form.querySelector('input[name="logradouro"]').value = data.logradouro;
|
||||||
|
form.querySelector('input[name="bairro"]').value = data.bairro;
|
||||||
|
form.querySelector('input[name="cidade"]').value = data.localidade;
|
||||||
|
form.querySelector('select[name="estado"]').value = data.uf;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Título Eleitoral
|
||||||
|
const tituloInputs = document.querySelectorAll('input[name="titulo_eleitoral"]');
|
||||||
|
tituloInputs.forEach(input => {
|
||||||
|
input.addEventListener('input', function(e) {
|
||||||
|
let value = e.target.value.replace(/\D/g, '');
|
||||||
|
if (value.length <= 12) {
|
||||||
|
value = value.replace(/(\d{4})(\d)/, '$1 $2');
|
||||||
|
value = value.replace(/(\d{4})(\d)/, '$1 $2');
|
||||||
|
e.target.value = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Função para mostrar alertas
|
||||||
|
function mostrarAlerta(mensagem, tipo) {
|
||||||
|
// Criar o elemento de alerta
|
||||||
|
const alertDiv = document.createElement('div');
|
||||||
|
alertDiv.className = `alert alert-${tipo} alert-dismissible fade show position-fixed top-0 start-50 translate-middle-x mt-3`;
|
||||||
|
alertDiv.style.zIndex = '9999';
|
||||||
|
alertDiv.innerHTML = `
|
||||||
|
${mensagem}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Adicionar o alerta ao corpo do documento
|
||||||
|
document.body.appendChild(alertDiv);
|
||||||
|
|
||||||
|
// Configurar o Bootstrap alert
|
||||||
|
const bsAlert = new bootstrap.Alert(alertDiv);
|
||||||
|
|
||||||
|
// Remover o alerta após 3 segundos
|
||||||
|
setTimeout(() => {
|
||||||
|
bsAlert.close();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
@@ -3,12 +3,15 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||||
<title>{% block title %}{% endblock %} - Controles OCI</title>
|
<title>{% block title %}{% endblock %} - Controles OCI</title>
|
||||||
|
|
||||||
<!-- Bootstrap 5 CSS -->
|
<!-- Bootstrap 5 CSS -->
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css?v=1" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css?v=1" rel="stylesheet">
|
||||||
<!-- Font Awesome 6 -->
|
<!-- Font Awesome 6 -->
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css?v=1">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css?v=1">
|
||||||
|
<!-- Componentes CSS -->
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/components.css') }}">
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
<form method="POST" action="{{ url_for('login') }}" class="needs-validation" novalidate>
|
<form method="POST" action="{{ url_for('login') }}" class="needs-validation" novalidate>
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input type="text" class="form-control" id="email" name="email" placeholder="Email ou Usuário" required>
|
<input type="text" class="form-control" id="email" name="email" placeholder="Email ou Usuário" required>
|
||||||
<label for="email">Email ou Usuário</label>
|
<label for="email">Email ou Usuário</label>
|
||||||
|
|||||||
23
templates/militantes.html
Normal file
23
templates/militantes.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<!-- Botões de ação -->
|
||||||
|
<td class="text-end">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm btn-outline-primary"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#modalEditarMilitante"
|
||||||
|
data-militante-id="{{ militante.id }}"
|
||||||
|
data-militante-nome="{{ militante.nome }}"
|
||||||
|
title="Editar">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</button>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm btn-outline-danger"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#deleteModal"
|
||||||
|
data-militante-id="{{ militante.id }}"
|
||||||
|
data-militante-nome="{{ militante.nome }}"
|
||||||
|
title="Excluir">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- Modal de Edição -->
|
<!-- Modal de Editar Militante -->
|
||||||
<div class="modal fade" id="modalEditarMilitante" tabindex="-1">
|
<div class="modal fade" id="modalEditarMilitante" tabindex="-1">
|
||||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@@ -8,44 +8,259 @@
|
|||||||
</h5>
|
</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
|
<form id="formEditarMilitante" method="POST">
|
||||||
|
<input type="hidden" id="edit_militante_id" name="militante_id">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form id="formEditarMilitante" method="post">
|
<!-- Nav tabs -->
|
||||||
|
<ul class="nav nav-tabs nav-fill mb-3" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#edit-dados-basicos" type="button">
|
||||||
|
<i class="fas fa-user me-2"></i>Dados Básicos
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#edit-contato" type="button">
|
||||||
|
<i class="fas fa-address-book me-2"></i>Contato
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#edit-profissional" type="button">
|
||||||
|
<i class="fas fa-briefcase me-2"></i>Profissional
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#edit-organizacao" type="button">
|
||||||
|
<i class="fas fa-users me-2"></i>Organização
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- Tab content -->
|
||||||
|
<div class="tab-content">
|
||||||
|
<!-- Dados Básicos -->
|
||||||
|
<div class="tab-pane fade show active" id="edit-dados-basicos">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="editNome" class="form-label">Nome:</label>
|
<label for="edit_nome" class="form-label">Nome</label>
|
||||||
<input type="text" class="form-control" id="editNome" name="nome" required>
|
<input type="text" class="form-control" id="edit_nome" name="nome" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="editCpf" class="form-label">CPF:</label>
|
<label for="edit_cpf" class="form-label">CPF</label>
|
||||||
<input type="text" class="form-control" id="editCpf" name="cpf" required>
|
<input type="text" class="form-control" id="edit_cpf" name="cpf" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="editEmail" class="form-label">Email:</label>
|
<label for="edit_titulo_eleitoral" class="form-label">Título Eleitoral</label>
|
||||||
<input type="email" class="form-control" id="editEmail" name="email" required>
|
<input type="text" class="form-control" id="edit_titulo_eleitoral" name="titulo_eleitoral">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="editTelefone" class="form-label">Telefone:</label>
|
<label for="edit_data_nascimento" class="form-label">Data de Nascimento</label>
|
||||||
<input type="text" class="form-control" id="editTelefone" name="telefone">
|
<input type="date" class="form-control" id="edit_data_nascimento" name="data_nascimento">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_data_entrada" class="form-label">Data de Entrada OCI</label>
|
||||||
|
<input type="date" class="form-control" id="edit_data_entrada" name="data_entrada_oci">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_data_efetivacao" class="form-label">Data de Efetivação</label>
|
||||||
|
<input type="date" class="form-control" id="edit_data_efetivacao" name="data_efetivacao_oci">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Contato -->
|
||||||
|
<div class="tab-pane fade" id="edit-contato">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_telefone1" class="form-label">Telefone Principal</label>
|
||||||
|
<input type="text" class="form-control" id="edit_telefone1" name="telefone1">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_telefone2" class="form-label">Telefone Alternativo</label>
|
||||||
|
<input type="text" class="form-control" id="edit_telefone2" name="telefone2">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Email Principal -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="edit_email" class="form-label">Email Principal</label>
|
||||||
|
<input type="email" class="form-control" id="edit_email" name="email" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Endereço -->
|
||||||
|
<div class="endereco-container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<label for="edit_cep" class="form-label">CEP</label>
|
||||||
|
<input type="text" class="form-control" id="edit_cep" name="cep">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<label for="edit_estado" class="form-label">Estado</label>
|
||||||
|
<select class="form-select" id="edit_estado" name="estado">
|
||||||
|
<option value="">Selecione...</option>
|
||||||
|
<!-- Estados serão carregados via JavaScript -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<label for="edit_cidade" class="form-label">Cidade</label>
|
||||||
|
<input type="text" class="form-control" id="edit_cidade" name="cidade">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<label for="edit_bairro" class="form-label">Bairro</label>
|
||||||
|
<input type="text" class="form-control" id="edit_bairro" name="bairro">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_logradouro" class="form-label">Logradouro</label>
|
||||||
|
<input type="text" class="form-control" id="edit_logradouro" name="rua">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2 mb-3">
|
||||||
|
<label for="edit_numero" class="form-label">Número</label>
|
||||||
|
<input type="text" class="form-control" id="edit_numero" name="numero">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="editEndereco" class="form-label">Endereço:</label>
|
<label for="edit_complemento" class="form-label">Complemento</label>
|
||||||
<input type="text" class="form-control" id="editEndereco" name="endereco">
|
<input type="text" class="form-control" id="edit_complemento" name="complemento">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Profissional -->
|
||||||
|
<div class="tab-pane fade" id="edit-profissional">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_profissao" class="form-label">Profissão</label>
|
||||||
|
<input type="text" class="form-control" id="edit_profissao" name="profissao">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_regime_trabalho" class="form-label">Regime de Trabalho</label>
|
||||||
|
<select class="form-select" id="edit_regime_trabalho" name="regime_trabalho">
|
||||||
|
<option value="">Selecione...</option>
|
||||||
|
<option value="CLT">CLT</option>
|
||||||
|
<option value="Estatutário">Estatutário</option>
|
||||||
|
<option value="Terceirizado">Terceirizado</option>
|
||||||
|
<option value="Autônomo">Autônomo</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_empresa" class="form-label">Empresa</label>
|
||||||
|
<input type="text" class="form-control" id="edit_empresa" name="empresa">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_contratante" class="form-label">Contratante</label>
|
||||||
|
<input type="text" class="form-control" id="edit_contratante" name="contratante">
|
||||||
|
<small class="text-muted">Para terceirizados</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<!-- Dados Acadêmicos -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 mb-3">
|
||||||
|
<label for="edit_instituicao_ensino" class="form-label">Instituição de Ensino</label>
|
||||||
|
<input type="text" class="form-control" id="edit_instituicao_ensino" name="instituicao_ensino">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<label for="edit_tipo_instituicao" class="form-label">Tipo</label>
|
||||||
|
<select class="form-select" id="edit_tipo_instituicao" name="tipo_instituicao">
|
||||||
|
<option value="">Selecione...</option>
|
||||||
|
<option value="Federal">Federal</option>
|
||||||
|
<option value="Estadual">Estadual</option>
|
||||||
|
<option value="Municipal">Municipal</option>
|
||||||
|
<option value="Privada">Privada</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Organização -->
|
||||||
|
<div class="tab-pane fade" id="edit-organizacao">
|
||||||
|
<!-- Dados Sindicais -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_sindicato" class="form-label">Sindicato</label>
|
||||||
|
<input type="text" class="form-control" id="edit_sindicato" name="sindicato">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_cargo_sindical" class="form-label">Cargo Sindical</label>
|
||||||
|
<input type="text" class="form-control" id="edit_cargo_sindical" name="cargo_sindical">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_central_sindical" class="form-label">Central Sindical</label>
|
||||||
|
<input type="text" class="form-control" id="edit_central_sindical" name="central_sindical">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3 d-flex align-items-center">
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="edit_dirigente_sindical" name="dirigente_sindical">
|
||||||
|
<label class="form-check-label" for="edit_dirigente_sindical">Dirigente Sindical</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<!-- Estado na Organização -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_estado_militante" class="form-label">Estado</label>
|
||||||
|
<select class="form-select" id="edit_estado_militante" name="estado">
|
||||||
|
<option value="ativo">Ativo</option>
|
||||||
|
<option value="licenciado">Licenciado</option>
|
||||||
|
<option value="suspenso">Suspenso</option>
|
||||||
|
<option value="desligado">Desligado</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_celula" class="form-label">Célula</label>
|
||||||
|
<select class="form-select" id="edit_celula" name="celula_id">
|
||||||
|
<option value="">Selecione...</option>
|
||||||
|
{% for celula in celulas %}
|
||||||
|
<option value="{{ celula.id }}">{{ celula.nome }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label d-block">Responsabilidades</label>
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="edit_resp_1" name="responsabilidades" value="1">
|
||||||
|
<label class="form-check-label" for="edit_resp_1">Finanças</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="edit_resp_2" name="responsabilidades" value="2">
|
||||||
|
<label class="form-check-label" for="edit_resp_2">Imprensa</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="edit_resp_4" name="responsabilidades" value="4">
|
||||||
|
<label class="form-check-label" for="edit_resp_4">Quadro-Orientador</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 form-check">
|
|
||||||
<input type="checkbox" class="form-check-input" id="editFiliado" name="filiado">
|
|
||||||
<label class="form-check-label" for="editFiliado">Filiado</label>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||||
<button type="submit" form="formEditarMilitante" class="btn btn-success">
|
<button type="submit" class="btn btn-primary">
|
||||||
<i class="fas fa-save me-2"></i>Salvar
|
<i class="fas fa-save me-2"></i>Salvar
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -10,38 +10,238 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form id="formNovoMilitante" method="post" action="{{ url_for('criar_militante') }}">
|
<form id="formNovoMilitante" method="post" action="{{ url_for('criar_militante') }}">
|
||||||
|
<!-- Nav tabs -->
|
||||||
|
<ul class="nav nav-tabs nav-fill mb-3" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#tab-dados-basicos" type="button">
|
||||||
|
<i class="fas fa-user me-2"></i>Dados Básicos
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-contato" type="button">
|
||||||
|
<i class="fas fa-address-book me-2"></i>Contato
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-profissional" type="button">
|
||||||
|
<i class="fas fa-briefcase me-2"></i>Profissional
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-organizacao" type="button">
|
||||||
|
<i class="fas fa-users me-2"></i>Organização
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- Tab content -->
|
||||||
|
<div class="tab-content">
|
||||||
|
<!-- Dados Básicos -->
|
||||||
|
<div class="tab-pane fade show active" id="tab-dados-basicos">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="nome" class="form-label">Nome:</label>
|
<label for="nome" class="form-label">Nome</label>
|
||||||
<input type="text" class="form-control" id="nome" name="nome" required>
|
<input type="text" class="form-control" id="nome" name="nome" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="cpf" class="form-label">CPF:</label>
|
<label for="cpf" class="form-label">CPF</label>
|
||||||
<input type="text" class="form-control" id="cpf" name="cpf" required
|
<input type="text" class="form-control" id="cpf" name="cpf" required
|
||||||
pattern="\d{3}\.?\d{3}\.?\d{3}-?\d{2}"
|
pattern="\d{3}\.?\d{3}\.?\d{3}-?\d{2}"
|
||||||
title="Digite um CPF no formato: xxx.xxx.xxx-xx">
|
title="Digite um CPF no formato: xxx.xxx.xxx-xx">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="email" class="form-label">Email:</label>
|
<label for="titulo_eleitoral" class="form-label">Título Eleitoral</label>
|
||||||
<input type="email" class="form-control" id="email" name="email" required>
|
<input type="text" class="form-control" id="titulo_eleitoral" name="titulo_eleitoral">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="telefone" class="form-label">Telefone:</label>
|
<label for="data_nascimento" class="form-label">Data de Nascimento</label>
|
||||||
<input type="text" class="form-control" id="telefone" name="telefone">
|
<input type="date" class="form-control" id="data_nascimento" name="data_nascimento">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="data_entrada" class="form-label">Data de Entrada OCI</label>
|
||||||
|
<input type="date" class="form-control" id="data_entrada" name="data_entrada_oci">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="data_efetivacao" class="form-label">Data de Efetivação</label>
|
||||||
|
<input type="date" class="form-control" id="data_efetivacao" name="data_efetivacao_oci">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Contato -->
|
||||||
|
<div class="tab-pane fade" id="tab-contato">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="telefone1" class="form-label">Telefone Principal</label>
|
||||||
|
<input type="text" class="form-control" id="telefone1" name="telefone1">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="telefone2" class="form-label">Telefone Alternativo</label>
|
||||||
|
<input type="text" class="form-control" id="telefone2" name="telefone2">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Email Principal -->
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="endereco" class="form-label">Endereço:</label>
|
<label for="email" class="form-label">Email Principal</label>
|
||||||
<input type="text" class="form-control" id="endereco" name="endereco">
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3 form-check">
|
<!-- Endereço -->
|
||||||
<input type="checkbox" class="form-check-input" id="filiado" name="filiado">
|
<div class="endereco-container">
|
||||||
<label class="form-check-label" for="filiado">Filiado</label>
|
<div class="row">
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<label for="cep" class="form-label">CEP</label>
|
||||||
|
<input type="text" class="form-control" id="cep" name="cep">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<label for="estado" class="form-label">Estado</label>
|
||||||
|
<select class="form-select" id="estado" name="estado">
|
||||||
|
<option value="">Selecione...</option>
|
||||||
|
<!-- Estados serão carregados via JavaScript -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<label for="cidade" class="form-label">Cidade</label>
|
||||||
|
<input type="text" class="form-control" id="cidade" name="cidade">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<label for="bairro" class="form-label">Bairro</label>
|
||||||
|
<input type="text" class="form-control" id="bairro" name="bairro">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="logradouro" class="form-label">Logradouro</label>
|
||||||
|
<input type="text" class="form-control" id="logradouro" name="logradouro">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2 mb-3">
|
||||||
|
<label for="numero" class="form-label">Número</label>
|
||||||
|
<input type="text" class="form-control" id="numero" name="numero">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="complemento" class="form-label">Complemento</label>
|
||||||
|
<input type="text" class="form-control" id="complemento" name="complemento">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Profissional -->
|
||||||
|
<div class="tab-pane fade" id="tab-profissional">
|
||||||
|
<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">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="regime_trabalho" class="form-label">Regime de Trabalho</label>
|
||||||
|
<select class="form-select" id="regime_trabalho" name="regime_trabalho">
|
||||||
|
<option value="">Selecione...</option>
|
||||||
|
<option value="CLT">CLT</option>
|
||||||
|
<option value="Estatutário">Estatutário</option>
|
||||||
|
<option value="Terceirizado">Terceirizado</option>
|
||||||
|
<option value="Autônomo">Autônomo</option>
|
||||||
|
</select>
|
||||||
|
</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">
|
||||||
|
</div>
|
||||||
|
<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">
|
||||||
|
<small class="text-muted">Para terceirizados</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<!-- Dados Acadêmicos -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 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">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<label for="tipo_instituicao" class="form-label">Tipo</label>
|
||||||
|
<select class="form-select" id="tipo_instituicao" name="tipo_instituicao">
|
||||||
|
<option value="">Selecione...</option>
|
||||||
|
<option value="Federal">Federal</option>
|
||||||
|
<option value="Estadual">Estadual</option>
|
||||||
|
<option value="Municipal">Municipal</option>
|
||||||
|
<option value="Privada">Privada</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Organização -->
|
||||||
|
<div class="tab-pane fade" id="tab-organizacao">
|
||||||
|
<!-- Dados Sindicais -->
|
||||||
|
<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">
|
||||||
|
</div>
|
||||||
|
<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">
|
||||||
|
</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">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3 d-flex align-items-center">
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="dirigente_sindical" name="dirigente_sindical">
|
||||||
|
<label class="form-check-label" for="dirigente_sindical">Dirigente Sindical</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<!-- Estado na Organização -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="estado_militante" class="form-label">Estado</label>
|
||||||
|
<select class="form-select" id="estado_militante" name="estado">
|
||||||
|
<option value="ATIVO">Ativo</option>
|
||||||
|
<option value="DESLIGADO">Desligado</option>
|
||||||
|
<option value="SUSPENSO">Suspenso</option>
|
||||||
|
<option value="AFASTADO">Afastado</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="celula" class="form-label">Célula</label>
|
||||||
|
<select class="form-select" id="celula" name="celula_id" required>
|
||||||
|
<option value="">Selecione...</option>
|
||||||
|
{% for celula in celulas %}
|
||||||
|
<option value="{{ celula.id }}">{{ celula.nome }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label d-block">Responsabilidades</label>
|
||||||
|
<div class="row g-3">
|
||||||
|
{% for valor, nome in Militante.get_responsabilidades_list() %}
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="resp_{{ valor }}"
|
||||||
|
name="responsabilidades" value="{{ valor }}">
|
||||||
|
<label class="form-check-label" for="resp_{{ valor }}">{{ nome }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user