The frontend for Soter, built with Next.js 15+, providing a modern, responsive interface for transparent humanitarian aid distribution on the Stellar blockchain.
This Next.js application serves as the user-facing interface for the Soter platform, enabling:
- Donor Dashboard: Create and manage aid campaigns
- Recipient Portal: Claim aid packages via wallet connection
- Live Maps: Visualize aid distribution using Leaflet
- AI Verification: Client-side need verification workflows
- Blockchain Integration: Connect with Stellar wallets (Freighter) and interact with Soroban smart contracts
- Framework: Next.js 16 (App Router)
- Language: TypeScript 5.9
- Styling: Tailwind CSS 4
- UI Components: Radix UI
- Data Fetching: React Query (TanStack Query)
- Mapping: Leaflet + React Leaflet
- Blockchain: Stellar SDK, Freighter Wallet API
- Linting: ESLint 9
src/
├── app/ # Next.js app router
│ ├── api/ # API routes (health check, etc.)
│ ├── layout.tsx # Root layout with providers
│ ├── page.tsx # Homepage
│ └── globals.css # Global styles
├── components/ # React components (to be added)
│ ├── ui/ # Radix UI components
│ └── features/ # Feature-specific components
├── lib/ # Utilities and providers
│ └── query-provider.tsx # React Query setup
├── hooks/ # Custom React hooks
├── types/ # TypeScript type definitions
└── config/ # Configuration files
- Node.js ≥ 18
- pnpm (recommended) or npm/yarn
- A Stellar wallet (e.g., Freighter extension)
From the monorepo root (app/):
pnpm installOr from this directory:
cd app/frontend
pnpm install- Copy the example environment file:
cp .env.example .env.local- Configure the variables in
.env.local:
# API Configuration
NEXT_PUBLIC_API_URL=http://localhost:4000
# Stellar Network (testnet, futurenet, or mainnet)
NEXT_PUBLIC_STELLAR_NETWORK=testnet
# Optional alias: NEXT_PUBLIC_NETWORK is used if NEXT_PUBLIC_STELLAR_NETWORK is not set
NEXT_PUBLIC_STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org
NEXT_PUBLIC_STELLAR_SOROBAN_RPC_URL=https://soroban-testnet.stellar.org
# Optional: application environment label (dev, staging, prod) — shown in the navbar
# NEXT_PUBLIC_ENV_NAME=dev
# Contract IDs (get these after deploying contracts)
NEXT_PUBLIC_AID_ESCROW_CONTRACT_ID=your_contract_id
NEXT_PUBLIC_VERIFICATION_CONTRACT_ID=your_contract_idThe navbar displays a small network & environment indicator (e.g. “Network: testnet”, “Environment: dev”) so contributors and testers always know which Stellar network and app environment they are using. These values come from NEXT_PUBLIC_STELLAR_NETWORK (or NEXT_PUBLIC_NETWORK) and optional NEXT_PUBLIC_ENV_NAME; they are safe to expose in production and contain no secrets.
Run the development server:
# From monorepo root
pnpm --filter frontend dev
# Or from this directory
pnpm devOpen http://localhost:3000 in your browser.
The app uses hot module replacement (HMR) - changes are reflected immediately.
Build for production:
pnpm buildTest the production build locally:
pnpm start| Script | Description |
|---|---|
dev |
Start development server on port 3000 |
build |
Create optimized production build |
start |
Run production server |
lint |
Run ESLint for code quality checks |
type-check |
Run TypeScript compiler without emitting files |
test |
Run test suite (placeholder for now) |
The frontend includes a health check endpoint for monitoring:
Endpoint: GET /api/health
Response:
{
"status": "ok",
"timestamp": "2026-01-19T00:00:00.000Z",
"service": "soter-frontend"
}Use this endpoint in CI/CD pipelines, monitoring tools, or health check probes.
To enable the mock API layer for development when the backend is unavailable:
- Set
NEXT_PUBLIC_USE_MOCKS=truein your.env.localfile. - The application will intercept requests to supported endpoints (e.g.,
/health,/aid-packages) and return mock data. - Mock handlers are defined in
src/lib/mock-api/handlers.ts.
When NEXT_PUBLIC_USE_MOCKS=true a Demo Mode banner is shown at the top of every page so contributors and testers always know they are not seeing live data.
The platform surfaces three distinct modes to make data provenance explicit:
| Mode | Trigger | What it means |
|---|---|---|
fixture |
NEXT_PUBLIC_USE_MOCKS=true or NEXT_PUBLIC_DEMO_MODE=true or AI service TEST_PROVIDER_MODE=true |
All AI responses come from local fixture files. No API keys required. |
deterministic |
AI service AI_DETERMINISTIC_MODE=true |
AI inference returns hardcoded stable outputs. Useful for CI. |
live |
All mock flags are off and a real API key is configured | Real AI provider is active. |
A coloured banner is rendered at the top of the app for fixture and deterministic modes.
The AI service also stamps every response with the X-Demo-Mode header (fixture, deterministic, or live) and exposes a /health/mode JSON endpoint for programmatic consumers.
Add to .env.local:
NEXT_PUBLIC_USE_MOCKS=true
# or, to force the banner independently of the mock layer:
# NEXT_PUBLIC_DEMO_MODE=trueData fetching is handled by React Query with configured defaults:
- Stale time: 60 seconds
- Refetch on window focus: disabled
Provider is located at src/lib/query-provider.tsx and wrapped in the root layout.
Pre-installed Radix primitives:
@radix-ui/react-dialog- Modal dialogs@radix-ui/react-dropdown-menu- Dropdown menus@radix-ui/react-toast- Toast notifications@radix-ui/react-avatar- User avatars@radix-ui/react-select- Select inputs@radix-ui/react-slot- Composition utility
Create custom components in src/components/ui/.
For mapping aid distributions:
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
// Use in components
<MapContainer center={[51.505, -0.09]} zoom={13}>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
<Marker position={[51.505, -0.09]}>
<Popup>Aid Distribution Point</Popup>
</Marker>
</MapContainer>;Note: Leaflet requires client-side rendering. Use dynamic imports with ssr: false for map components.
Connect with Freighter wallet (to be implemented):
import { isConnected, getPublicKey } from '@stellar/freighter-api';
// Check if wallet is available
const hasWallet = await isConnected();
// Get user's public key
const publicKey = await getPublicKey();- Use TypeScript for all new files
- Follow ESLint rules (run
pnpm lint) - Use functional components with hooks
- Prefer named exports for components
- Use Tailwind utility classes for styling
// Component template
interface MyComponentProps {
title: string;
onAction?: () => void;
}
export function MyComponent({ title, onAction }: MyComponentProps) {
return <div>{title}</div>;
}- Server state: React Query
- Client state: React hooks (useState, useReducer)
- Global state: Context API (if needed)
Use React Query hooks for data fetching:
import { useQuery } from '@tanstack/react-query';
function useCampaigns() {
return useQuery({
queryKey: ['campaigns'],
queryFn: async () => {
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/campaigns`);
return res.json();
},
});
}If port 3000 is occupied:
# Windows PowerShell
netstat -ano | findstr :3000
taskkill /PID <PID> /F
# Or use a different port
pnpm dev -- -p 3001Common with server/client mismatches. For client-only components:
import dynamic from 'next/dynamic';
const MapComponent = dynamic(() => import('./MapComponent'), { ssr: false });- Ensure variables start with
NEXT_PUBLIC_for client-side access - Restart dev server after changing
.env.local - Check that
.env.localis in the frontend root (notsrc/)
If you encounter Leaflet type issues:
pnpm add -D @types/leaflet# Clear Next.js cache
rm -rf .next
# Reinstall dependencies
rm -rf node_modules pnpm-lock.yaml
pnpm install
# Type check
pnpm type-check- Connect your GitHub repository to Vercel
- Set the root directory to
app/frontend - Add environment variables in the Vercel dashboard
- Deploy
# Or via CLI
cd app/frontend
vercel --prod(To be added based on project needs)
Tests will be added as the project matures. Planned testing stack:
- Unit: Jest + React Testing Library
- E2E: Playwright
- Integration: Testing against local backend
See CONTRIBUTING.md for development workflow, commit conventions, and PR guidelines.
- Root README - Project overview
- Backend README - API documentation
- Contracts README - Smart contract details
- Next.js Docs
- Stellar Docs
MIT - See LICENSE for details.
Built with ❤️ for transparent humanitarian aid 🌍