diff --git a/frontend/app/admin/availability/page.tsx b/frontend/app/admin/availability/page.tsx index aab1123..998eb23 100644 --- a/frontend/app/admin/availability/page.tsx +++ b/frontend/app/admin/availability/page.tsx @@ -1,5 +1,7 @@ "use client"; +export const dynamic = "force-dynamic"; + import { useEffect, useState, useCallback } from "react"; import { Permission } from "../../auth-context"; import { adminApi } from "../../api"; diff --git a/frontend/app/admin/invites/page.tsx b/frontend/app/admin/invites/page.tsx index 166f4f9..65a3eb7 100644 --- a/frontend/app/admin/invites/page.tsx +++ b/frontend/app/admin/invites/page.tsx @@ -1,5 +1,7 @@ "use client"; +export const dynamic = "force-dynamic"; + import { useEffect, useState, useCallback } from "react"; import { Permission } from "../../auth-context"; import { adminApi } from "../../api"; diff --git a/frontend/app/admin/price-history/page.tsx b/frontend/app/admin/price-history/page.tsx index 5521591..86e3f88 100644 --- a/frontend/app/admin/price-history/page.tsx +++ b/frontend/app/admin/price-history/page.tsx @@ -1,5 +1,7 @@ "use client"; +export const dynamic = "force-dynamic"; + import { useState } from "react"; import { Permission } from "../../auth-context"; import { adminApi } from "../../api"; diff --git a/frontend/app/admin/trades/page.tsx b/frontend/app/admin/trades/page.tsx index f6cf22f..f41ac57 100644 --- a/frontend/app/admin/trades/page.tsx +++ b/frontend/app/admin/trades/page.tsx @@ -1,5 +1,7 @@ "use client"; +export const dynamic = "force-dynamic"; + import { useEffect, useState, useCallback, CSSProperties } from "react"; import { Permission } from "../../auth-context"; import { adminApi } from "../../api"; diff --git a/frontend/app/components/LanguageSelector.tsx b/frontend/app/components/LanguageSelector.tsx index a904cca..d8d420e 100644 --- a/frontend/app/components/LanguageSelector.tsx +++ b/frontend/app/components/LanguageSelector.tsx @@ -56,10 +56,11 @@ export function LanguageSelector() { top: "100%", right: 0, marginTop: "0.5rem", - backgroundColor: "white", - border: "1px solid #e5e7eb", + backgroundColor: "rgba(255, 255, 255, 0.03)", + backdropFilter: "blur(10px)", + border: "1px solid rgba(255, 255, 255, 0.08)", borderRadius: "0.5rem", - boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)", + boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)", zIndex: 1000, minWidth: "150px", }} @@ -76,16 +77,18 @@ export function LanguageSelector() { padding: "0.75rem 1rem", textAlign: "left", border: "none", - backgroundColor: locale === lang.code ? "#f3f4f6" : "transparent", + backgroundColor: locale === lang.code ? "rgba(255, 255, 255, 0.05)" : "transparent", + color: "rgba(255, 255, 255, 0.7)", cursor: "pointer", display: "flex", alignItems: "center", gap: "0.5rem", fontSize: "0.875rem", + fontFamily: "'DM Sans', system-ui, sans-serif", }} onMouseEnter={(e) => { if (locale !== lang.code) { - e.currentTarget.style.backgroundColor = "#f9fafb"; + e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.05)"; } }} onMouseLeave={(e) => { diff --git a/frontend/app/exchange/components/ExchangeDetailsStep.tsx b/frontend/app/exchange/components/ExchangeDetailsStep.tsx index 0f2d7de..8659940 100644 --- a/frontend/app/exchange/components/ExchangeDetailsStep.tsx +++ b/frontend/app/exchange/components/ExchangeDetailsStep.tsx @@ -348,24 +348,33 @@ export function ExchangeDetailsStep({
{direction === "buy" ? (

- {t("detailsStep.summaryBuy").split("{sats}")[0].trim()}{" "} + {t("detailsStep.summaryBuy", { sats: "", eur: "" }).split("{sats}")[0].trim()}{" "} {", "} - {t("detailsStep.summaryBuy").split("{sats}")[1]?.split("{eur}")[0]?.trim()}{" "} + {t("detailsStep.summaryBuy", { sats: "", eur: "" }) + .split("{sats}")[1] + ?.split("{eur}")[0] + ?.trim()}{" "} {formatEur(eurAmount)}

) : (

- {t("detailsStep.summarySell").split("{sats}")[0]?.split("{eur}")[0]?.trim()}{" "} + {t("detailsStep.summarySell", { sats: "", eur: "" }) + .split("{sats}")[0] + ?.split("{eur}")[0] + ?.trim()}{" "} {formatEur(eurAmount)} {", "} - {t("detailsStep.summarySell").split("{sats}")[0]?.split("{eur}")[1]?.trim()}{" "} + {t("detailsStep.summarySell", { sats: "", eur: "" }) + .split("{sats}")[0] + ?.split("{eur}")[1] + ?.trim()}{" "} - {t("detailsStep.summarySell").split("{sats}")[1]?.trim()} + {t("detailsStep.summarySell", { sats: "", eur: "" }).split("{sats}")[1]?.trim()}

)}
diff --git a/frontend/app/hooks/useAsyncData.ts b/frontend/app/hooks/useAsyncData.ts index ecafce7..6c7e72b 100644 --- a/frontend/app/hooks/useAsyncData.ts +++ b/frontend/app/hooks/useAsyncData.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect, useCallback, useRef } from "react"; /** * Hook for fetching async data with loading and error states. @@ -30,6 +30,16 @@ export function useAsyncData( const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); + // Use ref to store the latest fetcher function to avoid dependency issues + const fetcherRef = useRef(fetcher); + const onErrorRef = useRef(onError); + + // Update refs when values change + useEffect(() => { + fetcherRef.current = fetcher; + onErrorRef.current = onError; + }, [fetcher, onError]); + const fetchData = useCallback(async () => { if (!enabled) return; @@ -37,20 +47,20 @@ export function useAsyncData( setError(null); try { - const result = await fetcher(); + const result = await fetcherRef.current(); setData(result); } catch (err) { const errorMessage = err instanceof Error ? err.message : "Failed to load data"; setError(errorMessage); - if (onError) { - onError(err); + if (onErrorRef.current) { + onErrorRef.current(err); } else { console.error("Failed to fetch data:", err); } } finally { setIsLoading(false); } - }, [fetcher, enabled, onError]); + }, [enabled]); useEffect(() => { fetchData(); diff --git a/frontend/app/profile/page.tsx b/frontend/app/profile/page.tsx index 296fb00..106216a 100644 --- a/frontend/app/profile/page.tsx +++ b/frontend/app/profile/page.tsx @@ -94,7 +94,7 @@ export default function ProfilePage() { } finally { setIsLoadingProfile(false); } - }, []); + }, [t]); useEffect(() => { if (user && isAuthorized) { diff --git a/frontend/app/signup/page.tsx b/frontend/app/signup/page.tsx index 829e6ce..5e14d28 100644 --- a/frontend/app/signup/page.tsx +++ b/frontend/app/signup/page.tsx @@ -35,33 +35,36 @@ function SignupContent() { } }, [user, router]); - const checkInvite = useCallback(async (code: string) => { - if (!code.trim()) { - setInviteValid(null); - setInviteError(""); - return; - } - - setIsCheckingInvite(true); - setInviteError(""); - - try { - const response = await invitesApi.checkInvite(code.trim()); - - if (response.valid) { - setInviteValid(true); + const checkInvite = useCallback( + async (code: string) => { + if (!code.trim()) { + setInviteValid(null); setInviteError(""); - } else { - setInviteValid(false); - setInviteError(response.error || t("signup.invalidInviteCode")); + return; } - } catch { - setInviteValid(false); - setInviteError(t("signup.failedToVerify")); - } finally { - setIsCheckingInvite(false); - } - }, []); + + setIsCheckingInvite(true); + setInviteError(""); + + try { + const response = await invitesApi.checkInvite(code.trim()); + + if (response.valid) { + setInviteValid(true); + setInviteError(""); + } else { + setInviteValid(false); + setInviteError(response.error || t("signup.invalidInviteCode")); + } + } catch { + setInviteValid(false); + setInviteError(t("signup.failedToVerify")); + } finally { + setIsCheckingInvite(false); + } + }, + [t] + ); // Check invite code on mount if provided in URL useEffect(() => { diff --git a/frontend/app/styles/auth-form.ts b/frontend/app/styles/auth-form.ts index efe6701..7bfe4ad 100644 --- a/frontend/app/styles/auth-form.ts +++ b/frontend/app/styles/auth-form.ts @@ -14,8 +14,8 @@ import { export const authFormStyles: Record = { main: { + ...layoutStyles.main, ...layoutStyles.contentCentered, - minHeight: "100vh", padding: "1rem", }, container: {