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
15 changes: 9 additions & 6 deletions apps/web/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
TransactionProvider,
} from '@bridgewise/ui-components';
import { OfflineBanner } from '../components/OfflineBanner';
import { I18nProvider } from '../components/I18nProvider';
import './globals.css';

const customTheme = {
Expand Down Expand Up @@ -47,12 +48,14 @@ export default function RootLayout({
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<BridgeWiseProvider theme={customTheme} defaultMode="system">
<TransactionProvider>
<OfflineBanner />
{children}
</TransactionProvider>
</BridgeWiseProvider>
<I18nProvider>
<BridgeWiseProvider theme={customTheme} defaultMode="system">
<TransactionProvider>
<OfflineBanner />
{children}
</TransactionProvider>
</BridgeWiseProvider>
</I18nProvider>
</body>
</html>
);
Expand Down
34 changes: 18 additions & 16 deletions apps/web/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
'use client';

import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import {
BridgeWiseProvider,
TransactionHeartbeat,
useTransaction,
BridgeStatus,
} from '@bridgewise/ui-components';
import VersionDisplay from '../components/VersionDisplay';
import { LanguageSwitcher } from '../components/LanguageSwitcher';

const customTheme = {
primaryColor: '#22c55e',
Expand Down Expand Up @@ -37,52 +39,53 @@ function TransactionDemo() {
let nextProgress = state.progress + 5;
let nextStep = state.step;

if (nextProgress > 20 && nextProgress < 40) nextStep = 'Confirming on source chain...';
if (nextProgress > 50 && nextProgress < 70) nextStep = 'Bridging assets...';
if (nextProgress > 80) nextStep = 'Finalizing on destination...';
if (nextProgress > 20 && nextProgress < 40) nextStep = t('app.statusConfirming');
if (nextProgress > 50 && nextProgress < 70) nextStep = t('app.statusBridging');
if (nextProgress > 80) nextStep = t('app.statusFinalizing');

updateState({ progress: Math.min(nextProgress, 100), step: nextStep });
}, 800);

return () => clearInterval(interval);
}, [state, updateState]);
}, [state, updateState, t]);

const { t } = useTranslation();

