mirror of
https://github.com/we-promise/sure.git
synced 2026-05-31 08:19:03 +00:00
The show page repeated the same data multiple times across surfaces
that should each say one thing once. Per-screen counts before this
commit:
- Account % distribution: 4 places (distribution bar + dot-legend
strip + 5-bar weight pill + % column)
- Current balance: 3 places (ring, funding heading total, ring
"of $X" subline)
- Target amount: 3 places (header, ring subline, catch-up body)
- Target date: 3 places (header, catch-up body, chart axis)
- Pace: 2 places (catch-up body, projection subtitle)
- ".00" cents: every monetary string
This pass:
- Funding widget drops the dot-legend strip (color/name/% triplet
redundant with the distribution bar's color + the per-row avatar
color) and the 5-bar weight pill (rendered as "1-of-5 sliver" for
low-weight accounts — read as a glitch; the % number next to it
covered the same fact). Row grid shrinks from 5 to 4 columns.
- Funding section heading drops `· $187,031` — the ring card
already carries the total balance.
- Catch-up alert reframes:
Title was "Save $26,621/mo to stay on track" (the *full* required
rate, with the misleading "stay on track" while the pill says
"Behind"). Now "Save $20,002/mo more to catch up" using
`catch_up_delta_money` — the user's actual delta over current
pace.
Body collapsed from two with-date / no-date variants to a single
"Current pace $X/mo · required $Y/mo to hit your target." Drops
the target date duplication since the header already says it.
Pledge CTA pre-fills with the *delta*, not the full required —
so accepting it once funds the gap instead of stacking the full
required rate on top of existing pace.
Secondary link "Or adjust your target" → "Adjust target instead"
(less defeatist framing).
- Projection chart subtitle "At $X/mo you'll miss your target date."
drops the pace duplication (catch-up above already states pace).
New: "Falling short at current pace." Diagnostic only.
- All money on the show page uses `format(precision: 0)`. The ".00"
cents added no information at goal-tracking scale.
- Header `Record pledge` demotes to `outline` variant when status is
`:behind` — the catch-up alert below owns the primary action.
One primary action per surface.
Also adjacent fixes:
- Funding widget keys avatar / distribution color off `account.id`,
not `account.name`. Renaming an account no longer recolors it
retroactively; two accounts with name-hash collisions no longer
share a color (Ruby idiom audit finding).
- `Goals::StatusPillComponent`: add `:completed` variant with
`circle-check-big` icon. `Goal#display_status` now returns
`:completed` when `goal.completed?` so a manually-completed
goal (e.g. user stopped at 80%) reads "Completed" rather than
falling through to `:on_track`/`:behind` and lying on the index.
Locale: drop `body_with_date` (folded into `body`),
`projection.behind` no longer carries interpolation args (caller
doesn't pass them either), `projection.no_pace` plain-language
rewrite ("inflow" → "deposits"), add `status.completed: "Completed"`.
43 lines
1.6 KiB
Ruby
43 lines
1.6 KiB
Ruby
class Goals::StatusPillComponent < ApplicationComponent
|
|
# Text colors here intentionally use palette steps (green/yellow/gray-700)
|
|
# instead of the `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). Each variant carries
|
|
# a theme-dark: override so the dark-700 text doesn't disappear against
|
|
# the dark-mode tinted surface. Local override only; revert once
|
|
# we-promise/sure#1736 lands token-level fixes.
|
|
VARIANTS = {
|
|
on_track: { classes: "bg-green-500/10 text-green-700 theme-dark:text-green-300", icon: "circle-check" },
|
|
behind: { classes: "bg-yellow-500/10 text-yellow-700 theme-dark:text-yellow-300", icon: "triangle-alert" },
|
|
reached: { classes: "bg-green-500/10 text-green-700 theme-dark:text-green-300", icon: "star" },
|
|
completed: { classes: "bg-green-500/10 text-green-700 theme-dark:text-green-300", icon: "circle-check-big" },
|
|
no_target_date: { classes: "bg-surface-inset text-gray-700 theme-dark:text-gray-200", icon: "infinity" },
|
|
paused: { classes: "bg-surface-inset text-gray-700 theme-dark:text-gray-200", icon: "pause" },
|
|
archived: { classes: "bg-surface-inset text-gray-700 theme-dark:text-gray-200", 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("goals.status.#{status_key}")
|
|
end
|
|
|
|
def classes
|
|
variant[:classes]
|
|
end
|
|
|
|
def icon_name
|
|
variant[:icon]
|
|
end
|
|
end
|