refactor: melhorias na UI - formulário de pagamento e cards

This commit is contained in:
andersonid
2025-04-02 17:29:11 -03:00
parent f0faf4270b
commit ce3b5a4231
3 changed files with 218 additions and 91 deletions

55
app.py
View File

@@ -447,6 +447,7 @@ def listar_cotas():
@app.route("/pagamentos/novo", methods=["GET", "POST"])
@require_login
def novo_pagamento():
"""Rota para criar um novo pagamento"""
user = current_user
# Verificar se o usuário tem permissão para registrar pagamentos em alguma instância
@@ -458,24 +459,46 @@ def novo_pagamento():
return redirect(url_for('home'))
if request.method == "POST":
pagamentos = Pagamento(
militante_id=request.form["militante_id"],
tipo_pagamento_id=request.form["tipo_pagamento_id"],
valor=request.form["valor"],
data_pagamento=datetime.strptime(request.form["data_pagamento"], "%Y-%m-%d")
)
db_session.add(pagamentos)
try:
db_session.commit()
return redirect(url_for("listar_pagamentos"))
except Exception as e:
print(e)
db_session.rollback()
flash('Erro ao cadastrar pagamento. Verifique se os dados fornecidos são válidos.', 'error')
return render_template("novo_pagamento.html")
militante_id = request.form.get("militante_id")
tipo_pagamento_id = request.form.get("tipo_pagamento_id")
valor = request.form.get("valor")
data_pagamento = request.form.get("data_pagamento")
return render_template("novo_pagamento.html")
# Validações
if not all([militante_id, tipo_pagamento_id, valor, data_pagamento]):
flash("Todos os campos são obrigatórios.", "danger")
return redirect(url_for("novo_pagamento"))
# Criar novo pagamento
pagamento = Pagamento(
militante_id=militante_id,
tipo_pagamento_id=tipo_pagamento_id,
valor=float(valor),
data_pagamento=datetime.strptime(data_pagamento, "%Y-%m-%d")
)
db_session.add(pagamento)
db_session.commit()
flash("Pagamento registrado com sucesso!", "success")
return redirect(url_for("listar_pagamentos"))
except Exception as e:
db_session.rollback()
app.logger.error(f"Erro ao registrar pagamento: {str(e)}")
flash("Erro ao registrar pagamento. Por favor, tente novamente.", "danger")
return redirect(url_for("novo_pagamento"))
# GET - Renderizar formulário
militantes = db_session.query(Militante).order_by(Militante.nome).all()
tipos_pagamento = db_session.query(TipoPagamento).order_by(TipoPagamento.descricao).all()
hoje = datetime.now().strftime("%Y-%m-%d")
return render_template("novo_pagamento.html",
militantes=militantes,
tipos_pagamento=tipos_pagamento,
hoje=hoje)
# Rota para listar pagamentos
@app.route("/pagamentos")

View File

