From 44b3190cd875c7fdc94d9363a3b233aac55fd24b Mon Sep 17 00:00:00 2001 From: Guillem Arias Date: Mon, 11 May 2026 14:17:54 +0200 Subject: [PATCH] feat(savings_goals): status pill icons + paused variant, attention-first sort, paused chip, rename "No date" to "Open-ended" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P4: status pills now carry an icon alongside the colored tint (circle-check / triangle-alert / star / infinity / pause), so color is no longer the sole signal. Drop the redundant dot. P4: default sort on the active goals list becomes attention-first — behind → on_track → no_target_date → paused, alphabetical within bucket. The user opens the page and lands on the goals that need them. P5: add a Paused filter chip + render paused goal cards with opacity-75 so they read as inactive at a glance. Rename "No date" chip to "Open-ended" — clearer to non-jargon readers. --- .../savings/goal_card_component.html.erb | 4 ++-- .../savings/status_pill_component.html.erb | 4 ++-- .../savings/status_pill_component.rb | 20 ++++++++++--------- app/controllers/savings_goals_controller.rb | 2 ++ app/views/savings_goals/index.html.erb | 2 +- config/locales/views/savings_goals/en.yml | 4 +++- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/app/components/savings/goal_card_component.html.erb b/app/components/savings/goal_card_component.html.erb index e15f2f998..6fcdf3cc7 100644 --- a/app/components/savings/goal_card_component.html.erb +++ b/app/components/savings/goal_card_component.html.erb @@ -1,9 +1,9 @@ <%= link_to savings_goal_path(goal), - class: "group block bg-container rounded-xl shadow-border-xs hover:bg-surface-hover transition-colors p-6", + class: "group block bg-container rounded-xl shadow-border-xs hover:bg-surface-hover transition-colors p-6 #{"opacity-75" if goal.paused?}", data: { savings_goals_filter_target: "card", goal_name: goal.name, - goal_status: goal.status + goal_status: goal.paused? ? "paused" : goal.status } do %>
<%= render Savings::GoalAvatarComponent.new(goal: goal, size: "lg") %> diff --git a/app/components/savings/status_pill_component.html.erb b/app/components/savings/status_pill_component.html.erb index 3246b6bbf..6b6bea3fc 100644 --- a/app/components/savings/status_pill_component.html.erb +++ b/app/components/savings/status_pill_component.html.erb @@ -1,4 +1,4 @@ - - + + <%= helpers.icon(icon_name, size: "xs") %> <%= label %> diff --git a/app/components/savings/status_pill_component.rb b/app/components/savings/status_pill_component.rb index d200f3833..ed8763bc7 100644 --- a/app/components/savings/status_pill_component.rb +++ b/app/components/savings/status_pill_component.rb @@ -1,32 +1,34 @@ class Savings::StatusPillComponent < ApplicationComponent VARIANTS = { - on_track: { classes: "bg-green-500/10 text-success", dot: "bg-green-600" }, - behind: { classes: "bg-yellow-500/10 text-warning", dot: "bg-yellow-600" }, - reached: { classes: "bg-green-500/10 text-success", dot: "bg-green-600" }, - no_target_date: { classes: "bg-surface-inset text-secondary", dot: "bg-gray-400" } + on_track: { classes: "bg-green-500/10 text-success", icon: "circle-check" }, + behind: { classes: "bg-yellow-500/10 text-warning", icon: "triangle-alert" }, + reached: { classes: "bg-green-500/10 text-success", icon: "star" }, + no_target_date: { classes: "bg-surface-inset text-secondary", icon: "infinity" }, + paused: { classes: "bg-surface-inset text-secondary", icon: "pause" } }.freeze def initialize(goal:) @goal = goal end - def status + def status_key + return :paused if @goal.paused? @goal.status end def variant - VARIANTS.fetch(status, VARIANTS[:no_target_date]) + VARIANTS.fetch(status_key, VARIANTS[:no_target_date]) end def label - I18n.t("savings_goals.status.#{status}") + I18n.t("savings_goals.status.#{status_key}") end def classes variant[:classes] end - def dot_classes - variant[:dot] + def icon_name + variant[:icon] end end diff --git a/app/controllers/savings_goals_controller.rb b/app/controllers/savings_goals_controller.rb index 4ab275ee7..34659b6e2 100644 --- a/app/controllers/savings_goals_controller.rb +++ b/app/controllers/savings_goals_controller.rb @@ -2,6 +2,7 @@ class SavingsGoalsController < ApplicationController before_action :set_savings_goal, only: %i[show edit update destroy pause resume complete archive unarchive] STATE_FILTERS = %w[all active paused completed archived].freeze + ACTIVE_STATUS_RANK = { behind: 0, on_track: 1, no_target_date: 2 }.freeze def index @counts = STATE_FILTERS.each_with_object({}) do |state, h| @@ -10,6 +11,7 @@ class SavingsGoalsController < ApplicationController all_goals = Current.family.savings_goals.with_current_balance.alphabetically.to_a @active_goals = all_goals.reject { |g| %w[completed archived].include?(g.state) } + .sort_by { |g| [ g.paused? ? 3 : ACTIVE_STATUS_RANK.fetch(g.status, 4), g.name.downcase ] } @completed_goals = all_goals.select { |g| g.state == "completed" } @linkable_account_count = Current.family.accounts.where(accountable_type: "Depository").visible.count diff --git a/app/views/savings_goals/index.html.erb b/app/views/savings_goals/index.html.erb index d813eca56..27be9f105 100644 --- a/app/views/savings_goals/index.html.erb +++ b/app/views/savings_goals/index.html.erb @@ -119,7 +119,7 @@
- <% %w[all on_track behind no_target_date].each do |status| %> + <% %w[all on_track behind no_target_date paused].each do |status| %> <% active = status == "all" %>