pretty decent state
This commit is contained in:
parent
63a4b0f8a2
commit
f6c552cefd
10 changed files with 75 additions and 42 deletions
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
"use client";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Permission } from "../../auth-context";
|
||||
import { adminApi } from "../../api";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -348,24 +348,33 @@ export function ExchangeDetailsStep({
|
|||
<div style={styles.tradeSummary}>
|
||||
{direction === "buy" ? (
|
||||
<p style={styles.summaryText}>
|
||||
{t("detailsStep.summaryBuy").split("{sats}")[0].trim()}{" "}
|
||||
{t("detailsStep.summaryBuy", { sats: "", eur: "" }).split("{sats}")[0].trim()}{" "}
|
||||
<strong style={styles.satsValue}>
|
||||
<SatsDisplay sats={satsAmount} />
|
||||
</strong>
|
||||
{", "}
|
||||
{t("detailsStep.summaryBuy").split("{sats}")[1]?.split("{eur}")[0]?.trim()}{" "}
|
||||
{t("detailsStep.summaryBuy", { sats: "", eur: "" })
|
||||
.split("{sats}")[1]
|
||||
?.split("{eur}")[0]
|
||||
?.trim()}{" "}
|
||||
<strong>{formatEur(eurAmount)}</strong>
|
||||
</p>
|
||||
) : (
|
||||
<p style={styles.summaryText}>
|
||||
{t("detailsStep.summarySell").split("{sats}")[0]?.split("{eur}")[0]?.trim()}{" "}
|
||||
{t("detailsStep.summarySell", { sats: "", eur: "" })
|
||||
.split("{sats}")[0]
|
||||
?.split("{eur}")[0]
|
||||
?.trim()}{" "}
|
||||
<strong>{formatEur(eurAmount)}</strong>
|
||||
{", "}
|
||||
{t("detailsStep.summarySell").split("{sats}")[0]?.split("{eur}")[1]?.trim()}{" "}
|
||||
{t("detailsStep.summarySell", { sats: "", eur: "" })
|
||||
.split("{sats}")[0]
|
||||
?.split("{eur}")[1]
|
||||
?.trim()}{" "}
|
||||
<strong style={styles.satsValue}>
|
||||
<SatsDisplay sats={satsAmount} />
|
||||
</strong>
|
||||
{t("detailsStep.summarySell").split("{sats}")[1]?.trim()}
|
||||
{t("detailsStep.summarySell", { sats: "", eur: "" }).split("{sats}")[1]?.trim()}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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<T>(
|
|||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(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<T>(
|
|||
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();
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ export default function ProfilePage() {
|
|||
} finally {
|
||||
setIsLoadingProfile(false);
|
||||
}
|
||||
}, []);
|
||||
}, [t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (user && isAuthorized) {
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import {
|
|||
|
||||
export const authFormStyles: Record<string, CSSProperties> = {
|
||||
main: {
|
||||
...layoutStyles.main,
|
||||
...layoutStyles.contentCentered,
|
||||
minHeight: "100vh",
|
||||
padding: "1rem",
|
||||
},
|
||||
container: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue