mirror of
https://github.com/we-promise/sure.git
synced 2026-05-31 00:09:01 +00:00
Previous savings goals UI looked nothing like the Claude Design output
(see sure-design-context/design/savings-goals/project/goals/*.jsx) and
the hand-rolled ring did not match the segmented D3 donut used at
app/views/budgets/_budget_donut.html.erb. This rewires the surface end
to end.
Donut chart:
- SavingsGoal#to_donut_segments_json returns the same segment shape as
Budget#to_donut_segments_json: filled portion in goal color, unused
remainder as `var(--budget-unallocated-fill)`. Visual identity is now
the same: segmented arc with cornerRadius and gap, courtesy of the
shared `donut-chart` Stimulus controller and D3.
- ProgressRingComponent renders a `data-controller="donut-chart"` div
with the same default-content/inner-text pattern as `_budget_donut`.
Index page (matches GoalsIndex.jsx):
- Page header: title + "Save toward what matters." subtitle + "New goal"
primary CTA right-aligned.
- Summary strip card: total saved / target, overall bar, active goals,
on-track ratio, behind count.
- State filter rendered as DS::Tabs-style pill nav (`bg-surface-inset
p-1 rounded-lg`, white-pill active state).
- Cards rebuilt: avatar (44px, rounded-xl, white initial on goal color)
+ name + secondary line ("N days left · by date" / "No target date" /
"Completed" / "Past due"), status pill with leading dot, big
$current/$target line + percent, bar in status colour, AccountStack
(overlapping initials) + "N accounts" + "to go".
Goal detail (matches GoalDetail.jsx):
- Header: 64px avatar + h1 name + status pill + "Target $X by date ·
N days left" subline + Edit (outline) + Add contribution (primary) +
kebab (DS::Menu for AASM transitions).
- Donut-chart ring card with stats overlay.
- 4-col stat row (Avg monthly, Total contributions, Target date,
Started) with mono numerals and "Needs $X/mo" / "Above target pace"
sub-captions where relevant.
- Two-col bottom: contributions list (avatar + account · date · source
· green +$amount) and funding accounts breakdown (stacked bar +
per-account row with $ and % of saved).
New components: Savings::AccountStackComponent (overlapping account
initials with ring-2 ring-container). StatusPillComponent now uses a
leading colored dot instead of an icon. GoalAvatarComponent radii
match Claude Design (rounded-md/lg/xl/2xl) and white initial.
Locale: new keys under savings_goals.{index.subtitle, index.summary.*,
goal_card.{accounts,days_left,completed,past_due,no_target_date},
show.header.*, show.ring.{of,to_go}, show.stats.*, show.funding_balance,
show.of_saved, show.notes}.
28 lines
1.3 KiB
Plaintext
28 lines
1.3 KiB
Plaintext
<% if total.zero? %>
|
|
<p class="text-sm text-secondary"><%= t("savings_goals.show.no_contributions_yet") %></p>
|
|
<% else %>
|
|
<div class="flex h-2 rounded-full overflow-hidden mb-4">
|
|
<% rows.each do |row| %>
|
|
<% next if row[:amount].to_d.zero? %>
|
|
<div style="width: <%= percent_for(row[:amount]) %>%; background-color: <%= goal.color %>;"
|
|
title="<%= row[:account].name %>"></div>
|
|
<% end %>
|
|
</div>
|
|
|
|
<ul class="space-y-3">
|
|
<% rows.each do |row| %>
|
|
<li class="flex items-center gap-3">
|
|
<%= render Savings::GoalAvatarComponent.new(name: row[:account].name, color: goal.color, size: "sm") %>
|
|
<div class="flex-1 min-w-0">
|
|
<p class="text-sm font-medium text-primary truncate"><%= row[:account].name %></p>
|
|
<p class="text-[11px] text-subdued"><%= row[:account].subtype&.titleize || row[:account].accountable_type %> · <%= t("savings_goals.show.funding_balance", amount: Money.new(row[:account].balance, row[:account].currency).format) %></p>
|
|
</div>
|
|
<div class="text-right">
|
|
<p class="text-sm font-medium text-primary tabular-nums"><%= row[:money].format %></p>
|
|
<p class="text-[10px] text-subdued tabular-nums"><%= percent_for(row[:amount]) %>% <%= t("savings_goals.show.of_saved") %></p>
|
|
</div>
|
|
</li>
|
|
<% end %>
|
|
</ul>
|
|
<% end %>
|