Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions messages/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"Common": {
"copyAddress": "نسخ {label}",
"copied": "تم النسخ",
"viewInvestment": "عرض استثمارك",
"viewOnExplorer": "عرض {label} على Stellar Expert",
"shareLink": "مشاركة الرابط"
},
Expand All @@ -21,12 +22,14 @@
"testnet": "شبكة الاختبار (نشطة)",
"networkStatus": "الشبكة: شبكة الاختبار نشطة",
"portfolio": "محفظتك",
"watchlist": "قائمة المراقبة",
"switchToDark": "التبديل إلى الوضع الداكن",
"switchToLight": "التبديل إلى الوضع الفاتح",
"language": "اللغة",
"account": "الحساب",
"copyAddress": "نسخ العنوان",
"copied": "تم النسخ",
"viewInvestment": "عرض استثمارك",
"viewOnExplorer": "عرض على Stellar Expert",
"shareLink": "مشاركة رابط الإحالة",
"disconnect": "قطع الاتصال",
Expand Down Expand Up @@ -110,9 +113,9 @@
"Explore": {
"title": "أين يعمل المجمع",
"sub": "كل مشروع يموله المجمع، مع درجات مؤكدة من Oracle. استكشف بحرية — توصيل المحفظة يأتي لاحقاً.",
"filterAll": "الكل",
"searchLabel": "ابحث في المشاريع",
"searchPlaceholder": "ابحث بالاسم أو الموقع",
"searchAriaLabel": "ابحث في المشاريع بالاسم أو الموقع",
"filterAll": "الكل",
"countSorted": "{count} مشاريع",
"cardFundedFromPool": "ممول من المجمع",
"cardVerifiedAgo": "تم التحقق قبل {ago}",
Expand All @@ -121,6 +124,21 @@
"loadMore": "عرض {count} المزيد",
"showingCount": "عرض {shown} من {total} مشاريع"
},
"Watchlist": {
"title": "قائمة المراقبة الخاصة بك",
"sub": "السندات التي تتابعها. حفظ سند لا يعني الاستثمار — بل يبقيه في متناول اليد ويشير إلى وقت فتحه للتمويل.",
"cardFundedFromPool": "ممول من المجمع",
"emptyTitle": "لا شيء محفوظ حتى الآن",
"emptySub": "افتح سنداً من استكشف وانقر على النجمة لإضافته هنا. سنشير هنا عندما يكون السند المحفوظ مفتوحاً للتمويل.",
"added": "تمت الإضافة إلى قائمة المراقبة",
"removed": "تمت الإزالة من قائمة المراقبة",
"saveLabel": "إضافة {name} إلى قائمة المراقبة",
"removeLabel": "إزالة {name} من قائمة المراقبة",
"availableBanner": "{count, plural, one {سند واحد متابع مفتوح للتمويل الآن.} other {# سندات متابعة مفتوحة للتمويل الآن.}}",
"statusOpen": "مفتوح للتمويل",
"statusUpcoming": "غير متاح بعد",
"notYetAvailable": "سنشير هنا بمجرد فتحه."
},
"Deposit": {
"stepAmount": "المبلغ",
"stepReview": "مراجعة",
Expand Down Expand Up @@ -152,6 +170,7 @@
"pendingTx": "tx a91f…3c0d · عرض على Stellar Expert ↗",
"successH1": "مرحباً بك في المجمع",
"successBody": "حصلت على <num>{shares} HBS</num> وتدعم الآن <b>{count} مشاريع مؤكدة</b>.",
"viewInvestment": "عرض استثمارك",
"viewExpert": "عرض على Stellar Expert",
"goPortfolio": "انتقل إلى المحفظة"
},
Expand All @@ -168,6 +187,11 @@
"impactBody": "حصتك تساعد في تمويل <b>{count} مشاريع مؤكدة</b> في 6 دول.",
"projectsBacked": "المشاريع المدعومة",
"weightedGreen": "الدرجة الخضراء المرجحة",
"riskScore": "درجة مخاطر المحفظة",
"riskTierConservative": "محافظ",
"riskTierModerate": "معتدل",
"riskTierAggressive": "عالي المخاطر",
"riskCaption": "بناءً على مزيج تصنيفات السندات في حيازاتك.",
"activityTitle": "النشاط",
"activityNote": "كل إدخال → Stellar Expert"
},
Expand Down
26 changes: 25 additions & 1 deletion messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"Common": {
"copyAddress": "Copy {label}",
"copied": "Copied",
"viewInvestment": "Ver tu inversión",
"viewOnExplorer": "View {label} on Stellar Expert",
"referralProgramShareLink": "Enlace para compartir el programa de referidos"
},
Expand All @@ -14,12 +15,14 @@
"testnet": "Testnet (active)",
"networkStatus": "Network: Testnet active",
"portfolio": "Your portfolio",
"watchlist": "Lista de seguimiento",
"switchToDark": "Switch to dark",
"switchToLight": "Switch to light",
"language": "Language",
"account": "Account",
"copyAddress": "Copy address",
"copied": "Copied",
"viewInvestment": "Ver tu inversión",
"viewOnExplorer": "View on Stellar Expert",
"disconnect": "Disconnect",
"disconnectConfirm": "Yes, disconnect",
Expand Down Expand Up @@ -103,8 +106,8 @@
"Explore": {
"title": "Where the pool works",
"sub": "Every project the pool funds, with oracle-verified scores. Explore freely — connecting a wallet comes later.",
"searchLabel": "Buscar proyectos",
"searchPlaceholder": "Search by name or location",
"searchAriaLabel": "Search projects",
"filterAll": "All",
"countSorted": "{count} projects",
"cardFundedFromPool": "Funded from the pool",
Expand All @@ -114,6 +117,21 @@
"loadMore": "Show {count} more",
"showingCount": "Showing {shown} of {total} projects"
},
"Watchlist": {
"title": "Tu lista de seguimiento",
"sub": "Los bonos que sigues. Guardar uno no invierte — lo mantiene a mano y te avisa cuando se abre a financiación.",
"cardFundedFromPool": "Financiado por el fondo",
"emptyTitle": "Nada guardado por ahora",
"emptySub": "Abre un bono desde Explorar y toca la estrella para añadirlo aquí. Te avisaremos cuando un bono guardado esté abierto a financiación.",
"added": "Añadido a la lista de seguimiento",
"removed": "Eliminado de la lista de seguimiento",
"saveLabel": "Añadir {name} a la lista de seguimiento",
"removeLabel": "Eliminar {name} de la lista de seguimiento",
"availableBanner": "{count, plural, one {# bono seguido está abierto a financiación ahora mismo.} other {# bonos seguidos están abiertos a financiación ahora mismo.}}",
"statusOpen": "Abierto a financiación",
"statusUpcoming": "Aún no disponible",
"notYetAvailable": "Te avisaremos aquí en cuanto se abra."
},
"Deposit": {
"stepAmount": "Amount",
"stepReview": "Review",
Expand Down Expand Up @@ -145,6 +163,7 @@
"pendingTx": "tx a91f…3c0d · view on Stellar Expert ↗",
"successH1": "Welcome to the pool",
"successBody": "You received <num>{shares} HBS</num> and now back <b>{count} verified projects</b>.",
"viewInvestment": "Ver tu inversión",
"viewExpert": "View on Stellar Expert",
"goPortfolio": "Go to portfolio"
},
Expand All @@ -161,6 +180,11 @@
"impactBody": "Your stake helps fund <b>{count} verified projects</b> across 6 countries.",
"projectsBacked": "Projects backed",
"weightedGreen": "Weighted green score",
"riskScore": "Puntuación de riesgo de la cartera",
"riskTierConservative": "Conservador",
"riskTierModerate": "Moderado",
"riskTierAggressive": "Agresivo",
"riskCaption": "Basado en la combinación de calificaciones de bonos en sus tenencias.",
"activityTitle": "Activity",
"activityNote": "every entry → Stellar Expert"
},
Expand Down
14 changes: 9 additions & 5 deletions messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"Common": {
"copyAddress": "Copier {label}",
"copied": "Copié",
"viewInvestment": "Voir votre investissement",
"referralShareLink": "Lien de parrainage",
"viewOnExplorer": "Voir {label} sur Stellar Expert"
},
Expand All @@ -12,7 +13,7 @@
"creator": "Créateurs",
"connect": "Se connecter",
"testnet": "Testnet (actif)",
"networkStatus": "Réseau : Testnet actif",
"networkStatus": "Réseau : Testnet actif",
"portfolio": "Votre portefeuille",
"switchToDark": "Passer en sombre",
"switchToLight": "Passer en clair",
Expand All @@ -21,6 +22,7 @@
"watchlist": "Liste de suivi",
"copyAddress": "Copier l'adresse",
"copied": "Copié",
"viewInvestment": "Voir votre investissement",
"referralShareLink": "Lien de parrainage",
"viewOnExplorer": "Voir sur Stellar Expert",
"disconnect": "Se déconnecter",
Expand Down Expand Up @@ -161,6 +163,7 @@
"pendingTx": "tx a91f…3c0d · voir sur Stellar Expert ↗",
"successH1": "Bienvenue dans le pool",
"successBody": "Vous avez reçu <num>{shares} HBS</num> et soutenez désormais <b>{count} projets vérifiés</b>.",
"viewInvestment": "Voir votre investissement",
"viewExpert": "Voir sur Stellar Expert",
"goPortfolio": "Aller au portefeuille"
},
Expand All @@ -177,6 +180,11 @@
"impactBody": "Votre participation aide à financer <b>{count} projets vérifiés</b> dans 6 pays.",
"projectsBacked": "Projets soutenus",
"weightedGreen": "Score vert pondéré",
"riskScore": "Score de risque du portefeuille",
"riskTierConservative": "Conservateur",
"riskTierModerate": "Modéré",
"riskTierAggressive": "Agressif",
"riskCaption": "Basé sur la répartition des notations d'obligations dans vos avoirs.",
"activityTitle": "Activité",
"activityNote": "chaque entrée → Stellar Expert"
},
Expand Down Expand Up @@ -223,10 +231,6 @@
"rejectionReason": "Raison",
"actionRevoke": "Révoquer",
"revokeConfirm": "Oui, révoquer",
"revokeConfirmBody": "Cette action révoque l'accès du créateur au registre. Voulez-vous continuer ?",
"revokeCancel": "Annuler",
"revokeToast": "Créateur révoqué",
"revokeUndo": "Annuler"voquer",
"revokeConfirmBody": "La révocation retirera immédiatement l'accès de ce créateur. Vous pourrez annuler cette action.",
"revokeCancel": "Annuler",
"revokeToast": "Créateur révoqué",
Expand Down
25 changes: 18 additions & 7 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,25 @@ import createNextIntlPlugin from 'next-intl/plugin'

