import { A } from "@solidjs/router" import { Show, createEffect, createSignal, onCleanup } from "solid-js" import type { Relay, PlanId } from "@/lib/api" import menuDotsIcon from "@/assets/menu-dots-2.svg" import Field from "@/components/Field" import PricingTable from "@/components/PricingTable" import ToggleButton from "@/components/ToggleButton" import ToggleField from "@/components/ToggleField" import { setToastMessage } from "@/components/Toast" import { plans } from "@/lib/state" const STATUS_STYLES: Record = { active: "bg-green-50 text-green-700 border-green-200", inactive: "bg-gray-100 text-gray-500 border-gray-200", } function StatusBadge(props: { status: string }) { const styles = () => STATUS_STYLES[props.status] ?? "bg-gray-100 text-gray-500 border-gray-200" const label = () => props.status.replace(/_/g, " ") return ( {label()} ) } function DetailSection(props: { title: string; children: any }) { return (

{props.title}

{props.children}
) } function MembershipSection(props: { title: string; children: any }) { return (

{props.title}

{props.children}
) } type RelayDetailCardProps = { relay: Relay currentMembers?: number showTenant?: boolean editHref?: string onDeactivate?: () => void onReactivate?: () => void deactivating?: boolean reactivating?: boolean onTogglePublicJoin?: () => void onToggleStripSignatures?: () => void onToggleGroups?: () => void onToggleManagement?: () => void onToggleMediaStorage?: () => void onToggleLivekitSupport?: () => void onTogglePushNotifications?: () => void onUpdatePlan?: (plan: PlanId) => Promise enforcePlanLimits?: boolean showPlanActions?: boolean } export default function RelayDetailCard(props: RelayDetailCardProps) { const r = () => props.relay const flag = (value: number, fallback: boolean) => { if (value === 0) return false if (value === 1) return true return fallback } const [menuOpen, setMenuOpen] = createSignal(false) const [plan, setPlan] = createSignal(props.relay.plan) let menuContainerRef: HTMLDivElement | undefined const memberLimitLabel = () => { const p = plans().find(p => p.id === r().plan) if (!p) return "?" return p.members === null ? "∞" : String(p.members) } const planLimited = () => (props.enforcePlanLimits ?? true) && r().plan === "free" const showPlanActions = () => props.showPlanActions ?? true async function changePlan(plan: PlanId) { setPlan(plan) try { await props.onUpdatePlan?.(plan) setToastMessage(`Plan updated to ${plan}`, "success") } catch { // error is handled by the caller } } createEffect(() => { if (!menuOpen()) return const handleClickOutside = (event: MouseEvent) => { const target = event.target as Node | null if (target && !menuContainerRef?.contains(target)) { setMenuOpen(false) } } const handleEscape = (event: KeyboardEvent) => { if (event.key === "Escape") { setMenuOpen(false) } } document.addEventListener("mousedown", handleClickOutside) document.addEventListener("keydown", handleEscape) onCleanup(() => { document.removeEventListener("mousedown", handleClickOutside) document.removeEventListener("keydown", handleEscape) }) }) return (
{/* Header */}

{r().info_name || r().subdomain}

wss://{r().subdomain}.spaces.coracle.social

{r().info_description}

setMenuOpen(false)} > Edit Details

Provisioning error

{r().sync_error}



}> Upgrade Plan } > Update Plan } > }> Upgrade Plan } > Update Plan } >
{props.currentMembers ?? "—"} {memberLimitLabel()} {r().tenant}
{r().plan} } >
) }