Files
sure/app/components/savings/status_pill_component.rb
Guillem Arias 37dfd32628 fix(savings_goals): use display_status for inactive goals; hide pace + projection
- SavingsGoal#display_status returns :archived / :paused before falling
  through to the visualization status. Memoized like #status. The plain
  #status method keeps its meaning (visualization vs. target/pace) so
  callers that genuinely want "is this on track" — KPI sort, goal-card
  ring color, projection_payload — keep working unchanged.
- Savings::StatusPillComponent: status_key uses display_status; new
  :archived variant (bg-surface-inset / text-gray-700 / archive icon).
  Previously an archived goal showed "Behind" on the detail page while
  the archived banner said the goal was archived — conflicting signal.
- show.html.erb: paused/archived goals render a static recap card
  (current saved vs target) instead of the projection chart. Pace stat
  (avg vs required monthly) is also hidden — extrapolating "Behind by
  $X/mo" against a goal that isn't accepting contributions is misleading.
- New locale keys: savings_goals.status.archived,
  savings_goals.show.inactive.{heading_paused, heading_archived, body}.
- Tests cover display_status for archived / paused / active goals.
2026-05-11 19:40:21 +02:00

40 lines
1.2 KiB
Ruby

class Savings::StatusPillComponent < ApplicationComponent
# Text colors here intentionally use palette steps (green-700 / yellow-700 /
# gray-700) rather than `text-success` / `text-warning` / `text-secondary`
# tokens because the functional tokens drop below WCAG 1.4.3 4.5:1 on tinted
# surfaces in light mode (~2.88:1 / 3.0:1 / 4.16:1). Local override only;
# revert once we-promise/sure#1736 lands token-level fixes.
VARIANTS = {
on_track: { classes: "bg-green-500/10 text-green-700", icon: "circle-check" },
behind: { classes: "bg-yellow-500/10 text-yellow-700", icon: "triangle-alert" },
reached: { classes: "bg-green-500/10 text-green-700", icon: "star" },
no_target_date: { classes: "bg-surface-inset text-gray-700", icon: "infinity" },
paused: { classes: "bg-surface-inset text-gray-700", icon: "pause" },
archived: { classes: "bg-surface-inset text-gray-700", icon: "archive" }
}.freeze
def initialize(goal:)
@goal = goal
end
def status_key
@goal.display_status
end
def variant
VARIANTS.fetch(status_key, VARIANTS[:no_target_date])
end
def label
I18n.t("savings_goals.status.#{status_key}")
end
def classes
variant[:classes]
end
def icon_name
variant[:icon]
end
end