diff --git a/expenses_manager/expenses/static/expenses/css/base.css b/expenses_manager/expenses/static/expenses/css/base.css index 48588b1..fb214c5 100644 --- a/expenses_manager/expenses/static/expenses/css/base.css +++ b/expenses_manager/expenses/static/expenses/css/base.css @@ -367,8 +367,23 @@ button[type="submit"]:hover, .filters-main { display: flex; flex-wrap: wrap; - gap: 0.5rem; align-items: center; + gap: 0.5rem; +} + +.filters-main select { + padding: 0.5rem 0.7rem; + font-family: inherit; + font-size: 0.95rem; + color: var(--color-text); + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: 6px; +} + +.filters-main select:focus { + outline: 2px solid var(--color-focus-ring); + outline-offset: 1px; } .filters-advanced { @@ -826,17 +841,14 @@ tr:hover { transition: width 0.3s ease-in-out; } -.progress-fill.low, .progress-fill.danger { background-color: var(--color-danger-accent); } -.progress-fill.medium, .progress-fill.warning { background-color: var(--color-warning-accent); } -.progress-fill.high, .progress-fill.complete { background-color: var(--color-success-accent); } diff --git a/expenses_manager/expenses/templates/expenses/dashboard.html b/expenses_manager/expenses/templates/expenses/dashboard.html index 49df5a5..f9ff28b 100644 --- a/expenses_manager/expenses/templates/expenses/dashboard.html +++ b/expenses_manager/expenses/templates/expenses/dashboard.html @@ -428,11 +428,7 @@ aria-valuenow="{{ goal.percentage|unlocalize }}" aria-label="Progreso de {{ goal.name }}" aria-valuetext="{{ goal.progress|floatformat:1 }}€ de {{ goal.target_amount|floatformat:1 }}€ ({{ goal.percentage|floatformat:1 }}%)"> -
diff --git a/expenses_manager/expenses/templates/goals/list.html b/expenses_manager/expenses/templates/goals/list.html index 9806361..66a4ee8 100644 --- a/expenses_manager/expenses/templates/goals/list.html +++ b/expenses_manager/expenses/templates/goals/list.html @@ -9,57 +9,86 @@ {% block content %}

Objetivos

-➕ Nuevo objetivo +
+ ➕ Nuevo objetivo +
+ +
+
+ + + + + Limpiar +
+
- - - - - - - - {% for goal in goals %} - - - - + + + + + + + + + {% for goal in goals %} + + + + + - - - {% endfor %} + + + {% empty %} + + + + {% endfor %} +
NombreTipoProgreso
{{ goal.name }} - {{ goal.get_kind_display }} - {% if goal.kind == "budget" %} - ({{ goal.get_period_display|lower }}) - {% endif %} - -
- - {{ goal.progress|floatformat:1 }}€ / {{ goal.target_amount|floatformat:1 }}€ - -
-
-
- - {{ goal.percentage|floatformat:1 }}% - - {% if goal.is_exceeded %} - Excedido +
NombreTipoProgreso
{{ goal.name }} + {{ goal.get_kind_display }} + {% if goal.kind == "budget" %} + ({{ goal.get_period_display|lower }}) {% endif %} - - +
+ + {{ goal.progress|floatformat:1 }}€ / {{ goal.target_amount|floatformat:1 }}€ + +
+
+
+ + {{ goal.percentage|floatformat:1 }}% + + {% if goal.is_exceeded %} + Excedido + {% endif %} +
+
- Editar - Eliminar -
+ Editar + Eliminar +
+

No hay objetivos

+ Añade el primero +
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/expenses_manager/expenses/tests/test_goals.py b/expenses_manager/expenses/tests/test_goals.py index e7717ae..2b3066e 100644 --- a/expenses_manager/expenses/tests/test_goals.py +++ b/expenses_manager/expenses/tests/test_goals.py @@ -1,6 +1,7 @@ import pytest from datetime import date from decimal import Decimal +from django.urls import reverse from expenses.models import Account, Category, Expense, Goal from expenses.forms import GoalForm @@ -194,3 +195,23 @@ def test_goalform_valid_payment_saves_goal(user, category): goal.save() assert Goal.objects.filter(pk=goal.pk, name="Ahorro viaje", kind=Goal.KIND_PAYMENT, category=category).exists() + + +def test_goal_list_filters_by_kind(auth_client, user, category): + Goal.objects.create(owner=user, name="Pago", target_amount=Decimal("100"), kind=Goal.KIND_PAYMENT, category=category) + Goal.objects.create(owner=user, name="Presupuesto", target_amount=Decimal("200"), kind=Goal.KIND_BUDGET, category=category, period=Goal.PERIOD_MONTH) + + response = auth_client.get(reverse('goal_list'), {"kind": Goal.KIND_BUDGET}) + + assert [g.name for g in response.context["goals"]] == ["Presupuesto"] + assert response.context["selected_kind"] == Goal.KIND_BUDGET + + +def test_goal_list_ignores_invalid_kind_param(auth_client, user, category): + Goal.objects.create(owner=user, name="Pago", target_amount=Decimal("100"), kind=Goal.KIND_PAYMENT, category=category) + + response = auth_client.get(reverse('goal_list'), {"kind": "bogus"}) + + assert response.status_code == 200 + assert [g.name for g in response.context["goals"]] == ["Pago"] + assert response.context["selected_kind"] == "" diff --git a/expenses_manager/expenses/views.py b/expenses_manager/expenses/views.py index a24ffc9..906a9a1 100644 --- a/expenses_manager/expenses/views.py +++ b/expenses_manager/expenses/views.py @@ -1071,11 +1071,21 @@ def category_delete(request, pk): def goal_list(request): goals = Goal.objects.filter(owner=request.user) + selected_kind = request.GET.get("kind") or "" + if selected_kind in dict(Goal.KIND_CHOICES): + goals = goals.filter(kind=selected_kind) + else: + selected_kind = "" + + goals = goals.order_by("name") + return render( request, "goals/list.html", { "goals": goals, + "kind_choices": Goal.KIND_CHOICES, + "selected_kind": selected_kind, "active_menu": "settings", }, )