const nextConfig: NextConfig = {
reactStrictMode: true,
// Turbopack (dev) handles code-splitting automatically — no webpack override needed.
// The three/react-three chunk is naturally split by Turbopack's dependency graph.
//
// If you ever need to opt specific packages into a custom Turbopack rule, use:
// experimental: { turbo: { rules: { ... } } }
// Reference: https://nextjs.org/docs/app/api-reference/next-config-js/turbopack
webpack(config) {
const splitChunks = config.optimization.splitChunks || {}
config.optimization.splitChunks = {
...splitChunks,
cacheGroups: {
...(splitChunks.cacheGroups || {}),
three: {
test: /[\\/]node_modules[\\/](?:three|@react-three\/fiber)(?:[\\/]|$)/,
name: 'three',
chunks: 'async',
priority: 40,
enforce: true,
},
},
}
return config
},
}

const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts')

export default withNextIntl(nextConfig)
export default withNextIntl(nextConfig)
2 changes: 2 additions & 0 deletions src/__tests__/catalog-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest'
import en from '../../messages/en.json'
import fr from '../../messages/fr.json'
import es from '../../messages/es.json'
import ar from '../../messages/ar.json'

function getLeafKeys(obj: Record<string, unknown>, prefix = ''): string[] {
const keys: string[] = []
Expand All @@ -21,6 +22,7 @@ describe('Message catalog parity', () => {
it.each([
['fr.json', fr],
['es.json', es],
['ar.json', ar],
] as const)('en.json and %s have identical key sets', (_name, catalog) => {
const enKeys = getLeafKeys(en).sort()
const catalogKeys = getLeafKeys(catalog).sort()
Expand Down
6 changes: 3 additions & 3 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { type Locale, RTL_LOCALES } from '../i18n/request'
import { THEME_SCRIPT } from '../theme/themeScript'
import '../styles/index.css'

const TopBar = dynamic(() => import('../shell/TopBar'))
const Footer = dynamic(() => import('../shell/Footer'))
const TopBar = dynamic(() => import('../shell/TopBar').then((m) => m.TopBar))
const Footer = dynamic(() => import('../shell/Footer').then((m) => m.Footer))

export const metadata: Metadata = {
title: 'Heliobond — sunlight made financial',
Expand Down Expand Up @@ -66,7 +66,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
suppressHydrationWarning
>
<head>
<script dangerouslySetInnerHtml={{ __html: THEME_SCRIPT }} />
<script dangerouslySetInnerHTML={{ __html: THEME_SCRIPT }} />
{/*
Warm the font CDN connections. The webfont stylesheets sit behind a CSS
`@import` chain (`src/styles/index.css` → `tokens/fonts.css`), so the
Expand Down
11 changes: 8 additions & 3 deletions src/app/portfolio/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
'use client'

import { useRouter } from 'next/navigation'
import { Portfolio } from '../../screens/Portfolio'
import { RequireWallet } from '../../wallet/RequireWallet'
import { usePortfolioRisk } from '../../hooks/usePortfolioRisk'

function PortfolioRoute() {
const riskScore = usePortfolioRisk()
return <Portfolio riskScore={riskScore} />
const router = useRouter()
return (
<Portfolio
onWithdraw={() => router.push('/withdraw')}
onDeposit={() => router.push('/deposit')}
/>
)
}

export default function PortfolioPage() {
Expand Down
2 changes: 2 additions & 0 deletions src/app/project/[id]/page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ describe('ProjectDetailPage', () => {
fundingGoal: 200000,
},
detail: {
name: 'Test Project',
location: 'Test Location',
heroGradient: 'linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)',
creator: { name: 'Test Creator', verified: true, since: '2025' },
story: 'Test story',
Expand Down
16 changes: 8 additions & 8 deletions src/components/AmountInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function AmountInput({

const liveMsg = overCap && !wasOverCap ? (capMessage ?? '') : ''

const set = (v: number) => onChange?(String(v))
const set = (v: number) => onChange?.(String(v))

return (
<div style={{ position: 'relative', ...style }}>
Expand Down Expand Up @@ -99,7 +99,7 @@ export function AmountInput({
alignItems: 'center',
gap: 10,
background: 'var(--surface)',
border: `p1px solid ${overCap ? 'var(--solar)' : 'var(--ink-12)'}`,
border: `1px solid ${overCap ? 'var(--solar)' : 'var(--ink-12)'}`,
borderRadius: 'var(--radius-input)',
padding: '0 16px',
height: 64,
Expand All @@ -108,7 +108,7 @@ export function AmountInput({
>
<input
id={id}
type="number"
type="text"
inputMode="decimal"
placeholder="0.00"
value={value}
Expand All @@ -117,12 +117,12 @@ export function AmountInput({
e.preventDefault()
const pastedText = e.clipboardData.getData('text')
const sanitized = sanitizeAmount(pastedText)
onChange?(sanitized)
onChange?.(sanitized)
}}
onChange={(e) => {
const v = sanitizeAmount(e.target.value)
// If editing existing value, typing should replace not append when field was pre-filled
onChange?(v)
onChange?.(v)
}}
style={{
flex: 1,
Expand All @@ -134,7 +134,7 @@ export function AmountInput({
fontWeight: 600,
fontSize: 'var(--type-data-display)',
color: 'var(--ink)',
fontFeatureSettings: '"tnum' 1',
fontFeatureSettings: '"tnum" 1',
}}
/>
<span
Expand All @@ -157,14 +157,14 @@ export function AmountInput({
margin: '8px 0 0',
}}
>
Min 1 USDC — Max {cap ?? ‘⁊7} USDC
Min 1 USDC — Max {cap ?? '—'} USDC
</p>
<div style={{ display: 'flex', gap: 8, marginTop: 10 }}>
{chips.map((c) => (
<button key={c} type="button" onClick={() => set(c)} style={chipStyle}>
{c}
</button>
)}
))}
{cap != null && maxChipLabel && (
<button
key="max"
Expand Down
2 changes: 1 addition & 1 deletion src/components/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function Card({ children, style, className, ...props }: CardProps) {
<div
style={{
background: 'var(--surface)',
border: '1'px solid var(--ink-12)',
border: '1px solid var(--ink-12)',
borderRadius: 'var(--radius-card)',
padding: 24,
boxShadow: 'var(--shadow-sm)',
Expand Down
Loading
Loading