refactor: melhorias na UI - formulário de pagamento e cards
This commit is contained in:
55
app.py
55
app.py
@@ -447,6 +447,7 @@ def listar_cotas():
|
|||||||
@app.route("/pagamentos/novo", methods=["GET", "POST"])
|
@app.route("/pagamentos/novo", methods=["GET", "POST"])
|
||||||
@require_login
|
@require_login
|
||||||
def novo_pagamento():
|
def novo_pagamento():
|
||||||
|
"""Rota para criar um novo pagamento"""
|
||||||
user = current_user
|
user = current_user
|
||||||
|
|
||||||
# Verificar se o usuário tem permissão para registrar pagamentos em alguma instância
|
# 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'))
|
return redirect(url_for('home'))
|
||||||
|
|
||||||
if request.method == "POST":
|
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:
|
try:
|
||||||
db_session.commit()
|
militante_id = request.form.get("militante_id")
|
||||||
return redirect(url_for("listar_pagamentos"))
|
tipo_pagamento_id = request.form.get("tipo_pagamento_id")
|
||||||
except Exception as e:
|
valor = request.form.get("valor")
|
||||||
print(e)
|
data_pagamento = request.form.get("data_pagamento")
|
||||||
db_session.rollback()
|
|
||||||
flash('Erro ao cadastrar pagamento. Verifique se os dados fornecidos são válidos.', 'error')
|
|
||||||
return render_template("novo_pagamento.html")
|
|
||||||
|
|
||||||
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
|
# Rota para listar pagamentos
|
||||||
@app.route("/pagamentos")
|
@app.route("/pagamentos")
|
||||||
|
|||||||
@@ -72,14 +72,109 @@ body {
|
|||||||
|
|
||||||
.card {
|
.card {
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 10px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
|
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
||||||
transition: transform 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover {
|
.card:hover {
|
||||||
transform: translateY(-5px);
|
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 {
|
.btn-primary {
|
||||||
|
|||||||
@@ -6,80 +6,89 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8 offset-md-2">
|
<div class="col-md-8 offset-md-2">
|
||||||
<h1 class="mb-4">Novo Pagamento</h1>
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-light">
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
<h4 class="card-title mb-0">
|
||||||
{% if messages %}
|
<i class="fas fa-money-bill-wave me-2"></i>Registrar Novo Pagamento
|
||||||
{% for category, message in messages %}
|
</h4>
|
||||||
<div class="alert alert-{{ category }}">{{ message }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
<form method="post" class="mb-4">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="militante_id" class="form-label">Militante:</label>
|
|
||||||
<select class="form-select" id="militante_id" name="militante_id" required>
|
|
||||||
<option value="">Selecione o militante</option>
|
|
||||||
{% for militante in militantes %}
|
|
||||||
<option value="{{ militante.id }}">{{ militante.nome }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
<div class="mb-3">
|
<form method="post" class="needs-validation" novalidate>
|
||||||
<label for="tipo_pagamento" class="form-label">Tipo de Pagamento:</label>
|
<div class="mb-3">
|
||||||
<select class="form-select" id="tipo_pagamento" name="tipo_pagamento" required>
|
<label for="militante_id" class="form-label">Militante:</label>
|
||||||
<option value="">Selecione o tipo</option>
|
<select class="form-select" id="militante_id" name="militante_id" required>
|
||||||
<option value="cota">Cota</option>
|
<option value="">Selecione um militante</option>
|
||||||
<option value="jornal">Jornal</option>
|
{% for militante in militantes %}
|
||||||
<option value="assinatura">Assinatura</option>
|
<option value="{{ militante.id }}">{{ militante.nome }}</option>
|
||||||
<option value="campanha">Campanha Financeira</option>
|
{% endfor %}
|
||||||
</select>
|
</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>
|
||||||
|
</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>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% 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 %}
|
||||||
|
|||||||
Reference in New Issue
Block a user