import type { ReactNode } from 'react'; import { Loader2, ServerOff } from 'lucide-react'; import { NO_ACTIVE_SERVER } from './shared'; import { headscaleErrorMessage } from './useHeadscaleServers'; import { ErrorNote } from './Cards'; // The loading / no-server / failed states every domain section shares. // // "No active server" is a 409 carrying a `code`, deliberately not a 404 and deliberately not an empty // list — an empty node table would read as "your tailnet is empty", which is a very different and much // more alarming statement than "you haven't picked a server". function isNoActiveServer(err: unknown): boolean { const raw = (err as { message?: unknown } | null)?.message; if (typeof raw !== 'string') return false; try { return (JSON.parse(raw) as { code?: unknown }).code === NO_ACTIVE_SERVER; } catch { return false; } } type ViewShellProps = { isLoading: boolean; error: unknown; /** What this section is called, for the loading and empty copy. */ label: string; children: ReactNode; }; export const ViewShell = ({ isLoading, error, label, children }: ViewShellProps) => { if (isLoading) { return (
Pick one in the Servers section to see its {label}.
{hint}