feat(finance): inline help tips + guided empty states
- Global .help-tip / .help-popup CSS + click-toggle JS in base.html - Global .setup-steps / .setup-step CSS for step-by-step guidance - Dashboard: ? tooltips on Free Cash (formula), Savings Rate, Net Worth - Goals: ? tooltips on Monthly Amount, At Current Rate, Free Cash After - Goals empty state: 3-step guide (planner → commit → fund) - Transactions empty state: 3-step guide (account → import → tag) with prominent Import / Add buttons replacing the inline text links Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e93cb38756
commit
6acea3da31
@ -481,6 +481,55 @@
|
|||||||
}
|
}
|
||||||
.empty-state h3 { font-size: 17px; color: var(--text2); margin-bottom: 8px; }
|
.empty-state h3 { font-size: 17px; color: var(--text2); margin-bottom: 8px; }
|
||||||
|
|
||||||
|
/* ── Setup steps (empty-state guidance) ────────────────────────── */
|
||||||
|
.setup-steps { display: flex; flex-direction: column; gap: 10px; max-width: 380px; margin: 0 auto 24px; }
|
||||||
|
.setup-step {
|
||||||
|
display: flex; align-items: flex-start; gap: 12px;
|
||||||
|
background: var(--bg3); border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-sm); padding: 11px 14px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.setup-step-num {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 22px; height: 22px; border-radius: 50%;
|
||||||
|
background: var(--accent-glow); border: 1px solid var(--accent);
|
||||||
|
color: var(--accent); font-size: 11px; font-weight: 700;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
}
|
||||||
|
.setup-step strong { display: block; font-size: 13px; color: var(--text); margin-bottom: 2px; }
|
||||||
|
.setup-step p { font-size: 12px; color: var(--text3); margin: 0; line-height: 1.5; }
|
||||||
|
|
||||||
|
/* ── Help tooltips ──────────────────────────────────────────────── */
|
||||||
|
.help-tip {
|
||||||
|
display: inline-flex; align-items: center; justify-content: center;
|
||||||
|
width: 14px; height: 14px; border-radius: 50%;
|
||||||
|
background: var(--bg3); border: 1px solid var(--border2);
|
||||||
|
color: var(--text3); font-size: 8px; font-weight: 700;
|
||||||
|
cursor: help; position: relative; vertical-align: middle;
|
||||||
|
margin-left: 5px; flex-shrink: 0; user-select: none;
|
||||||
|
transition: border-color 0.15s, color 0.15s;
|
||||||
|
}
|
||||||
|
.help-tip:hover, .help-tip.open { border-color: var(--accent); color: var(--accent); }
|
||||||
|
.help-popup {
|
||||||
|
display: none; position: absolute; z-index: 400;
|
||||||
|
bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
|
||||||
|
min-width: 230px; max-width: 300px;
|
||||||
|
background: var(--surface); border: 1px solid var(--border2);
|
||||||
|
border-radius: var(--radius); padding: 12px 14px;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
font-size: 12px; line-height: 1.6; color: var(--text2);
|
||||||
|
font-weight: 400; white-space: normal; text-align: left;
|
||||||
|
pointer-events: none; cursor: default;
|
||||||
|
}
|
||||||
|
.help-tip.open .help-popup { display: block; }
|
||||||
|
.help-popup strong { color: var(--text); display: block; margin-bottom: 4px; font-size: 12.5px; font-weight: 600; }
|
||||||
|
.help-popup code {
|
||||||
|
display: block; margin-top: 8px;
|
||||||
|
background: var(--bg3); border-radius: 6px; padding: 7px 9px;
|
||||||
|
font-family: ui-monospace, monospace; font-size: 11px; color: var(--accent2);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Misc utils ──────────────────────────────────────────────────── */
|
/* ── Misc utils ──────────────────────────────────────────────────── */
|
||||||
.flex { display: flex; gap: 8px; align-items: center; }
|
.flex { display: flex; gap: 8px; align-items: center; }
|
||||||
.flex-wrap { flex-wrap: wrap; }
|
.flex-wrap { flex-wrap: wrap; }
|
||||||
@ -730,6 +779,13 @@
|
|||||||
}
|
}
|
||||||
Chart.defaults.color = () => getThemeColor('textColor');
|
Chart.defaults.color = () => getThemeColor('textColor');
|
||||||
Chart.defaults.borderColor = () => getThemeColor('gridColor');
|
Chart.defaults.borderColor = () => getThemeColor('gridColor');
|
||||||
|
|
||||||
|
/* ── Help tips ────────────────────────────────────────────────────── */
|
||||||
|
document.addEventListener('click', e => {
|
||||||
|
const tip = e.target.closest('.help-tip');
|
||||||
|
document.querySelectorAll('.help-tip.open').forEach(t => { if (t !== tip) t.classList.remove('open'); });
|
||||||
|
if (tip) { e.stopPropagation(); tip.classList.toggle('open'); }
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -136,7 +136,7 @@
|
|||||||
|
|
||||||
<!-- Free cash total -->
|
<!-- Free cash total -->
|
||||||
<div style="display:flex; justify-content:space-between; align-items:center; padding:14px 0 0 0; margin-top:4px; border-top:1px solid var(--border);">
|
<div style="display:flex; justify-content:space-between; align-items:center; padding:14px 0 0 0; margin-top:4px; border-top:1px solid var(--border);">
|
||||||
<span style="font-size:14px; font-weight:600; color:var(--text);">{{$d.T.Get "dashboard.waterfall.free_cash"}}</span>
|
<span style="font-size:14px; font-weight:600; color:var(--text);">{{$d.T.Get "dashboard.waterfall.free_cash"}}<span class="help-tip">?<div class="help-popup"><strong>Free Cash</strong>What's left after paying for life and funding your goals. Positive means you have room to save or spend more.<code>Income − Living expenses − Goal contributions</code></div></span></span>
|
||||||
<div class="animate-counter {{if lt $d.WaterfallFreeCash 0}}negative{{else}}positive{{end}}"
|
<div class="animate-counter {{if lt $d.WaterfallFreeCash 0}}negative{{else}}positive{{end}}"
|
||||||
style="font-size:32px; font-weight:500; letter-spacing:-1px; line-height:1;"
|
style="font-size:32px; font-weight:500; letter-spacing:-1px; line-height:1;"
|
||||||
data-target="{{$d.WaterfallFreeCash}}" data-prefix="€">€0</div>
|
data-target="{{$d.WaterfallFreeCash}}" data-prefix="€">€0</div>
|
||||||
@ -249,7 +249,7 @@ function wfToggleCat(id) {
|
|||||||
<div class="grid" style="margin-bottom:16px;">
|
<div class="grid" style="margin-bottom:16px;">
|
||||||
|
|
||||||
<div class="card value-card animate-on-scroll">
|
<div class="card value-card animate-on-scroll">
|
||||||
<h2>{{$d.T.Get "dashboard.cards.savings_rate"}}</h2>
|
<h2>{{$d.T.Get "dashboard.cards.savings_rate"}}<span class="help-tip">?<div class="help-popup"><strong>Savings Rate</strong>The share of your income you're keeping. Above 20% is healthy; below 0% means you spent more than you earned.<code>(Income − Expenses) ÷ Income × 100</code></div></span></h2>
|
||||||
<div class="value {{if gt $d.SavingsRatePct 0}}positive{{else}}negative{{end}}">{{$d.SavingsRatePct}}%</div>
|
<div class="value {{if gt $d.SavingsRatePct 0}}positive{{else}}negative{{end}}">{{$d.SavingsRatePct}}%</div>
|
||||||
{{if $d.LastMonthSavingsRatePct}}
|
{{if $d.LastMonthSavingsRatePct}}
|
||||||
<p style="font-size:12px; margin-top:6px; {{if gt $d.SavingsRatePct $d.LastMonthSavingsRatePct}}color:var(--green){{else}}color:var(--red){{end}};">
|
<p style="font-size:12px; margin-top:6px; {{if gt $d.SavingsRatePct $d.LastMonthSavingsRatePct}}color:var(--green){{else}}color:var(--red){{end}};">
|
||||||
@ -259,7 +259,7 @@ function wfToggleCat(id) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card value-card animate-on-scroll">
|
<div class="card value-card animate-on-scroll">
|
||||||
<h2>{{$d.T.Get "dashboard.cards.net_worth"}}</h2>
|
<h2>{{$d.T.Get "dashboard.cards.net_worth"}}<span class="help-tip">?<div class="help-popup"><strong>Net Worth</strong>Everything you own minus everything you owe. Tracking this monthly is the single best measure of financial health.<code>Cash + Investments + Property equity − Debts</code></div></span></h2>
|
||||||
<div class="value animate-counter {{if lt $d.NetWorthCents 0}}negative{{else}}positive{{end}}"
|
<div class="value animate-counter {{if lt $d.NetWorthCents 0}}negative{{else}}positive{{end}}"
|
||||||
data-target="{{$d.NetWorthCents}}" data-prefix="€">€0.00</div>
|
data-target="{{$d.NetWorthCents}}" data-prefix="€">€0.00</div>
|
||||||
<p style="font-size:12px; color:var(--text3); margin-top:6px;"><a href="/networth" style="color:var(--accent);">{{$d.T.Get "dashboard.cards.net_worth_link"}}</a></p>
|
<p style="font-size:12px; color:var(--text3); margin-top:6px;"><a href="/networth" style="color:var(--accent);">{{$d.T.Get "dashboard.cards.net_worth_link"}}</a></p>
|
||||||
|
|||||||
@ -80,7 +80,7 @@
|
|||||||
|
|
||||||
<div style="display:flex; gap:24px; flex-wrap:wrap; align-items:flex-start;">
|
<div style="display:flex; gap:24px; flex-wrap:wrap; align-items:flex-start;">
|
||||||
<div style="text-align:center;">
|
<div style="text-align:center;">
|
||||||
<div style="font-size:11px; color:var(--text3); margin-bottom:3px;">{{$d.T.Get "goals.goal_card.need_per_month"}}</div>
|
<div style="font-size:11px; color:var(--text3); margin-bottom:3px;">{{$d.T.Get "goals.goal_card.need_per_month"}}<span class="help-tip">?<div class="help-popup"><strong>Monthly amount needed</strong>How much to set aside each month to hit your target by the deadline.<code>(Target − Saved) ÷ Months remaining</code></div></span></div>
|
||||||
<div class="animate-counter" style="font-size:18px; font-weight:600; color:var(--text);"
|
<div class="animate-counter" style="font-size:18px; font-weight:600; color:var(--text);"
|
||||||
data-target="{{.MonthlyCents}}" data-prefix="€">€0</div>
|
data-target="{{.MonthlyCents}}" data-prefix="€">€0</div>
|
||||||
</div>
|
</div>
|
||||||
@ -89,7 +89,7 @@
|
|||||||
<div style="font-size:18px; font-weight:600; color:var(--text);">{{.MonthsLeft}}</div>
|
<div style="font-size:18px; font-weight:600; color:var(--text);">{{.MonthsLeft}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="text-align:center;">
|
<div style="text-align:center;">
|
||||||
<div style="font-size:11px; color:var(--text3); margin-bottom:3px;">{{$d.T.Get "goals.goal_card.at_current_rate"}}</div>
|
<div style="font-size:11px; color:var(--text3); margin-bottom:3px;">{{$d.T.Get "goals.goal_card.at_current_rate"}}<span class="help-tip">?<div class="help-popup"><strong>At current rate</strong>How many months it would actually take based on your average monthly savings. Green = on time, red = late.</div></span></div>
|
||||||
{{if gt .MonthsAtCurrentRate 0}}
|
{{if gt .MonthsAtCurrentRate 0}}
|
||||||
<div style="font-size:18px; font-weight:600;
|
<div style="font-size:18px; font-weight:600;
|
||||||
{{if .Feasible}}color:var(--green){{else}}color:var(--red){{end}};">
|
{{if .Feasible}}color:var(--green){{else}}color:var(--red){{end}};">
|
||||||
@ -100,7 +100,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div style="text-align:center;">
|
<div style="text-align:center;">
|
||||||
<div style="font-size:11px; color:var(--text3); margin-bottom:3px;">{{$d.T.Get "goals.goal_card.disposable_after"}}</div>
|
<div style="font-size:11px; color:var(--text3); margin-bottom:3px;">{{$d.T.Get "goals.goal_card.disposable_after"}}<span class="help-tip">?<div class="help-popup"><strong>Free cash after this goal</strong>Your estimated monthly free cash if you commit to this goal. Red means you'd need to cut spending elsewhere.<code>Income − Living − All committed goals − This goal</code></div></span></div>
|
||||||
<div class="animate-counter" style="font-size:18px; font-weight:600;
|
<div class="animate-counter" style="font-size:18px; font-weight:600;
|
||||||
{{if ge .ImpactOnDisposable 0}}color:var(--green){{else}}color:var(--red){{end}};"
|
{{if ge .ImpactOnDisposable 0}}color:var(--green){{else}}color:var(--red){{end}};"
|
||||||
data-target="{{.ImpactOnDisposable}}" data-prefix="€">€0</div>
|
data-target="{{.ImpactOnDisposable}}" data-prefix="€">€0</div>
|
||||||
@ -186,7 +186,30 @@
|
|||||||
<div class="card empty-state animate-on-scroll">
|
<div class="card empty-state animate-on-scroll">
|
||||||
<div style="font-size:48px; margin-bottom:16px;">🎯</div>
|
<div style="font-size:48px; margin-bottom:16px;">🎯</div>
|
||||||
<h3>{{$d.T.Get "goals.empty.title"}}</h3>
|
<h3>{{$d.T.Get "goals.empty.title"}}</h3>
|
||||||
<p style="margin-bottom:20px;">{{$d.T.Get "goals.empty.desc"}}</p>
|
<p style="margin-bottom:24px; color:var(--text3);">{{$d.T.Get "goals.empty.desc"}}</p>
|
||||||
|
<div class="setup-steps">
|
||||||
|
<div class="setup-step">
|
||||||
|
<span class="setup-step-num">1</span>
|
||||||
|
<div>
|
||||||
|
<strong>Open the Goal Planner</strong>
|
||||||
|
<p>Simulate how much you need to save monthly and by when. Try different deadlines to find a realistic target.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setup-step">
|
||||||
|
<span class="setup-step-num">2</span>
|
||||||
|
<div>
|
||||||
|
<strong>Commit to the goal</strong>
|
||||||
|
<p>Mark a goal as committed so it appears in your waterfall and "what now?" prompts on the dashboard.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setup-step">
|
||||||
|
<span class="setup-step-num">3</span>
|
||||||
|
<div>
|
||||||
|
<strong>Fund it every month</strong>
|
||||||
|
<p>Add expense transactions tagged to this goal, or link a spending category in Settings to auto-tag them.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<a href="/goals?tab=planner" class="btn btn-primary">{{$d.T.Get "goals.empty.btn_open_planner"}}</a>
|
<a href="/goals?tab=planner" class="btn btn-primary">{{$d.T.Get "goals.empty.btn_open_planner"}}</a>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@ -100,10 +100,39 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{{else}}
|
{{else}}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="6" class="text-center text-muted" style="padding:44px;">
|
<td colspan="6" style="padding:0;">
|
||||||
{{$d.T.Get "transactions.table.empty_msg"}}
|
<div style="text-align:center; padding:40px 24px 48px;">
|
||||||
<a href="/import" style="color:var(--accent);">{{$d.T.Get "transactions.table.empty_import_link"}}</a> or
|
<div style="font-size:40px; margin-bottom:14px;">🧾</div>
|
||||||
<button class="btn btn-outline btn-sm" onclick="openAddModal()" style="margin-left:4px;">{{$d.T.Get "transactions.table.empty_add_btn"}}</button>.
|
<div style="font-size:16px; font-weight:600; color:var(--text2); margin-bottom:8px;">No transactions yet</div>
|
||||||
|
<p style="font-size:13px; color:var(--text3); margin-bottom:28px;">Transactions are the foundation of every chart, goal, and insight in this app.</p>
|
||||||
|
<div class="setup-steps" style="margin-bottom:28px;">
|
||||||
|
<div class="setup-step">
|
||||||
|
<span class="setup-step-num">1</span>
|
||||||
|
<div>
|
||||||
|
<strong>Add an account</strong>
|
||||||
|
<p>Go to Settings → Accounts and create your checking or savings account first.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setup-step">
|
||||||
|
<span class="setup-step-num">2</span>
|
||||||
|
<div>
|
||||||
|
<strong>Import a CSV or add manually</strong>
|
||||||
|
<p>Export a statement from your bank and import it, or tap "+ Add Transaction" to enter one by hand.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setup-step">
|
||||||
|
<span class="setup-step-num">3</span>
|
||||||
|
<div>
|
||||||
|
<strong>Tag categories & goals</strong>
|
||||||
|
<p>Once imported, categorise your spending. Link categories to goals so contributions are tracked automatically.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex; gap:10px; justify-content:center; flex-wrap:wrap;">
|
||||||
|
<a href="/import" class="btn btn-primary">{{$d.T.Get "transactions.table.empty_import_link"}}</a>
|
||||||
|
<button class="btn btn-outline" onclick="openAddModal()">{{$d.T.Get "transactions.table.empty_add_btn"}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user