Web app for LiquiFact — the global invoice liquidity network on Stellar. Next.js dashboard for SMEs (upload invoices, get liquidity) and investors (fund tokenized invoices, earn yield). Stellar wallet integration is planned.
Part of the LiquiFact stack: frontend (this repo) | backend (Express API) | contracts (Soroban).
- Node.js 20+ (LTS recommended)
- npm 9+
-
Clone the repo
git clone <this-repo-url> cd liquifact-frontend
-
Install dependencies
npm ci
-
Configure environment (optional)
cp .env.local.example .env.local # Set NEXT_PUBLIC_API_URL if the API is not at http://localhost:3001
| Command | Description |
|---|---|
npm run dev |
Start dev server (Turbopack) |
npm run lint |
Run ESLint |
npm test |
Run accessibility tests (Jest) |
npm run build |
Production build |
npm run start |
Start production server |
Default: http://localhost:3000. The home page can check API health at NEXT_PUBLIC_API_URL (default http://localhost:3001).
liquifact-frontend/
├─ app/
│ ├─ layout.js # Root layout, LiquiFact metadata (imports Geist via next/font/google)
│ ├─ page.js # Home (wallet CTA, API health check)
│ ├─ copy/ # Localized copy strings (e.g., en.js)
│ ├─ invoices/ # Invoices placeholder page
│ └─ invest/ # Invest placeholder page
├─ components/ # Shared UI components
│ ├─ ErrorBanner.jsx
│ ├─ Footer.jsx
│ ├─ InvoiceListSkeleton.jsx
│ ├─ ToastProvider.jsx
│ ├─ UploadZone.jsx
│ └─ WalletStatus.jsx
├─ public/ # Static assets
├─ tests/ # Jest / Playwright test suites
├─ .github/workflows/ci.yml # CI pipeline (lint + accessibility tests)
├─ .env.local.example
├─ eslint.config.mjs
├─ jest.config.js
├─ jest.setup.js
├─ next.config.mjs
├─ playwright.config.mjs
└─ package.json
Tech: Next.js 16 (App Router), React 19, Tailwind CSS 4.
GitHub Actions runs on every push and pull request to main:
- Lint –
npm run lint - Test Accessibility –
npm test --silent
Both jobs must pass before a PR can be merged.
See CONTRIBUTING.md for the full contributor workflow, branch naming convention, local checks, and accessibility expectations.
- Fork the repo and clone your fork.
- Create a branch from
main:git checkout -b feature/your-featureorfix/your-fix. - Setup:
npm ci, optionallycp .env.local.example .env.local. - Make changes:
- Follow existing patterns under
app/andcomponents/. - Run
npm run lintandnpm testlocally.
- Follow existing patterns under
- Commit with clear messages (e.g.,
feat: add X,fix: Y). - Push to your fork and open a Pull Request to
main. - Wait for CI and address review feedback.
We welcome UI improvements, new pages (e.g., invoice upload, marketplace), and Stellar wallet integration aligned with the LiquiFact product.
See COMPONENTS.md for the full component library reference — props, accessibility notes, and usage examples for every shared component (ErrorBanner, Footer, InvoiceListSkeleton, ToastProvider, UploadZone, WalletStatus).
Global tokens are defined in app/globals.css and used across all components.
| Token | Value | Tailwind equivalent |
|---|---|---|
--color-bg |
#0f0f0f |
slate-950 |
--color-primary |
#06b6d4 |
cyan-400 |
Font: Geist is loaded via next/font/google (see app/layout.js). Headings use font-bold; body copy uses the default weight.
See TESTING.md for the full guide covering Jest unit/accessibility tests and Playwright end‑to‑end setup.
MIT (see root LiquiFact project for full license).
Web app for LiquiFact — the global invoice liquidity network on Stellar. Next.js dashboard for SMEs (upload invoices, get liquidity) and investors (fund tokenized invoices, earn yield). Stellar wallet integration is planned.
Part of the LiquiFact stack: frontend (this repo) | backend (Express API) | contracts (Soroban).
- Node.js 20+ (LTS recommended)
- npm 9+
-
Clone the repo
git clone <this-repo-url> cd liquifact-frontend
-
Install dependencies
npm ci
-
Configure environment (optional)
cp .env.local.example .env.local # Set NEXT_PUBLIC_API_URL if the API is not at http://localhost:3001
For frontend/backend contract details see:
| Command | Description |
|---|---|
npm run dev |
Start dev server (Turbopack) |
npm run build |
Production build |
npm run start |
Start production server |
npm run lint |
Run ESLint |
npm run test:e2e |
Run Playwright smoke tests |
Default: http://localhost:3000. The home page can check API health at NEXT_PUBLIC_API_URL (default http://localhost:3001).
The Invest page (app/invest/page.js) includes an issuer search field above the invoice list. Typing in the field filters invoices by case-insensitive substring match on issuer. Input is debounced at 200ms to avoid thrashing on every keystroke. When a filter is active, the aria-live status region announces the match count (e.g. "2 of 3 invoices match"). A distinct "no matches" state is shown when the filter yields zero results, separate from the empty-marketplace state.
liquifact-frontend/
├── app/
│ ├── layout.js # Root layout, LiquiFact metadata
│ ├── page.js # Home (wallet CTA, API health check)
│ ├── copy/en.js # Centralised UI copy
│ ├── invoices/ # SME invoice upload page
│ └── invest/ # Investor marketplace
│ ├── page.js # Marketplace list (links to detail)
│ ├── loading.js # Marketplace skeleton
│ ├── lib.js # Mock invoice data + helpers
│ └── [id]/ # Invoice detail + funding CTA
│ ├── page.js # Full invoice details
│ ├── loading.js # Detail skeleton
│ └── not-found.js # Unknown invoice fallback
├── components/
│ ├── WalletStatus.jsx # Wallet connection UI
│ └── WalletContext.jsx # Shared wallet state provider
├── public/
├── .env.local.example
├── eslint.config.mjs
└── package.json
Tech: Next.js 16 (App Router), React 19, Tailwind CSS 4.
GitHub Actions runs on every push and pull request to main:
- Lint —
npm run lint - Build —
npm run build
Keep both passing before opening a PR.
See CONTRIBUTING.md for the full contributor workflow, branch naming convention, local checks, and accessibility expectations. Also see our Accessibility Statement.
- Fork the repo and clone your fork.
- Create a branch from
main:git checkout -b feature/your-featureorfix/your-fix. - Setup:
npm ci, optionallycp .env.local.example .env.local. - Make changes:
- Follow existing patterns under
app/. - Run
npm run lintandnpm run buildlocally.
- Follow existing patterns under
- Commit with clear messages (e.g.
feat: add X,fix: Y). - Push to your fork and open a Pull Request to
main. - Wait for CI and address review feedback.
We welcome UI improvements, new pages (e.g. invoice upload, marketplace), and Stellar wallet integration aligned with the LiquiFact product.
See COMPONENTS.md for the full component library reference — props, accessibility notes, and usage examples for every shared component (ErrorBanner, Footer, InvoiceListSkeleton, ToastProvider, UploadZone, WalletProvider, WalletStatus).
Wallet state is shared app-wide via WalletProvider, mounted in app/layout.js inside ToastProvider. Any client component can read connection state with useWallet():
import { useWallet } from '@/components/WalletProvider';
function FundInvoiceButton() {
const { state, walletData, connect, disconnect } = useWallet();
if (state !== 'connected') {
return <button type="button" onClick={() => connect()}>Connect wallet</button>;
}
return <span>Ready to fund as {walletData.address}</span>;
}Persistence: On successful connect, a minimal snapshot is saved to localStorage under liquifact-wallet-snapshot:
| Field | Persisted | Notes |
|---|---|---|
version |
Yes | Schema version (1) |
state |
Yes | Only connected is restored |
address |
Yes | Truncated display form only (e.g. GABC...XYZ123) |
network |
Yes | public or testnet |
balance |
No | Fetched live after real wallet integration |
| Private keys / secrets | Never | Rejected on read if detected |
The provider rehydrates from storage after mount (SSR-safe). disconnect() clears storage immediately. See WALLET_INTEGRATION_CONTRACT.md for the full integration contract.
components/NavMenu.jsx — Responsive site-wide header navigation used on every page.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
walletLabel |
string |
'Connect Wallet' |
Label text rendered inside the wallet button |
onWalletClick |
function |
undefined |
Callback fired when the wallet button is clicked |
Behaviour
- Desktop (≥
mdbreakpoint): Home, Invoices, and Invest links render inline in the header row alongside the wallet button. - Mobile (<
mdbreakpoint): Nav links are hidden behind a hamburger toggle (☰). Clicking the toggle reveals a dropdown menu below the header bar. - The active route is detected automatically via
usePathnameand marked witharia-current="page"on the matching link. - The menu closes on Escape (with focus returned to the toggle button), on any navigation event (pathname change), or when the toggle is clicked again.
- Passes
jest-axeaccessibility checks in both open and closed states. The toggle exposesaria-expandedandaria-controlsso assistive technologies can correctly announce the disclosure state.
Usage
import NavMenu from "@/components/NavMenu";
// Drop-in replacement for the static <header> on any page
export default function MyPage() {
return (
<div className="min-h-screen bg-slate-950 text-slate-100">
<NavMenu />
<main>...</main>
</div>
);
}
// With Stellar wallet integration
<NavMenu walletLabel="Freighter" onWalletClick={handleConnectWallet} />;-
Colors
--color-bg:#0f0f0f(slate‑950)--color-primary:#06b6d4(cyan‑400)
-
Typography
- Font family: Geist – imported via
@fontsource/geist. - Headings use
font‑bold, body usesfont‑regular.
- Font family: Geist – imported via
See TESTING.md for the full guide covering Jest unit/accessibility tests and Playwright end-to-end setup.
- Bounded health rendering — The home page displays the backend
/healthresponse through a bounded pipeline: recognised fields (status,message,version) are extracted and shown in a structured summary. The full payload is hidden behind a collapsible<details>element and stringified via a depth-limited (max 5 levels), length-truncated (max 2000 characters) formatter (lib/format/safeJson.js). This prevents DoS from giant or deeply nested attacker-controlled payloads.
MIT (see root LiquiFact project for full license).