feat(finance): i18n — auth pages and homepage fully translated

Wire T translator into auth login, auth register, and homepage
handlers; convert all hardcoded strings in those three templates
to T.Get keys (business features section, mock screen data,
sign-in block, footer, page title). Completes full i18n coverage
across all Finance Hub templates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gonçalo Rodrigues 2026-06-17 22:42:27 +01:00
parent 4b7c01e632
commit 2166790fab
5 changed files with 120 additions and 122 deletions

View File

@ -454,6 +454,7 @@ func (h *Handler) Homepage(w http.ResponseWriter, r *http.Request) {
renderRaw(w, homepageTmpl, map[string]interface{}{ renderRaw(w, homepageTmpl, map[string]interface{}{
"Email": a.Email, "Email": a.Email,
"UserID": a.UserID, "UserID": a.UserID,
"T": h.t(r),
}) })
} }

View File

@ -218,6 +218,7 @@ func (h *Handler) AuthLogin(w http.ResponseWriter, r *http.Request) {
renderRaw(w, authLoginTmpl, map[string]any{ renderRaw(w, authLoginTmpl, map[string]any{
"GoogleEnabled": h.googleID != "", "GoogleEnabled": h.googleID != "",
"Error": errMsg, "Error": errMsg,
"T": h.t(r),
}) })
} }
@ -232,6 +233,7 @@ func (h *Handler) authLoginPost(w http.ResponseWriter, r *http.Request) {
"Error": msg, "Error": msg,
"Email": email, "Email": email,
"GoogleEnabled": h.googleID != "", "GoogleEnabled": h.googleID != "",
"T": h.t(r),
}) })
} }
@ -281,7 +283,7 @@ func (h *Handler) AuthRegister(w http.ResponseWriter, r *http.Request) {
h.authRegisterPost(w, r) h.authRegisterPost(w, r)
return return
} }
renderRaw(w, authRegisterTmpl, map[string]any{"GoogleEnabled": h.googleID != ""}) renderRaw(w, authRegisterTmpl, map[string]any{"GoogleEnabled": h.googleID != "", "T": h.t(r)})
} }
func (h *Handler) authRegisterPost(w http.ResponseWriter, r *http.Request) { func (h *Handler) authRegisterPost(w http.ResponseWriter, r *http.Request) {
@ -296,6 +298,7 @@ func (h *Handler) authRegisterPost(w http.ResponseWriter, r *http.Request) {
"Email": email, "Email": email,
"Name": name, "Name": name,
"GoogleEnabled": h.googleID != "", "GoogleEnabled": h.googleID != "",
"T": h.t(r),
}) })
} }

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sign in — Finance Hub</title> <title>{{.T.Get "auth.login.page_title"}}</title>
<style> <style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; } html, body { height: 100%; }
@ -169,35 +169,31 @@
<div class="wrap"> <div class="wrap">
<div class="logo-row"> <div class="logo-row">
<div class="logo-icon"></div> <div class="logo-icon"></div>
<span class="logo-name">Finance Hub</span> <span class="logo-name">{{.T.Get "auth.login.brand"}}</span>
</div> </div>
<div class="card"> <div class="card">
<h1>Welcome back</h1> <h1>{{.T.Get "auth.login.heading"}}</h1>
<p class="sub">Sign in to your account. No account? <a href="/auth/register">Create one →</a></p> <p class="sub">{{.T.Get "auth.login.subtext"}} <a href="/auth/register">{{.T.Get "auth.login.subtext_link"}}</a></p>
{{if .Error}} {{if .Error}}
<div class="error-box">{{.Error}}</div> <div class="error-box">{{.Error}}</div>
{{end}} {{end}}
{{if eq (index . "Error") "oauth"}}
<div class="error-box">Google sign-in failed. Please try again.</div>
{{end}}
<form method="POST" action="/auth/login"> <form method="POST" action="/auth/login">
<div class="field"> <div class="field">
<label for="email">Email</label> <label for="email">{{.T.Get "auth.login.field_email"}}</label>
<input type="email" id="email" name="email" value="{{.Email}}" placeholder="you@example.com" required autofocus> <input type="email" id="email" name="email" value="{{.Email}}" placeholder="{{.T.Get "auth.login.placeholder_email"}}" required autofocus>
</div> </div>
<div class="field"> <div class="field">
<label for="password">Password</label> <label for="password">{{.T.Get "auth.login.field_password"}}</label>
<input type="password" id="password" name="password" placeholder="••••••••" required> <input type="password" id="password" name="password" placeholder="{{.T.Get "auth.login.placeholder_password"}}" required>
</div> </div>
<button class="btn-primary" type="submit">Sign in →</button> <button class="btn-primary" type="submit">{{.T.Get "auth.login.btn_submit"}}</button>
</form> </form>
{{if .GoogleEnabled}} {{if .GoogleEnabled}}
<div class="divider">or</div> <div class="divider">{{.T.Get "auth.login.divider"}}</div>
<a class="btn-google" href="/auth/oauth/google"> <a class="btn-google" href="/auth/oauth/google">
<svg width="18" height="18" viewBox="0 0 24 24"> <svg width="18" height="18" viewBox="0 0 24 24">
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/> <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
@ -205,12 +201,12 @@
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z"/> <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z"/>
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/> <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
</svg> </svg>
Continue with Google {{.T.Get "auth.login.btn_google"}}
</a> </a>
{{end}} {{end}}
</div> </div>
<div class="footer-link"><a href="/">← Back to home</a></div> <div class="footer-link"><a href="/">{{.T.Get "auth.login.footer_back"}}</a></div>
</div> </div>
<script> <script>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create account — Finance Hub</title> <title>{{.T.Get "auth.register.page_title"}}</title>
<style> <style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; } html, body { height: 100%; }
@ -125,12 +125,12 @@
<div class="wrap"> <div class="wrap">
<div class="logo-row"> <div class="logo-row">
<div class="logo-icon"></div> <div class="logo-icon"></div>
<span class="logo-name">Finance Hub</span> <span class="logo-name">{{.T.Get "auth.register.brand"}}</span>
</div> </div>
<div class="card"> <div class="card">
<h1>Create your account</h1> <h1>{{.T.Get "auth.register.heading"}}</h1>
<p class="sub">Already have an account? <a href="/auth/login">Sign in →</a></p> <p class="sub">{{.T.Get "auth.register.subtext"}} <a href="/auth/login">{{.T.Get "auth.register.subtext_link"}}</a></p>
{{if .Error}} {{if .Error}}
<div class="error-box">{{.Error}}</div> <div class="error-box">{{.Error}}</div>
@ -144,34 +144,34 @@
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z"/> <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z"/>
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/> <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
</svg> </svg>
Continue with Google {{.T.Get "auth.register.btn_google"}}
</a> </a>
<div class="divider">or sign up with email</div> <div class="divider">{{.T.Get "auth.register.divider_oauth"}}</div>
{{end}} {{end}}
<form method="POST" action="/auth/register"> <form method="POST" action="/auth/register">
<div class="field"> <div class="field">
<label for="name">Name <span style="color:#364e4c;font-weight:400">(optional)</span></label> <label for="name">{{.T.Get "auth.register.field_name"}} <span style="color:#364e4c;font-weight:400">{{.T.Get "auth.register.name_optional"}}</span></label>
<input type="text" id="name" name="name" value="{{.Name}}" placeholder="Your name"> <input type="text" id="name" name="name" value="{{.Name}}" placeholder="{{.T.Get "auth.register.placeholder_name"}}">
</div> </div>
<div class="field"> <div class="field">
<label for="email">Email</label> <label for="email">{{.T.Get "auth.register.field_email"}}</label>
<input type="email" id="email" name="email" value="{{.Email}}" placeholder="you@example.com" required autofocus> <input type="email" id="email" name="email" value="{{.Email}}" placeholder="{{.T.Get "auth.register.placeholder_email"}}" required autofocus>
</div> </div>
<div class="field"> <div class="field">
<label for="password">Password</label> <label for="password">{{.T.Get "auth.register.field_password"}}</label>
<input type="password" id="password" name="password" placeholder="••••••••" required minlength="8"> <input type="password" id="password" name="password" placeholder="{{.T.Get "auth.register.placeholder_password"}}" required minlength="8">
<div class="hint">At least 8 characters</div> <div class="hint">{{.T.Get "auth.register.hint_password"}}</div>
</div> </div>
<div class="field"> <div class="field">
<label for="confirm">Confirm password</label> <label for="confirm">{{.T.Get "auth.register.field_confirm"}}</label>
<input type="password" id="confirm" name="confirm" placeholder="••••••••" required> <input type="password" id="confirm" name="confirm" placeholder="{{.T.Get "auth.register.placeholder_confirm"}}" required>
</div> </div>
<button class="btn-primary" type="submit">Create account →</button> <button class="btn-primary" type="submit">{{.T.Get "auth.register.btn_submit"}}</button>
</form> </form>
</div> </div>
<div class="footer-link"><a href="/">← Back to home</a></div> <div class="footer-link"><a href="/">{{.T.Get "auth.register.footer_back"}}</a></div>
</div> </div>
<script> <script>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Finance Hub — Personal &amp; Business Finance</title> <title>{{.T.Get "homepage.title"}}</title>
<meta name="description" content="A self-hosted finance platform for personal wealth and business finance management."> <meta name="description" content="A self-hosted finance platform for personal wealth and business finance management.">
<style> <style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
@ -517,51 +517,49 @@
<nav class="topnav"> <nav class="topnav">
<a href="/" class="topnav-logo"> <a href="/" class="topnav-logo">
<div class="topnav-logo-icon"></div> <div class="topnav-logo-icon"></div>
Finance Hub {{.T.Get "homepage.brand"}}
</a> </a>
<div class="topnav-right"> <div class="topnav-right">
{{if .UserID}} {{if .UserID}}
<span class="topnav-email">{{.Email}}</span> <span class="topnav-email">{{.Email}}</span>
<a href="/dashboard" class="btn-open">Personal</a> <a href="/dashboard" class="btn-open">{{.T.Get "homepage.nav.personal"}}</a>
<a href="/orgs" class="btn-open">Business</a> <a href="/orgs" class="btn-open">{{.T.Get "homepage.nav.business"}}</a>
{{else}} {{else}}
<a href="/auth/login" class="btn-signin">Sign in <span></span></a> <a href="/auth/login" class="btn-signin">{{.T.Get "homepage.nav.signin"}} <span></span></a>
{{end}} {{end}}
</div> </div>
</nav> </nav>
<!-- ── Hero ─────────────────────────────────────────────────────── --> <!-- ── Hero ─────────────────────────────────────────────────────── -->
<section class="hero"> <section class="hero">
<div class="hero-eyebrow"><span class="eyebrow-dot"></span>Self-hosted · Private · Open</div> <div class="hero-eyebrow"><span class="eyebrow-dot"></span>{{.T.Get "homepage.hero.eyebrow"}}</div>
<h1 class="hero-title"> <h1 class="hero-title">
<span class="hero-title-line1">Personal wealth.</span> <span class="hero-title-line1">{{.T.Get "homepage.hero.title_line1"}}</span>
<span class="hero-title-line2">Business finances.</span> <span class="hero-title-line2">{{.T.Get "homepage.hero.title_line2"}}</span>
</h1> </h1>
<p class="hero-sub"> <p class="hero-sub">{{.T.Get "homepage.hero.subtitle"}}</p>
One self-hosted platform to track your personal wealth and manage your organisation's budget — without handing your data to anyone.
</p>
<div class="hero-cta"> <div class="hero-cta">
{{if .UserID}} {{if .UserID}}
<a href="/dashboard" class="cta-primary">Open personal <span class="arrow"></span></a> <a href="/dashboard" class="cta-primary">{{.T.Get "homepage.hero.cta_open_personal"}} <span class="arrow"></span></a>
<a href="/orgs" class="cta-secondary">Open business</a> <a href="/orgs" class="cta-secondary">{{.T.Get "homepage.hero.cta_open_business"}}</a>
{{else}} {{else}}
<a href="/auth/login" class="cta-primary">Get started <span class="arrow"></span></a> <a href="/auth/login" class="cta-primary">{{.T.Get "homepage.hero.cta_get_started"}} <span class="arrow"></span></a>
<a href="#products" class="cta-secondary">See what's inside</a> <a href="#products" class="cta-secondary">{{.T.Get "homepage.hero.cta_see_inside"}}</a>
{{end}} {{end}}
</div> </div>
</section> </section>
<!-- ── Trust bar ────────────────────────────────────────────────── --> <!-- ── Trust bar ────────────────────────────────────────────────── -->
<div class="trust-bar"> <div class="trust-bar">
<div class="trust-item"><span class="trust-icon">🔒</span> Your data, your server</div> <div class="trust-item"><span class="trust-icon">🔒</span> {{.T.Get "homepage.trust.data_privacy"}}</div>
<div class="trust-sep"></div> <div class="trust-sep"></div>
<div class="trust-item"><span class="trust-icon">🏦</span> Multi-bank CSV import</div> <div class="trust-item"><span class="trust-icon">🏦</span> {{.T.Get "homepage.trust.multi_bank"}}</div>
<div class="trust-sep"></div> <div class="trust-sep"></div>
<div class="trust-item"><span class="trust-icon">🌙</span> Dark &amp; light mode</div> <div class="trust-item"><span class="trust-icon">🌙</span> {{.T.Get "homepage.trust.dark_light"}}</div>
<div class="trust-sep"></div> <div class="trust-sep"></div>
<div class="trust-item"><span class="trust-icon">👥</span> Role-based access</div> <div class="trust-item"><span class="trust-icon">👥</span> {{.T.Get "homepage.trust.role_access"}}</div>
<div class="trust-sep"></div> <div class="trust-sep"></div>
<div class="trust-item"><span class="trust-icon">📊</span> Real-time analytics</div> <div class="trust-item"><span class="trust-icon">📊</span> {{.T.Get "homepage.trust.analytics"}}</div>
</div> </div>
<!-- ── Products ─────────────────────────────────────────────────── --> <!-- ── Products ─────────────────────────────────────────────────── -->
@ -571,39 +569,39 @@
<a href="{{if .UserID}}/dashboard{{else}}/auth/login{{end}}" <a href="{{if .UserID}}/dashboard{{else}}/auth/login{{end}}"
class="pcard pcard-teal reveal" id="card-personal"> class="pcard pcard-teal reveal" id="card-personal">
<div class="pcard-glow"></div> <div class="pcard-glow"></div>
<div class="pcard-badge">Personal</div> <div class="pcard-badge">{{.T.Get "homepage.products.personal.badge"}}</div>
<div class="pcard-icon">🏦</div> <div class="pcard-icon">🏦</div>
<div class="pcard-name">My Finance</div> <div class="pcard-name">{{.T.Get "homepage.products.personal.name"}}</div>
<p class="pcard-desc">Full visibility into your personal wealth — spending, investments, goals, and net worth in one place.</p> <p class="pcard-desc">{{.T.Get "homepage.products.personal.desc"}}</p>
<ul class="pcard-features"> <ul class="pcard-features">
<li>Spending dashboard &amp; monthly trends</li> <li>{{.T.Get "homepage.products.personal.feature_1"}}</li>
<li>Transactions with category budgets</li> <li>{{.T.Get "homepage.products.personal.feature_2"}}</li>
<li>Investment portfolio tracking</li> <li>{{.T.Get "homepage.products.personal.feature_3"}}</li>
<li>Financial goals with progress</li> <li>{{.T.Get "homepage.products.personal.feature_4"}}</li>
<li>Net worth &amp; what-if projections</li> <li>{{.T.Get "homepage.products.personal.feature_5"}}</li>
<li>Tax report export</li> <li>{{.T.Get "homepage.products.personal.feature_6"}}</li>
<li>Household sharing &amp; multi-user view</li> <li>{{.T.Get "homepage.products.personal.feature_7"}}</li>
</ul> </ul>
<span class="pcard-cta">{{if .UserID}}Open app{{else}}Sign in{{end}} <span class="cta-arr"></span></span> <span class="pcard-cta">{{if .UserID}}{{.T.Get "homepage.products.personal.cta_open"}}{{else}}{{.T.Get "homepage.products.personal.cta_signin"}}{{end}} <span class="cta-arr"></span></span>
</a> </a>
<a href="{{if .UserID}}/orgs{{else}}/auth/login{{end}}" <a href="{{if .UserID}}/orgs{{else}}/auth/login{{end}}"
class="pcard pcard-purple reveal" id="card-business"> class="pcard pcard-purple reveal" id="card-business">
<div class="pcard-glow"></div> <div class="pcard-glow"></div>
<div class="pcard-badge">Business</div> <div class="pcard-badge">{{.T.Get "homepage.products.business.badge"}}</div>
<div class="pcard-icon">🏢</div> <div class="pcard-icon">🏢</div>
<div class="pcard-name">Organisations</div> <div class="pcard-name">{{.T.Get "homepage.products.business.name"}}</div>
<p class="pcard-desc">Manage organisations, plan fiscal years, control event budgets, and keep teams financially aligned.</p> <p class="pcard-desc">{{.T.Get "homepage.products.business.desc"}}</p>
<ul class="pcard-features"> <ul class="pcard-features">
<li>Multi-org with fiscal year lifecycle</li> <li>{{.T.Get "homepage.products.business.feature_1"}}</li>
<li>Teams with emoji avatars &amp; roles</li> <li>{{.T.Get "homepage.products.business.feature_2"}}</li>
<li>Events with budget lines &amp; goals</li> <li>{{.T.Get "homepage.products.business.feature_3"}}</li>
<li>Purchase requests &amp; approval flow</li> <li>{{.T.Get "homepage.products.business.feature_4"}}</li>
<li>Ledger &amp; bank CSV import</li> <li>{{.T.Get "homepage.products.business.feature_5"}}</li>
<li>Variance analysis per event &amp; team</li> <li>{{.T.Get "homepage.products.business.feature_6"}}</li>
<li>End-of-year narrative report</li> <li>{{.T.Get "homepage.products.business.feature_7"}}</li>
</ul> </ul>
<span class="pcard-cta">{{if .UserID}}Open orgs{{else}}Sign in{{end}} <span class="cta-arr"></span></span> <span class="pcard-cta">{{if .UserID}}{{.T.Get "homepage.products.business.cta_open"}}{{else}}{{.T.Get "homepage.products.business.cta_signin"}}{{end}} <span class="cta-arr"></span></span>
</a> </a>
</div> </div>
@ -614,29 +612,29 @@
<!-- ── Personal features ─────────────────────────────────────────── --> <!-- ── Personal features ─────────────────────────────────────────── -->
<div class="feature-section"> <div class="feature-section">
<div class="feature-text"> <div class="feature-text">
<p class="section-label teal">Personal Finance</p> <p class="section-label teal">{{.T.Get "homepage.features.personal.section_label"}}</p>
<h2 class="section-title">Complete visibility<br>over your wealth</h2> <h2 class="section-title">{{.T.Get "homepage.features.personal.section_title"}}</h2>
<p class="section-sub">From daily coffee to long-term portfolio — everything in one dashboard, updated as you import.</p> <p class="section-sub">{{.T.Get "homepage.features.personal.section_sub"}}</p>
<div class="feature-chips"> <div class="feature-chips">
<div class="feature-chip reveal"> <div class="feature-chip reveal">
<div class="feature-chip-icon teal">📊</div> <div class="feature-chip-icon teal">📊</div>
<div> <div>
<div class="feature-chip-title">Smart dashboard</div> <div class="feature-chip-title">{{.T.Get "homepage.features.personal.chip1_title"}}</div>
<div class="feature-chip-desc">Monthly income vs spending, top categories, recent transactions, and budget health at a glance.</div> <div class="feature-chip-desc">{{.T.Get "homepage.features.personal.chip1_desc"}}</div>
</div> </div>
</div> </div>
<div class="feature-chip reveal"> <div class="feature-chip reveal">
<div class="feature-chip-icon teal">📈</div> <div class="feature-chip-icon teal">📈</div>
<div> <div>
<div class="feature-chip-title">Portfolio tracking</div> <div class="feature-chip-title">{{.T.Get "homepage.features.personal.chip2_title"}}</div>
<div class="feature-chip-desc">Import trades, track ISIN-mapped tickers, see P&amp;L and allocation by asset class.</div> <div class="feature-chip-desc">{{.T.Get "homepage.features.personal.chip2_desc"}}</div>
</div> </div>
</div> </div>
<div class="feature-chip reveal"> <div class="feature-chip reveal">
<div class="feature-chip-icon teal">🎯</div> <div class="feature-chip-icon teal">🎯</div>
<div> <div>
<div class="feature-chip-title">Goals &amp; projections</div> <div class="feature-chip-title">{{.T.Get "homepage.features.personal.chip3_title"}}</div>
<div class="feature-chip-desc">Set savings goals, model scenarios, and see your projected net worth over time.</div> <div class="feature-chip-desc">{{.T.Get "homepage.features.personal.chip3_desc"}}</div>
</div> </div>
</div> </div>
</div> </div>
@ -651,31 +649,31 @@
<div class="mock-body"> <div class="mock-body">
<div style="display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:14px;"> <div style="display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:14px;">
<div class="mock-stat"> <div class="mock-stat">
<div class="mock-stat-label">Net income</div> <div class="mock-stat-label">{{.T.Get "homepage.mock.net_income_label"}}</div>
<div class="mock-stat-value teal">+€2,840</div> <div class="mock-stat-value teal">{{.T.Get "homepage.mock.net_income_value"}}</div>
<div class="mock-stat-sub">vs last month +12%</div> <div class="mock-stat-sub">{{.T.Get "homepage.mock.net_income_sub"}}</div>
</div> </div>
<div class="mock-stat"> <div class="mock-stat">
<div class="mock-stat-label">Net worth</div> <div class="mock-stat-label">{{.T.Get "homepage.mock.net_worth_label"}}</div>
<div class="mock-stat-value" style="font-size:18px;">€47,320</div> <div class="mock-stat-value" style="font-size:18px;">€47,320</div>
<div class="mock-stat-sub">↑ €1,240 this month</div> <div class="mock-stat-sub">{{.T.Get "homepage.mock.net_worth_sub"}}</div>
</div> </div>
</div> </div>
<div style="font-size:10px; color:var(--text3); font-weight:700; letter-spacing:0.06em; text-transform:uppercase; margin-bottom:8px;">Budget health</div> <div style="font-size:10px; color:var(--text3); font-weight:700; letter-spacing:0.06em; text-transform:uppercase; margin-bottom:8px;">{{.T.Get "homepage.mock.budget_health"}}</div>
<div class="mock-row"> <div class="mock-row">
<span class="mock-row-label">Groceries</span> <span class="mock-row-label">{{.T.Get "homepage.mock.groceries"}}</span>
<span class="mock-row-val green">€182 / €250</span> <span class="mock-row-val green">€182 / €250</span>
</div> </div>
<div class="mock-row"> <div class="mock-row">
<span class="mock-row-label">Dining out</span> <span class="mock-row-label">{{.T.Get "homepage.mock.dining_out"}}</span>
<span class="mock-row-val red">€148 / €100</span> <span class="mock-row-val red">€148 / €100</span>
</div> </div>
<div class="mock-row"> <div class="mock-row">
<span class="mock-row-label">Transport</span> <span class="mock-row-label">{{.T.Get "homepage.mock.transport"}}</span>
<span class="mock-row-val green">€64 / €120</span> <span class="mock-row-val green">€64 / €120</span>
</div> </div>
<div class="mock-row"> <div class="mock-row">
<span class="mock-row-label">Entertainment</span> <span class="mock-row-label">{{.T.Get "homepage.mock.entertainment"}}</span>
<span class="mock-row-val" style="color:var(--teal);">€38 / €80</span> <span class="mock-row-val" style="color:var(--teal);">€38 / €80</span>
</div> </div>
</div> </div>
@ -689,16 +687,16 @@
<div class="mock-dot" style="background:#f87171;"></div> <div class="mock-dot" style="background:#f87171;"></div>
<div class="mock-dot" style="background:#fbbf24;"></div> <div class="mock-dot" style="background:#fbbf24;"></div>
<div class="mock-dot" style="background:var(--green);"></div> <div class="mock-dot" style="background:var(--green);"></div>
<span style="font-size:11px; color:var(--text3); margin-left:8px;">Events — Summer Festival 2025</span> <span style="font-size:11px; color:var(--text3); margin-left:8px;">{{.T.Get "homepage.mock.events_label"}}</span>
</div> </div>
<div class="mock-body"> <div class="mock-body">
<div class="mock-stat" style="margin-bottom:14px;"> <div class="mock-stat" style="margin-bottom:14px;">
<div style="display:flex; justify-content:space-between; align-items:center;"> <div style="display:flex; justify-content:space-between; align-items:center;">
<div> <div>
<div class="mock-stat-label">Budget</div> <div class="mock-stat-label">{{.T.Get "homepage.mock.budget_label"}}</div>
<div class="mock-stat-value purple" style="color:var(--purple2);">€12,500</div> <div class="mock-stat-value purple" style="color:var(--purple2);">€12,500</div>
</div> </div>
<span class="mock-badge active">Active year</span> <span class="mock-badge active">{{.T.Get "homepage.mock.active_year"}}</span>
</div> </div>
<div class="mock-bar-wrap"> <div class="mock-bar-wrap">
<div style="display:flex; justify-content:space-between; font-size:10px; color:var(--text3); margin-bottom:4px;"> <div style="display:flex; justify-content:space-between; font-size:10px; color:var(--text3); margin-bottom:4px;">
@ -709,45 +707,45 @@
</div> </div>
</div> </div>
</div> </div>
<div style="font-size:10px; color:var(--text3); font-weight:700; letter-spacing:0.06em; text-transform:uppercase; margin-bottom:8px;">Requests</div> <div style="font-size:10px; color:var(--text3); font-weight:700; letter-spacing:0.06em; text-transform:uppercase; margin-bottom:8px;">{{.T.Get "homepage.mock.requests_label"}}</div>
<div class="mock-row"> <div class="mock-row">
<span class="mock-row-label">Sound equipment rental</span> <span class="mock-row-label">{{.T.Get "homepage.mock.sound_equipment"}}</span>
<span class="mock-badge approved">Approved</span> <span class="mock-badge approved">{{.T.Get "homepage.mock.approved"}}</span>
</div> </div>
<div class="mock-row"> <div class="mock-row">
<span class="mock-row-label">Catering deposit</span> <span class="mock-row-label">{{.T.Get "homepage.mock.catering_deposit"}}</span>
<span class="mock-badge pending">Pending</span> <span class="mock-badge pending">{{.T.Get "homepage.mock.pending"}}</span>
</div> </div>
<div class="mock-row"> <div class="mock-row">
<span class="mock-row-label">Stage lighting</span> <span class="mock-row-label">{{.T.Get "homepage.mock.stage_lighting"}}</span>
<span class="mock-badge active">In transit</span> <span class="mock-badge active">{{.T.Get "homepage.mock.in_transit"}}</span>
</div> </div>
</div> </div>
</div> </div>
<div class="feature-text"> <div class="feature-text">
<p class="section-label purple">Business Finance</p> <p class="section-label purple">{{.T.Get "homepage.features.business.section_label"}}</p>
<h2 class="section-title">Budget control<br>for your org</h2> <h2 class="section-title">{{.T.Get "homepage.features.business.section_title"}}</h2>
<p class="section-sub">From fiscal year kick-off to the final report — with full traceability for every euro spent.</p> <p class="section-sub">{{.T.Get "homepage.features.business.section_sub"}}</p>
<div class="feature-chips"> <div class="feature-chips">
<div class="feature-chip reveal"> <div class="feature-chip reveal">
<div class="feature-chip-icon purple">📅</div> <div class="feature-chip-icon purple">📅</div>
<div> <div>
<div class="feature-chip-title">Fiscal year lifecycle</div> <div class="feature-chip-title">{{.T.Get "homepage.features.business.chip1_title"}}</div>
<div class="feature-chip-desc">Plan events and budgets per year. Activate, lock, and close with an end-of-year report.</div> <div class="feature-chip-desc">{{.T.Get "homepage.features.business.chip1_desc"}}</div>
</div> </div>
</div> </div>
<div class="feature-chip reveal"> <div class="feature-chip reveal">
<div class="feature-chip-icon purple">📋</div> <div class="feature-chip-icon purple">📋</div>
<div> <div>
<div class="feature-chip-title">Request &amp; approval flow</div> <div class="feature-chip-title">{{.T.Get "homepage.features.business.chip2_title"}}</div>
<div class="feature-chip-desc">Teams submit purchase requests. Finance approves, tracks delivery, and settles against the ledger.</div> <div class="feature-chip-desc">{{.T.Get "homepage.features.business.chip2_desc"}}</div>
</div> </div>
</div> </div>
<div class="feature-chip reveal"> <div class="feature-chip reveal">
<div class="feature-chip-icon purple">📊</div> <div class="feature-chip-icon purple">📊</div>
<div> <div>
<div class="feature-chip-title">Variance analysis</div> <div class="feature-chip-title">{{.T.Get "homepage.features.business.chip3_title"}}</div>
<div class="feature-chip-desc">Compare planned vs actual per event and per team. Identify overspend before the year closes.</div> <div class="feature-chip-desc">{{.T.Get "homepage.features.business.chip3_desc"}}</div>
</div> </div>
</div> </div>
</div> </div>
@ -758,10 +756,10 @@
{{if not .UserID}} {{if not .UserID}}
<div class="signin-block"> <div class="signin-block">
<div class="signin-card"> <div class="signin-card">
<h2>Ready to get started?</h2> <h2>{{.T.Get "homepage.signin_block.title"}}</h2>
<p>Finance Hub is self-hosted on your homelab. Sign in with your account to access your personal and business dashboards.</p> <p>{{.T.Get "homepage.signin_block.body"}}</p>
<a href="/auth/login" class="cta-primary" style="margin: 0 auto;"> <a href="/auth/login" class="cta-primary" style="margin: 0 auto;">
Sign in to Finance Hub <span class="arrow"></span> {{.T.Get "homepage.signin_block.cta"}} <span class="arrow"></span>
</a> </a>
</div> </div>
</div> </div>
@ -771,17 +769,17 @@
<footer class="footer"> <footer class="footer">
<a href="/" class="footer-logo"> <a href="/" class="footer-logo">
<div style="width:22px; height:22px; background:linear-gradient(135deg,var(--teal),var(--purple2)); border-radius:6px; display:flex; align-items:center; justify-content:center; font-size:11px;"></div> <div style="width:22px; height:22px; background:linear-gradient(135deg,var(--teal),var(--purple2)); border-radius:6px; display:flex; align-items:center; justify-content:center; font-size:11px;"></div>
Finance Hub {{.T.Get "homepage.brand"}}
</a> </a>
<div class="footer-links"> <div class="footer-links">
{{if .UserID}} {{if .UserID}}
<a href="/dashboard">Personal</a> <a href="/dashboard">{{.T.Get "homepage.nav.personal"}}</a>
<a href="/orgs">Business</a> <a href="/orgs">{{.T.Get "homepage.nav.business"}}</a>
{{else}} {{else}}
<a href="/auth/login">Sign in</a> <a href="/auth/login">{{.T.Get "homepage.nav.signin"}}</a>
{{end}} {{end}}
</div> </div>
<span>Self-hosted homelab</span> <span>{{.T.Get "homepage.footer_tagline"}}</span>
</footer> </footer>
</div><!-- .page --> </div><!-- .page -->