feat(finance): goal monthly funding status on dashboard
Each committed goal card now shows a "This month" section beneath the overall progress bar with three states: - ✓ On track (green) when funded >= monthly need - partial (amber) showing shortfall + "Fund it →" link - unfunded (red) with monthly amount needed + "Fund it →" link "Fund it →" deep-links to /transactions?fund_goal=<id> which auto-opens the Add Transaction modal with the goal pre-selected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5f60d963a0
commit
4cfe80e3d5
@ -230,6 +230,13 @@ section_title = "Committed goals"
|
|||||||
all_goals_link = "→ all goals"
|
all_goals_link = "→ all goals"
|
||||||
months_left = "mo left"
|
months_left = "mo left"
|
||||||
per_month_needed = "/mo needed"
|
per_month_needed = "/mo needed"
|
||||||
|
of = "of"
|
||||||
|
this_month = "This month"
|
||||||
|
funded = "On track"
|
||||||
|
fund_link = "Fund it"
|
||||||
|
needed = "needed"
|
||||||
|
left_to_fund = "left to fund"
|
||||||
|
needed_this_month = "needed this month"
|
||||||
|
|
||||||
[dashboard.fixed_costs]
|
[dashboard.fixed_costs]
|
||||||
section_title = "Fixed costs"
|
section_title = "Fixed costs"
|
||||||
|
|||||||
@ -230,6 +230,13 @@ section_title = "Objetivos comprometidos"
|
|||||||
all_goals_link = "→ todos os objetivos"
|
all_goals_link = "→ todos os objetivos"
|
||||||
months_left = "meses restantes"
|
months_left = "meses restantes"
|
||||||
per_month_needed = "/mês necessários"
|
per_month_needed = "/mês necessários"
|
||||||
|
of = "de"
|
||||||
|
this_month = "Este mês"
|
||||||
|
funded = "No bom caminho"
|
||||||
|
fund_link = "Financiar"
|
||||||
|
needed = "necessários"
|
||||||
|
left_to_fund = "em falta"
|
||||||
|
needed_this_month = "necessários este mês"
|
||||||
|
|
||||||
[dashboard.fixed_costs]
|
[dashboard.fixed_costs]
|
||||||
section_title = "Custos fixos"
|
section_title = "Custos fixos"
|
||||||
|
|||||||
@ -377,28 +377,65 @@ function wfToggleCat(id) {
|
|||||||
<h2>{{$d.T.Get "dashboard.goals.section_title"}}</h2>
|
<h2>{{$d.T.Get "dashboard.goals.section_title"}}</h2>
|
||||||
<a href="/goals" style="font-size:12px; color:var(--text3);">{{$d.T.Get "dashboard.goals.all_goals_link"}}</a>
|
<a href="/goals" style="font-size:12px; color:var(--text3);">{{$d.T.Get "dashboard.goals.all_goals_link"}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex; flex-direction:column; gap:14px;">
|
<div style="display:flex; flex-direction:column; gap:18px;">
|
||||||
{{range $d.DashGoals}}
|
{{range $d.DashGoals}}
|
||||||
|
{{$funded := index $d.GoalFundedThisMonth .ID}}
|
||||||
|
{{$needed := .MonthlyCents}}
|
||||||
|
{{$met := ge $funded $needed}}
|
||||||
|
{{$partial := and (gt $funded 0) (not $met)}}
|
||||||
|
{{$monthPct := 0}}
|
||||||
|
{{if gt $needed 0}}{{$monthPct = clampPct $funded $needed}}{{end}}
|
||||||
<div>
|
<div>
|
||||||
<div style="display:flex; justify-content:space-between; align-items:baseline; margin-bottom:6px;">
|
<!-- name + overall progress -->
|
||||||
<div style="display:flex; align-items:center; gap:8px;">
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:5px;">
|
||||||
<span style="font-size:15px;">{{if eq .Type "once"}}🎯{{else if eq .Type "deposit"}}🏠{{else if eq .Type "emergency"}}🛡️{{else}}📈{{end}}</span>
|
<div style="display:flex; align-items:center; gap:8px; min-width:0;">
|
||||||
<span style="font-size:13px; font-weight:500; color:var(--text);">{{.Name}}</span>
|
<span style="font-size:14px; flex-shrink:0;">{{if eq .Type "once"}}🎯{{else if eq .Type "deposit"}}🏠{{else if eq .Type "emergency"}}🛡️{{else}}📈{{end}}</span>
|
||||||
|
<span style="font-size:13px; font-weight:500; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">{{.Name}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex; align-items:center; gap:12px;">
|
<div style="display:flex; align-items:center; gap:10px; flex-shrink:0; margin-left:12px;">
|
||||||
<span style="font-size:12px; color:var(--text3);">{{.MonthsLeft}}{{$d.T.Get "dashboard.goals.months_left"}}</span>
|
<span style="font-size:11px; color:var(--text3);">{{.MonthsLeft}}{{$d.T.Get "dashboard.goals.months_left"}}</span>
|
||||||
<span style="font-size:12px; font-weight:600; color:{{if .Feasible}}var(--green){{else}}var(--red){{end}};">{{.ProgressPct}}%</span>
|
<span style="font-size:12px; font-weight:600; color:{{if .Feasible}}var(--green){{else}}var(--red){{end}};">{{.ProgressPct}}%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="background:var(--bg3); border-radius:99px; height:5px; overflow:hidden;">
|
<!-- overall progress bar -->
|
||||||
|
<div style="background:var(--bg3); border-radius:99px; height:4px; overflow:hidden; margin-bottom:3px;">
|
||||||
<div style="height:100%; border-radius:99px; width:{{.ProgressPct}}%;
|
<div style="height:100%; border-radius:99px; width:{{.ProgressPct}}%;
|
||||||
background:{{if .Feasible}}var(--green){{else}}var(--accent){{end}};
|
background:{{if .Feasible}}var(--green){{else}}var(--accent){{end}};
|
||||||
transition:width 1s ease;"></div>
|
transition:width 1s ease;"></div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex; justify-content:space-between; margin-top:4px; font-size:11px; color:var(--text3);">
|
<div style="font-size:11px; color:var(--text3); margin-bottom:10px;">
|
||||||
<span>€{{cents .SavedCents}} of €{{cents .TargetCents}}</span>
|
€{{cents .SavedCents}} {{$d.T.Get "dashboard.goals.of"}} €{{cents .TargetCents}}
|
||||||
<span style="color:{{if .Feasible}}var(--green){{else}}var(--red){{end}};">€{{cents .MonthlyCents}}{{$d.T.Get "dashboard.goals.per_month_needed"}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- this-month funding status -->
|
||||||
|
{{if gt $needed 0}}
|
||||||
|
<div style="background:var(--bg2); border-radius:8px; padding:8px 12px;">
|
||||||
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:6px;">
|
||||||
|
<span style="font-size:11px; color:var(--text3); text-transform:uppercase; letter-spacing:.04em;">{{$d.T.Get "dashboard.goals.this_month"}}</span>
|
||||||
|
{{if $met}}
|
||||||
|
<span style="font-size:11px; font-weight:600; color:var(--green);">✓ {{$d.T.Get "dashboard.goals.funded"}}</span>
|
||||||
|
{{else if $partial}}
|
||||||
|
<a href="/transactions?fund_goal={{.ID}}" style="font-size:11px; color:var(--accent); text-decoration:none;">{{$d.T.Get "dashboard.goals.fund_link"}} →</a>
|
||||||
|
{{else}}
|
||||||
|
<a href="/transactions?fund_goal={{.ID}}" style="font-size:11px; color:var(--red); text-decoration:none; font-weight:500;">{{$d.T.Get "dashboard.goals.fund_link"}} →</a>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
<div style="background:var(--bg3); border-radius:99px; height:3px; overflow:hidden; margin-bottom:5px;">
|
||||||
|
<div style="height:100%; border-radius:99px; width:{{$monthPct}}%;
|
||||||
|
background:{{if $met}}var(--green){{else if $partial}}var(--accent){{else}}var(--red){{end}};
|
||||||
|
transition:width 1s ease;"></div>
|
||||||
|
</div>
|
||||||
|
<div style="font-size:11px; {{if $met}}color:var(--green){{else if $partial}}color:var(--text2){{else}}color:var(--red){{end}};">
|
||||||
|
{{if $met}}
|
||||||
|
€{{cents $funded}} {{$d.T.Get "dashboard.goals.of"}} €{{cents $needed}} {{$d.T.Get "dashboard.goals.needed"}}
|
||||||
|
{{else if $partial}}
|
||||||
|
€{{cents $funded}} {{$d.T.Get "dashboard.goals.of"}} €{{cents $needed}} — €{{cents (sub $needed $funded)}} {{$d.T.Get "dashboard.goals.left_to_fund"}}
|
||||||
|
{{else}}
|
||||||
|
€{{cents $needed}} {{$d.T.Get "dashboard.goals.needed_this_month"}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user