return (
<BridgeWiseProvider theme={customTheme} defaultMode="dark">
<div className="flex min-h-screen flex-col items-center justify-center gap-12 p-10 bg-zinc-50 dark:bg-black">
<main className="flex flex-col items-center gap-8 max-w-3xl">
<LanguageSwitcher />
<h1 className="text-4xl font-bold text-center text-zinc-900 dark:text-zinc-100">
BridgeWise Theming Demo
{t('app.title')}
</h1>

<p className="max-w-xl text-center text-zinc-600 dark:text-zinc-400">
This page demonstrates the BridgeWise theme system. The heartbeat and status components
are styled via CSS variables injected by <code>BridgeWiseProvider</code>, with a custom
primary color and dark background.
{t('app.description')}
</p>

<div className="flex flex-wrap items-center justify-center gap-4">
<button
onClick={() => startTransaction('tx-' + Date.now())}
className="px-6 py-3 rounded-lg text-sm font-medium text-white bg-emerald-500 hover:bg-emerald-600 active:scale-95 transition"
>
Start Transaction
{t('app.startTransaction')}
</button>
<button
onClick={clearState}
className="px-6 py-3 rounded-lg text-sm font-medium border border-zinc-300 dark:border-zinc-700 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 active:scale-95 transition"
>
Clear State
{t('app.clearState')}
</button>
</div>

<section className="grid w-full gap-6 md:grid-cols-2">
<div className="rounded-2xl border border-zinc-200/60 dark:border-zinc-800/80 bg-white/60 dark:bg-zinc-900/60 p-4 shadow-sm">
<h2 className="text-lg font-semibold mb-2 text-zinc-900 dark:text-zinc-50">
Inline BridgeStatus
{t('app.inlineStatusTitle')}
</h2>
<p className="text-sm text-zinc-600 dark:text-zinc-400 mb-4">
An inline status card using the same theme variables as the floating heartbeat.
{t('app.inlineStatusText')}
</p>
<BridgeStatus
txHash={state.txHash || '0x0000000000000000000000000000000000000000'}
Expand All @@ -99,14 +102,13 @@ function TransactionDemo() {

<div className="rounded-2xl border border-zinc-200/60 dark:border-zinc-800/80 bg-white/60 dark:bg-zinc-900/60 p-4 shadow-sm">
<h2 className="text-lg font-semibold mb-2 text-zinc-900 dark:text-zinc-50">
Component-level Overrides
{t('app.componentOverridesTitle')}
</h2>
<p className="text-sm text-zinc-600 dark:text-zinc-400 mb-4">
The floating heartbeat below uses a custom <code>className</code> to adjust its
position while still inheriting all theme variables.
{t('app.componentOverridesText')}
</p>
<p className="text-xs text-zinc-500 dark:text-zinc-500">
Trigger a transaction and you&apos;ll see the heartbeat appear in the bottom-left corner.
{t('app.componentOverridesHint')}
</p>
</div>
</section>
Expand Down
16 changes: 16 additions & 0 deletions apps/web/components/I18nProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use client';

import { ReactNode, useEffect } from 'react';
import { I18nextProvider } from 'react-i18next';
import i18n from '../i18n';

export function I18nProvider({ children }: { children: ReactNode }) {
useEffect(() => {
const storedLanguage = window.localStorage.getItem('bridgewise-language');
if (storedLanguage && storedLanguage !== i18n.language) {
i18n.changeLanguage(storedLanguage);
}
}, []);

return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
}
50 changes: 50 additions & 0 deletions apps/web/components/LanguageSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client';

import { ChangeEvent, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';

const AVAILABLE_LANGUAGES = [
{ code: 'en', label: 'English' },
{ code: 'fr', label: 'Français' },
];

export function LanguageSwitcher() {
const { i18n, t } = useTranslation();
const [loaded, setLoaded] = useState(false);

useEffect(() => {
const stored = window.localStorage.getItem('bridgewise-language');
if (stored && stored !== i18n.language) {
i18n.changeLanguage(stored);
}
setLoaded(true);
}, [i18n]);

const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
const nextLng = event.target.value;
i18n.changeLanguage(nextLng);
window.localStorage.setItem('bridgewise-language', nextLng);
};

if (!loaded) return null;

return (
<div className="mb-6 flex items-center gap-2">
<label htmlFor="bridgewise-language-selector" className="font-medium text-zinc-800 dark:text-zinc-200">
{t('app.language')}:
</label>
<select
id="bridgewise-language-selector"
value={i18n.language}
onChange={handleChange}
className="rounded border border-zinc-300 bg-white px-2 py-1 text-sm dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100"
>
{AVAILABLE_LANGUAGES.map((lang) => (
<option key={lang.code} value={lang.code}>
{lang.label}
</option>
))}
</select>
</div>
);
}
7 changes: 5 additions & 2 deletions apps/web/components/OfflineBanner.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
'use client';

import React from 'react';
import { useTranslation } from 'react-i18next';
import { useOfflineDetection } from '../hooks/useOfflineDetection';

export function OfflineBanner() {
const { t } = useTranslation();
const { isOffline, cache } = useOfflineDetection();

if (!isOffline) return null;
Expand Down Expand Up @@ -33,8 +35,9 @@ export function OfflineBanner() {
/>
</svg>
<span>
You are offline. Showing{' '}
{cachedAt ? `cached data from ${cachedAt}` : 'limited functionality'}.
{t('app.offline', {
cached: cachedAt ? t('app.offlineCached', { when: cachedAt }) : t('app.offlineLimited'),
})}
</span>
</div>
);
Expand Down
4 changes: 3 additions & 1 deletion apps/web/components/VersionDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import React from 'react';
import { useTranslation } from 'react-i18next';
import { useVersion, VersionData } from '../hooks/useVersion';

export interface VersionDisplayProps {
Expand Down Expand Up @@ -29,6 +30,7 @@ export const VersionDisplay: React.FC<VersionDisplayProps> = ({
apiUrl,
onClick,
}) => {
const { t } = useTranslation();
const { version, loading, error } = useVersion({
apiUrl,
enableLogging,
Expand Down Expand Up @@ -64,7 +66,7 @@ export const VersionDisplay: React.FC<VersionDisplayProps> = ({
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
Loading...
{t('app.loading')}
</div>
);
}
Expand Down
37 changes: 37 additions & 0 deletions apps/web/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

import en from './locales/en.json';
import fr from './locales/fr.json';

const resources = {
en: {
translation: en,
},
fr: {
translation: fr,
},
};

if (!i18n.isInitialized) {
i18n
.use(initReactI18next)
.init({
resources,
lng: 'en',
fallbackLng: 'en',
supportedLngs: ['en', 'fr'],
interpolation: {
escapeValue: false,
},
react: {
useSuspense: false,
},
})
.catch((error) => {
// eslint-disable-next-line no-console
console.error('i18next init failed', error);
});
}

export default i18n;
21 changes: 21 additions & 0 deletions apps/web/locales/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"app": {
"title": "BridgeWise Theming Demo",
"description": "This page demonstrates the BridgeWise theme system. The heartbeat and status components are styled via CSS variables injected by BridgeWiseProvider, with a custom primary color and dark background.",
"startTransaction": "Start Transaction",
"clearState": "Clear State",
"inlineStatusTitle": "Inline BridgeStatus",
"inlineStatusText": "An inline status card using the same theme variables as the floating heartbeat.",
"componentOverridesTitle": "Component-level Overrides",
"componentOverridesText": "The floating heartbeat below uses a custom className to adjust its position while still inheriting all theme variables.",
"componentOverridesHint": "Trigger a transaction and you'll see the heartbeat appear in the bottom-left corner.",
"statusConfirming": "Confirming on source chain...",
"statusBridging": "Bridging assets...",
"statusFinalizing": "Finalizing on destination...",
"offline": "You are offline. Showing {{cached}}.",
"offlineCached": "cached data from {{when}}",
"offlineLimited": "limited functionality",
"language": "Language",
"loading": "Loading..."
}
}
21 changes: 21 additions & 0 deletions apps/web/locales/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"app": {
"title": "Démo de thème BridgeWise",
"description": "Cette page démontre le système de thème BridgeWise. Les composants heartbeat et status sont stylés via des variables CSS injectées par BridgeWiseProvider, avec une couleur principale personnalisée et un fond sombre.",
"startTransaction": "Démarrer la transaction",
"clearState": "Réinitialiser l'état",
"inlineStatusTitle": "BridgeStatus en ligne",
"inlineStatusText": "Une carte d'état en ligne utilisant les mêmes variables de thème que le heartbeat flottant.",
"componentOverridesTitle": "Remplacements au niveau du composant",
"componentOverridesText": "Le heartbeat flottant ci-dessous utilise un className personnalisé pour ajuster sa position tout en héritant de toutes les variables de thème.",
"componentOverridesHint": "Déclenchez une transaction et vous verrez le heartbeat apparaître en bas à gauche.",
"statusConfirming": "Confirmation sur la chaîne source...",
"statusBridging": "Transfert d'actifs...",
"statusFinalizing": "Finalisation sur la chaîne de destination...",
"offline": "Vous êtes hors ligne. Affichage de {{cached}}.",
"offlineCached": "données mises en cache depuis {{when}}",
"offlineLimited": "fonctionnalité limitée",
"language": "Langue",
"loading": "Chargement..."
}
}
Loading