@@ -72,14 +72,109 @@ body {
.card {
border: none;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
transition: transform 0.3s ease;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
transition: all 0.3s ease;
overflow: hidden;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
box-shadow: 0 8px 24px rgba(0,0,0,0.1);
}
.card .card-body {
padding: 1.5rem;
}
/* Cards de estatísticas */
.card.bg-primary {
background: linear-gradient(135deg, #0d6efd, #0a58ca) !important;
}
.card.bg-success {
background: linear-gradient(135deg, #198754, #146c43) !important;
}
.card.bg-info {
background: linear-gradient(135deg, #0dcaf0, #0aa2c0) !important;
}
.card.bg-warning {
background: linear-gradient(135deg, #ffc107, #cc9a06) !important;
}
.card .fs-1 {
opacity: 0.8;
transition: all 0.3s ease;
}
.card:hover .fs-1 {
opacity: 1;
transform: scale(1.1);
}
.card h2 {
font-size: 2.5rem;
font-weight: 600;
margin: 0.5rem 0;
}
.card h6 {
font-size: 0.9rem;
font-weight: 400;
opacity: 0.9;
}
.card a {
font-size: 0.9rem;
opacity: 0.9;
transition: all 0.3s ease;
}
.card a:hover {
opacity: 1;
transform: translateX(5px);
}
/* Cards de listagem */
.card .card-header {
background: linear-gradient(to right, var(--secondary-dark), var(--secondary-color));
color: var(--text-light);
padding: 1rem 1.5rem;
border: none;
}
.card .card-header h5 {
font-size: 1.1rem;
font-weight: 500;
}
.list-group-item {
padding: 1rem 1.5rem;
border-left: none;
border-right: none;
transition: all 0.3s ease;
}
.list-group-item:hover {
background-color: rgba(0,0,0,0.02);
transform: translateX(5px);
}
.list-group-item h6 {
font-size: 1rem;
font-weight: 500;
margin: 0;
}
.list-group-item small {
font-size: 0.85rem;
}
.badge {
padding: 0.5em 0.8em;
font-weight: 500;
}
.btn-primary {

View File

@@ -6,80 +6,89 @@
<div class="container">
<div class="row">
<div class="col-md-8 offset-md-2">
<h1 class="mb-4">Novo Pagamento</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 class="card shadow-sm">
<div class="card-header bg-light">
<h4 class="card-title mb-0">
<i class="fas fa-money-bill-wave me-2"></i>Registrar Novo Pagamento
</h4>
</div>
<div class="mb-3">
<label for="tipo_pagamento" class="form-label">Tipo de Pagamento:</label>
<select class="form-select" id="tipo_pagamento" name="tipo_pagamento" required>
<option value="">Selecione o tipo</option>
<option value="cota">Cota</option>
<option value="jornal">Jornal</option>
<option value="assinatura">Assinatura</option>
<option value="campanha">Campanha Financeira</option>
</select>
<div class="card-body">
<form method="post" class="needs-validation" novalidate>
<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 um militante</option>
{% for militante in militantes %}
<option value="{{ militante.id }}">{{ militante.nome }}</option>
{% endfor %}
</select>
<div class="invalid-feedback">
Por favor, selecione um militante.
</div>
</div>
<div class="mb-3">
<label for="tipo_pagamento_id" class="form-label">Tipo de Pagamento:</label>
<select class="form-select" id="tipo_pagamento_id" name="tipo_pagamento_id" required>
<option value="">Selecione o tipo de pagamento</option>
{% for tipo in tipos_pagamento %}
<option value="{{ tipo.id }}">{{ tipo.descricao }}</option>
{% endfor %}
</select>
<div class="invalid-feedback">
Por favor, selecione o tipo de pagamento.
</div>
</div>
<div class="mb-3">
<label for="valor" class="form-label">Valor:</label>
<div class="input-group">
<span class="input-group-text">R$</span>
<input type="text" class="form-control money" id="valor" name="valor" required>
</div>
<div class="invalid-feedback">
Por favor, informe um valor válido.
</div>
</div>
<div class="mb-3">
<label for="data_pagamento" class="form-label">Data do Pagamento:</label>
<input type="date" class="form-control" id="data_pagamento" name="data_pagamento"
required max="{{ hoje }}">
<div class="invalid-feedback">
Por favor, informe uma data válida.
</div>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>Registrar
</button>
<a href="{{ url_for('listar_pagamentos') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-1"></i>Voltar
</a>
</div>
</form>
</div>
<div class="mb-3">
<label for="mes_referencia" class="form-label">Mês de Referência:</label>
<input type="month" class="form-control" id="mes_referencia" name="mes_referencia" required>
</div>
<div class="mb-3">
<label for="numero_jornal" class="form-label">Número do Jornal:</label>
<input type="number" class="form-control" id="numero_jornal" name="numero_jornal">
</div>
<div class="mb-3">
<label for="numero_inicial_assinatura" class="form-label">Número Inicial da Assinatura:</label>
<input type="number" class="form-control" id="numero_inicial_assinatura" name="numero_inicial_assinatura">
</div>
<div class="mb-3">
<label for="numero_final_assinatura" class="form-label">Número Final da Assinatura:</label>
<input type="number" class="form-control" id="numero_final_assinatura" name="numero_final_assinatura">
</div>
<div class="mb-3">
<label for="campanha_financeira" class="form-label">Campanha Financeira:</label>
<input type="text" class="form-control" id="campanha_financeira" name="campanha_financeira">
</div>
<div class="mb-3">
<label for="valor" class="form-label">Valor:</label>
<input type="number" class="form-control" id="valor" name="valor" step="0.01" required>
</div>
<div class="mb-3">
<label for="data_pagamento" class="form-label">Data do Pagamento:</label>
<input type="date" class="form-control" id="data_pagamento" name="data_pagamento" required>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">Registrar</button>
<a href="{{ url_for('listar_pagamentos') }}" class="btn btn-secondary">Voltar</a>
<a href="{{ url_for('home') }}" class="btn btn-outline-primary">Início</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.mask/1.14.16/jquery.mask.min.js"></script>
<script>
$(document).ready(function(){
$('.money').mask('000.000.000.000.000,00', {reverse: true});
// Converter valor para formato aceito pelo backend
$('form').on('submit', function(e) {
e.preventDefault();
const valor = $('#valor').val().replace(/\./g, '').replace(',', '.');
$('#valor').val(valor);
this.submit();
});
});
</script>
{% endblock %}