mirror of
https://github.com/hanzoai/finance.git
synced 2026-10-30 18:36:28 +00:00
Deliverable 1 of the two customer-facing Hanzo Finance surfaces. The umbrella repo
now carries the frontend alongside the Go financial core (embedded in cloud).
packages/finance-ui (@hanzo/finance-ui, PUBLISHED 0.1.1 to npm) — the DRY core BOTH
surfaces render, so a spend/usage/credits card is identical in finance.hanzo.ai and the
console Finance module:
- FinanceClient boundary: stubFinanceClient() (preview/dev) ⇄ httpFinanceClient(transport)
(live /v1/finance/*); the host injects auth/transport, swap = one line, zero UI change.
- Optional-safe normalizers (renamed/absent field -> em-dash; card masked to brand+last4
by construction; USD cents end to end). FinanceDashboard + monochrome, theme-aware,
self-injecting styles (no CSS import). tsc clean; 27 unit tests.
web (finance.hanzo.ai, Next 15) — landing + Hanzo IAM login + treasury/payments app shell:
- Marketing landing: canonical Hanzo mark (viewBox 0 0 67 67 / 44.6369), tight monochrome
copy, embeds the SHARED board as the product preview.
- Auth: hanzo-finance PUBLIC PKCE OAuth (Hanzo IAM, canonical /v1/iam/oauth/*), secretless
BFF code exchange (+optional KMS confidential upgrade), sealed httpOnly session, AuthGate.
- App shell renders the shared FinanceDashboard over /v1/finance/* via a bearer BFF proxy
(stub preview until NEXT_PUBLIC_FINANCE_MODE=live). Build green (11/11 routes); the Next
standalone server boots + serves verified.
- web/iam/: hanzo-finance app REGISTERED + VERIFIED LIVE in IAM (get-app-login resolves,
no 'Invalid client_id'); JSON + runbook committed for re-provisioning.
CI: root hanzo.yml (images finance-web + test + deploy) + ~15-line cicd.yml importing
hanzoai/ci@main on arc (zero GHA minutes). web/Dockerfile builds the pnpm-workspace Next
standalone image — built by CI/CD, never locally.
156 lines
5.5 KiB
TypeScript
156 lines
5.5 KiB
TypeScript
'use client'
|
||
|
||
import { FinanceDashboard, stubFinanceClient } from '@hanzo/finance-ui'
|
||
import { config } from '~/config'
|
||
import { startSignin } from '~/lib/auth/iam'
|
||
import { BrandLockup, HanzoMark } from '~/components/Logo'
|
||
|
||
const FEATURES = [
|
||
{
|
||
icon: '⇄',
|
||
title: 'Double-entry ledger',
|
||
body: 'Every movement posts to an append-only, always-balanced ledger on per-tenant Hanzo Base. Books reconcile by construction — the reserve can never over-mint.',
|
||
},
|
||
{
|
||
icon: '⌁',
|
||
title: 'Payment connectors',
|
||
body: 'One connector API over cards, HUSD, and cash rails. Metered spend, credits, top-ups, and payouts flow through a single money surface.',
|
||
},
|
||
{
|
||
icon: '◈',
|
||
title: 'Treasury & reserve',
|
||
body: 'A backed reserve behind credits and payouts, hashed and anchored on the Hanzo L1 — EVM-auditable, immutable, reconciled to the penny.',
|
||
},
|
||
]
|
||
|
||
const FACTS = [
|
||
{ v: '1', l: 'Unified ledger' },
|
||
{ v: '50+', l: 'Payment processors' },
|
||
{ v: '100%', l: 'Reconciled by construction' },
|
||
{ v: 'USD ¢', l: 'Exact, never a float' },
|
||
]
|
||
|
||
export default function LandingPage(): React.JSX.Element {
|
||
const onStart = () => {
|
||
void startSignin(config.appPath)
|
||
}
|
||
return (
|
||
<>
|
||
<nav className="fin-nav">
|
||
<div className="fin-container fin-nav-inner">
|
||
<BrandLockup brand={config.brandName} />
|
||
<div className="fin-nav-actions">
|
||
<a className="fin-btn fin-btn--ghost" href={config.homepage} target="_blank" rel="noreferrer">
|
||
{config.brandName}
|
||
</a>
|
||
<button className="fin-btn" onClick={onStart}>
|
||
Sign in
|
||
</button>
|
||
<button className="fin-btn fin-btn--primary" onClick={onStart}>
|
||
Get started
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<header className="fin-hero">
|
||
<div className="fin-container">
|
||
<span className="fin-eyebrow">
|
||
<span className="fin-dot" /> Native financial core, embedded in Hanzo Cloud
|
||
</span>
|
||
<h1>One ledger for your money operations.</h1>
|
||
<p>
|
||
{config.productName} is a double-entry ledger, payment connectors, and a treasury dashboard — so you can run
|
||
your organization’s credits, payouts, and reserves on books that balance by construction.
|
||
</p>
|
||
<div className="fin-hero-cta">
|
||
<button className="fin-btn fin-btn--primary fin-btn--lg" onClick={onStart}>
|
||
Get started
|
||
</button>
|
||
<a className="fin-btn fin-btn--lg" href="#product">
|
||
See the platform
|
||
</a>
|
||
</div>
|
||
<div className="fin-hero-note">Sign in with Hanzo IAM · single sign-on across every Hanzo product</div>
|
||
</div>
|
||
|
||
<div className="fin-container fin-preview" id="product">
|
||
<div className="fin-preview-frame">
|
||
<div className="fin-preview-bar">
|
||
<i />
|
||
<i />
|
||
<i />
|
||
</div>
|
||
<div className="fin-preview-body">
|
||
<div style={{ padding: 18 }}>
|
||
<FinanceDashboard client={stubFinanceClient()} mode="preview" theme="dark" showTreasury />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<section className="fin-section">
|
||
<div className="fin-container">
|
||
<div className="fin-section-head">
|
||
<h2>Everything money touches, in one place</h2>
|
||
<p>Formance ported native — one Go binary on Base, not five microservices. The books are the source of truth.</p>
|
||
</div>
|
||
<div className="fin-grid-3">
|
||
{FEATURES.map((f) => (
|
||
<div className="fin-feature" key={f.title}>
|
||
<div className="fin-feature-icon">{f.icon}</div>
|
||
<h3>{f.title}</h3>
|
||
<p>{f.body}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section className="fin-section">
|
||
<div className="fin-container">
|
||
<div className="fin-metrics">
|
||
{FACTS.map((m) => (
|
||
<div className="fin-metric" key={m.l}>
|
||
<div className="fin-metric-v">{m.v}</div>
|
||
<div className="fin-metric-l">{m.l}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section className="fin-section fin-cta">
|
||
<div className="fin-container">
|
||
<h2>Bring your books onto Hanzo.</h2>
|
||
<p>Sign in with your Hanzo account — your organization’s finances, scoped to you.</p>
|
||
<button className="fin-btn fin-btn--primary fin-btn--lg" onClick={onStart}>
|
||
Get started
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<footer className="fin-footer">
|
||
<div className="fin-container fin-footer-inner">
|
||
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 9 }}>
|
||
<HanzoMark size={18} />
|
||
{config.productName} · © {new Date().getFullYear()} Hanzo AI
|
||
</span>
|
||
<div className="fin-footer-links">
|
||
<a href={config.homepage} target="_blank" rel="noreferrer">
|
||
{config.brandName}
|
||
</a>
|
||
<a href="https://docs.hanzo.ai" target="_blank" rel="noreferrer">
|
||
Docs
|
||
</a>
|
||
<button className="fin-btn fin-btn--ghost" style={{ padding: 0 }} onClick={onStart}>
|
||
Sign in
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
</>
|
||
)
|
||
}
|