diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index a72a5b0..be65f82 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -19,6 +19,7 @@ import { QueryProvider } from "@/providers/QueryProvider";
import { DevToolsSwitcher } from "@/components/ui/DevToolsSwitcher";
import { OnboardingProvider } from "@/providers/OnboardingProvider";
import OnboardingTour from "@/components/layout/OnboardingTour";
+import { CurrencyProvider } from "@/hooks/useCurrency";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -136,22 +137,24 @@ export default function RootLayout({
disableTransitionOnChange
>
-
-
-
-
- }
- footerSlot={}
- >
- {children}
-
-
-
-
-
-
-
+
+
+
+
+
+ }
+ footerSlot={}
+ >
+ {children}
+
+
+
+
+
+
+
+
{/* Register service worker after page is interactive */}
diff --git a/src/components/home/layout/navbar.tsx b/src/components/home/layout/navbar.tsx
index d6be2a3..1530ba3 100644
--- a/src/components/home/layout/navbar.tsx
+++ b/src/components/home/layout/navbar.tsx
@@ -7,6 +7,9 @@ import { usePathname } from "next/navigation";
import { ThemeToggle } from "@/components/ui/theme-toggle";
import CreateWalletModal from "@/components/CreateWalletModal";
import { useWallet } from "@/providers/WalletProvider";
+import { useCurrency, type DisplayCurrency } from "@/hooks/useCurrency";
+
+const DISPLAY_CURRENCIES: DisplayCurrency[] = ["XLM", "USD", "EUR", "GBP", "JPY"];
function shortenAddress(address: string): string {
return `${address.slice(0, 4)}…${address.slice(-4)}`;
@@ -18,6 +21,7 @@ export default function NavBar() {
const [copied, setCopied] = useState(false);
const pathname = usePathname() ?? "";
const { address, balance, network, isLoading, isWrongNetwork, disconnect } = useWallet();
+ const { selectedCurrency, setSelectedCurrency, convert } = useCurrency();
const authRoutes = ["/login", "/sign-in", "/sign-up"];
if (authRoutes.includes(pathname)) return null;
@@ -115,6 +119,17 @@ export default function NavBar() {
+
setSelectedCurrency(event.target.value as DisplayCurrency)}
+ className="rounded-lg border border-border bg-card/40 px-2 py-1.5 text-xs font-medium text-foreground transition-colors hover:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500 cursor-pointer"
+ aria-label="Select display currency"
+ >
+ {DISPLAY_CURRENCIES.map((currency) => (
+ {currency}
+ ))}
+
+
{isLandingPage ? (
<>
@@ -138,7 +153,7 @@ export default function NavBar() {
{balance && (
- {balance} XLM
+ {convert(Number(balance))}
)}
+
+ Currency
+ setSelectedCurrency(event.target.value as DisplayCurrency)}
+ className="rounded-lg border border-border bg-card px-2 py-1 text-xs"
+ aria-label="Select display currency"
+ >
+ {DISPLAY_CURRENCIES.map((currency) => (
+ {currency}
+ ))}
+
+
+
{address ? (
("deposit");
const [isProcessing, setIsProcessing] = useState(false);
const [reservationId, setReservationId] = useState(null);
+ const { convert, selectedCurrency } = useCurrency();
+ const slotFeeXlm = Number.parseFloat(slotFee);
+ const displaySlotFee = Number.isFinite(slotFeeXlm) ? convert(slotFeeXlm) : `${slotFee} XLM`;
const handleDeposit = async () => {
setIsProcessing(true);
@@ -61,7 +65,12 @@ export default function BookSessionModal({
Slot Reservation Fee
-
{slotFee} XLM
+
+
{displaySlotFee}
+ {selectedCurrency !== "XLM" && (
+
{slotFee} XLM
+ )}
+
@@ -122,7 +131,7 @@ export default function BookSessionModal({
Session Reserved!
Your appointment with {expertName} has been secured.
- The slot fee of {slotFee} XLM will be applied to your final session invoice.
+ The slot fee of {displaySlotFee} {selectedCurrency !== "XLM" ? ` (${slotFee} XLM)` : ""} will be applied to your final session invoice.
diff --git a/src/components/marketplace/ExpertCard.tsx b/src/components/marketplace/ExpertCard.tsx
index 678cf77..e482d9b 100644
--- a/src/components/marketplace/ExpertCard.tsx
+++ b/src/components/marketplace/ExpertCard.tsx
@@ -16,7 +16,7 @@ interface ExpertCardProps {
export default function ExpertCard({ expert }: ExpertCardProps) {
const queryClient = useQueryClient();
const { multiplier, isSurgeActive } = useSurgeMultiplier();
- const { rates } = useCurrency();
+ const { convert, selectedCurrency } = useCurrency();
const handleMouseEnter = () => {
prefetchExpert(queryClient, expert.id);
@@ -25,9 +25,8 @@ export default function ExpertCard({ expert }: ExpertCardProps) {
// Calculate surge percentage for display
const surgePercent = Math.round((multiplier - 1) * 100);
- // Calculate hourly and per-minute USD rates
- const hourlyRateUSD = rates ? expert.hourlyRate * rates.usd : null;
- const minuteRateUSD = hourlyRateUSD ? hourlyRateUSD / 60 : null;
+ const displayHourlyRate = convert(expert.hourlyRate);
+ const displayMinuteRate = convert(expert.hourlyRate / 60);
return (
@@ -74,11 +73,11 @@ export default function ExpertCard({ expert }: ExpertCardProps) {
className="font-bold text-lg text-purple-400 cursor-help"
title="Rate may increase during high demand periods"
>
- {expert.hourlyRate} XLM/hr
+ {displayHourlyRate} / hr
- {minuteRateUSD && (
+ {selectedCurrency !== 'XLM' && (
- (${minuteRateUSD.toFixed(2)} / min)
+ {expert.hourlyRate.toFixed(4)} XLM/hr ({displayMinuteRate} / min)
)}
{isSurgeActive && (
diff --git a/src/components/profile/ExpertDetails.tsx b/src/components/profile/ExpertDetails.tsx
index 654b341..8b567f3 100644
--- a/src/components/profile/ExpertDetails.tsx
+++ b/src/components/profile/ExpertDetails.tsx
@@ -140,10 +140,7 @@ interface ExpertDetailsProps {
}
export default function ExpertDetails({ expert, onBookClick }: ExpertDetailsProps) {
- const { rates } = useCurrency();
-
- // Calculate USD rates
- const hourlyRateUSD = rates ? expert.hourlyRate * rates.usd : null;
+ const { convert, selectedCurrency } = useCurrency();
return (
@@ -209,10 +206,10 @@ export default function ExpertDetails({ expert, onBookClick }: ExpertDetailsProp
Total Sessions
-
{expert.hourlyRate} XLM/hr
-
- {hourlyRateUSD ? `($${hourlyRateUSD.toFixed(2)} / hr)` : 'Hourly Rate'}
-
+
{convert(expert.hourlyRate)} / hr
+ {selectedCurrency !== "XLM" && (
+
{expert.hourlyRate.toFixed(4)} XLM/hr
+ )}
{expert.reviews}
diff --git a/src/hooks/useCurrency.ts b/src/hooks/useCurrency.ts
index 4be0a19..bc8f0f8 100644
--- a/src/hooks/useCurrency.ts
+++ b/src/hooks/useCurrency.ts
@@ -1,6 +1,6 @@
"use client";
-import { useState, useEffect, useCallback } from "react";
+import { createContext, createElement, useState, useEffect, useCallback, useContext } from "react";
// ─── Types ────────────────────────────────────────────────────────────────────
@@ -21,6 +21,8 @@ export interface UseCurrencyReturn {
isLoading: boolean;
}
+const CurrencyContext = createContext
(null);
+
// ─── Constants ────────────────────────────────────────────────────────────────
const COINGECKO_URL =
@@ -46,7 +48,7 @@ const CURRENCY_DECIMALS: Record = {
// ─── Hook ─────────────────────────────────────────────────────────────────────
-export function useCurrency(): UseCurrencyReturn {
+function useCurrencyState(): UseCurrencyReturn {
const [selectedCurrency, setSelectedCurrency] =
useState("USD");
const [rates, setRates] = useState(null);
@@ -103,3 +105,19 @@ export function useCurrency(): UseCurrencyReturn {
return { selectedCurrency, setSelectedCurrency, rates, convert, isLoading };
}
+
+export function CurrencyProvider({ children }: { children: React.ReactNode }) {
+ const value = useCurrencyState();
+
+ return createElement(CurrencyContext.Provider, { value }, children);
+}
+
+export function useCurrency(): UseCurrencyReturn {
+ const context = useContext(CurrencyContext);
+
+ if (!context) {
+ throw new Error("useCurrency must be used within a CurrencyProvider");
+ }
+
+ return context;
+}