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:
parent
4b7c01e632
commit
2166790fab
@ -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),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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 & 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 & 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 & 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 & 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 & 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 & roles</li>
|
<li>{{.T.Get "homepage.products.business.feature_2"}}</li>
|
||||||
<li>Events with budget lines & goals</li>
|
<li>{{.T.Get "homepage.products.business.feature_3"}}</li>
|
||||||
<li>Purchase requests & approval flow</li>
|
<li>{{.T.Get "homepage.products.business.feature_4"}}</li>
|
||||||
<li>Ledger & bank CSV import</li>
|
<li>{{.T.Get "homepage.products.business.feature_5"}}</li>
|
||||||
<li>Variance analysis per event & 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&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 & 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 & 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 -->
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user