From a1c2a785a1e69f320d2ef6ce44b575f597bb851b Mon Sep 17 00:00:00 2001 From: 9qeklajc Date: Mon, 23 Mar 2026 22:36:16 +0100 Subject: [PATCH] better displaying errors --- ui/components/child-key-creator.tsx | 4 ++- ui/components/landing/key-info-details.tsx | 34 ++++++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/ui/components/child-key-creator.tsx b/ui/components/child-key-creator.tsx index d82added..4db00e2d 100644 --- a/ui/components/child-key-creator.tsx +++ b/ui/components/child-key-creator.tsx @@ -159,7 +159,9 @@ export function ChildKeyCreator({ error instanceof Error ? error.message : 'Failed to create child key'; try { const parsed = JSON.parse(errorMessage); - errorMessage = parsed.detail || parsed.message || errorMessage; + errorMessage = + parsed.detail?.error?.message || + (typeof parsed.detail === 'string' ? parsed.detail : errorMessage); } catch {} setError(errorMessage); toast.error(errorMessage); diff --git a/ui/components/landing/key-info-details.tsx b/ui/components/landing/key-info-details.tsx index dbadeb1c..22be49b0 100644 --- a/ui/components/landing/key-info-details.tsx +++ b/ui/components/landing/key-info-details.tsx @@ -5,6 +5,7 @@ import { Copy, RefreshCcw, RotateCcw, Trash2 } from 'lucide-react'; import type { RefundReceipt } from './cashu-payment-workflow'; import { toast } from 'sonner'; import { ApiKeyInput } from '../api-key-input'; +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Card, CardContent, @@ -60,6 +61,7 @@ export function KeyInfoDetails({ const [isRefreshing, setIsRefreshing] = useState(false); const [isResetting, setIsResetting] = useState(null); const [isRefunding, setIsRefunding] = useState(false); + const [error, setError] = useState(null); // Sync internal state with props if they change useEffect(() => { @@ -69,12 +71,24 @@ export function KeyInfoDetails({ const fetchDetails = useCallback( async (keyToFetch: string) => { setIsRefreshing(true); + setError(null); try { const response = await fetch(`${baseUrl}/v1/balance/info`, { headers: { Authorization: `Bearer ${keyToFetch}` }, }); if (!response.ok) { - throw new Error('Failed to fetch key info'); + const errorText = await response.text(); + let errorMessage = errorText || 'Failed to fetch key info'; + try { + const parsed = JSON.parse(errorText); + // The error structure seems to be { detail: { error: { message: ... } } } + errorMessage = + parsed.detail?.error?.message || + (typeof parsed.detail === 'string' + ? parsed.detail + : errorMessage); + } catch {} + throw new Error(errorMessage); } const payload = await response.json(); const snapshot: WalletSnapshot = { @@ -93,9 +107,9 @@ export function KeyInfoDetails({ onWalletInfoUpdated?.(snapshot); toast.success('Key details synced'); } catch (error) { - toast.error( - error instanceof Error ? error.message : 'Failed to fetch details' - ); + const message = + error instanceof Error ? error.message : 'Failed to fetch details'; + setError(message); } finally { setIsRefreshing(false); } @@ -103,13 +117,16 @@ export function KeyInfoDetails({ [baseUrl, onWalletInfoUpdated] ); - const handleRefresh = async () => { + const handleRefresh = async (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); if (!apiKeyInput) return; await fetchDetails(apiKeyInput); }; const handleKeyChange = (newKey: string) => { setApiKeyInput(newKey); + setError(null); onApiKeyChanged?.(newKey); // Optionally clear info when key changes if (newKey !== apiKey) { @@ -204,6 +221,7 @@ export function KeyInfoDetails({ className='min-w-[80px] gap-1' onClick={handleRefresh} disabled={isRefreshing || !apiKeyInput} + type='button' > + {error && ( + + Error + {error} + + )}