import { AlertTriangle } from 'lucide-react'; import type { MeshDataPlaneStatus } from '@/types/mesh'; type Reason = MeshDataPlaneStatus['reason']; type ActionableReason = Exclude; const HEADLINES: Record string> = { subnet_invalid: () => 'SENCHO_MESH_SUBNET is not a valid CIDR.', subnet_overlap: (s) => `Mesh subnet ${s.subnet} overlaps an existing Docker network on this host.`, subnet_mismatch: (s) => `sencho_mesh already exists with a different subnet than ${s.subnet}.`, ip_in_use: (s) => `Another container is using Sencho's address on ${s.subnet}.`, attach_failed: () => 'Sencho could not attach to its own mesh network.', not_found: (s) => `sencho_mesh is not present on this host (last known subnet ${s.subnet}).`, }; function isActionable(reason: Reason): reason is ActionableReason { return reason !== 'ok' && reason !== 'not_started' && reason !== 'not_in_docker'; } export type MeshDataPlaneBannerVariant = 'tab' | 'card'; /** * Surfaces the local mesh data-plane failure with the operator-actionable * recovery hint. Suppresses the dev-mode `not_in_docker` and the transient * `not_started` states so the operator only sees a banner when there is * something to fix. * * - `tab` variant: the full block shown at the top of the Routing tab. * - `card` variant: a single-line strip designed to sit inside a dashboard * card (e.g. Fleet Heartbeat). */ export function MeshDataPlaneBanner({ status, variant = 'tab', }: { status: MeshDataPlaneStatus | null; variant?: MeshDataPlaneBannerVariant; }) { if (!status || status.ok || !isActionable(status.reason)) return null; const headline = HEADLINES[status.reason](status); if (variant === 'card') { // Compact single-row strip for inside the Fleet Heartbeat card. // Headline only — the full recovery hint lives on the Routing tab // banner and in /docs/features/sencho-mesh.mdx#troubleshooting. return (
Mesh data plane down · {status.reason} · {headline}
); } return (
Mesh data plane is down
{headline} {' '} Set SENCHO_MESH_SUBNET to a free /24 (for example 10.42.0.0/24) and restart the Sencho container.
{status.message ? (
{status.message}
) : null}
); }