{previewError}
@@ -192,7 +196,9 @@ export function MarkdownEditor({Searching…
- )} - {!loading && results.length === 0 && ( -- No results for “{query}” -
+ data-search-dropdown + // Swallowing mousedown keeps focus on the input, so onBlur can close + // the popup immediately without racing the option's click. + onMouseDown={(e) => e.preventDefault()} + // Inline (mobile sheet): stay in the document flow so the popup + // cannot hang below a short viewport; the sheet's flex column and + // the popup's own scroll keep it reachable. Otherwise float right. + className={cn( + 'bg-popover border border-border rounded-md shadow-lg py-1 overflow-y-auto', + inline + ? 'mt-1 max-h-64' + : 'absolute top-full right-0 min-w-72 mt-1 z-50 max-h-[28rem]', )} + > + {/* Status lives outside the listbox — a listbox may only own + options, groups and presentational content. */} +Searching…
+ )} + {!loading && results.length === 0 && ( ++ No results for “{query}” +
+ )} +{fieldErrors['mergeInto']}
++ {fieldErrors['mergeInto']} +
)} )} diff --git a/apps/web/src/index.css b/apps/web/src/index.css index 088889e..d08befc 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -135,7 +135,7 @@ nav[aria-label="Breadcrumb"], [data-testid="offline-banner"], [data-testid="network-error-banner"], - [id="search-results-dropdown"] { + [data-search-dropdown] { display: none !important; } } diff --git a/apps/web/src/lib/queryClient.tsx b/apps/web/src/lib/queryClient.tsx index 47d45e3..97a0b7c 100644 --- a/apps/web/src/lib/queryClient.tsx +++ b/apps/web/src/lib/queryClient.tsx @@ -6,34 +6,36 @@ import { ApiError } from '@/lib/api'; export function ApiQueryClientProvider({ children }: { children: ReactNode }) { const { showError } = useNetworkError(); - const client = useMemo( - () => - new QueryClient({ - defaultOptions: { - queries: { - staleTime: 30_000, - retry: (failureCount, error) => { - if (error instanceof ApiError && error.status >= 400 && error.status < 500) { - return false; - } - return failureCount < 2; - }, - refetchOnWindowFocus: false, - }, - }, - queryCache: new QueryCache({ - onError: (error) => { - if (error instanceof ApiError && error.isServerError) { - showError('Something went wrong. We are looking at it.'); - } else if (!(error instanceof ApiError)) { - // Network-level error (fetch threw): treat as server error - showError('Network error. Please check your connection and try again.'); + const client = useMemo(() => { + // The banner's Retry re-issues whatever is currently on screen. The + // closure runs on a later error, after `queryClient` is assigned. + const retry = () => void queryClient.refetchQueries({ type: 'active' }); + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 30_000, + retry: (failureCount, error) => { + if (error instanceof ApiError && error.status >= 400 && error.status < 500) { + return false; } + return failureCount < 2; }, - }), + refetchOnWindowFocus: false, + }, + }, + queryCache: new QueryCache({ + onError: (error) => { + if (error instanceof ApiError && error.isServerError) { + showError('Something went wrong. We are looking at it.', retry); + } else if (!(error instanceof ApiError)) { + // Network-level error (fetch threw): treat as server error + showError('Network error. Please check your connection and try again.', retry); + } + }, }), - [showError], - ); + }); + return queryClient; + }, [showError]); // Clean up on unmount useEffect(() => { diff --git a/apps/web/src/pages/AccountClaim.tsx b/apps/web/src/pages/AccountClaim.tsx index be71480..10eaf76 100644 --- a/apps/web/src/pages/AccountClaim.tsx +++ b/apps/web/src/pages/AccountClaim.tsx @@ -105,8 +105,12 @@ export function AccountClaim() { if (loading || authLoading) { return ( -| Device | -IP | -Issued | -Status | +Device | +IP | +Issued | +Status |
|---|