import { For, Show, type Component } from "solid-js"; import type { FrontendSnapshot, StateSnapshot } from "../types"; import BackendRow from "./BackendRow"; import StatusBadge from "../components/StatusBadge"; import Flash from "../components/Flash"; import Zippy from "../components/Zippy"; import { isAdmin } from "../stores/mode"; import { formatVIPAddress } from "../stores/state"; type Props = { snap: StateSnapshot; frontend: FrontendSnapshot; }; // FrontendCard is rendered as a Zippy so a deployment with many VIPs // can collapse the frontends it doesn't care about. The title line // carries the frontend name, a live state badge, address:port, the // protocol, the sticky marker, and (when set) the description. // // The body renders a single consolidated backend table with one row // per (pool, backend). The first column holds the pool name, shown // only on the first row of each pool and blank on subsequent rows — // a classic grouped-table layout that keeps rows dense while still // making pool grouping instantly scannable. const FrontendCard: Component = (props) => { const backendByName = () => Object.fromEntries(props.snap.backends.map((b) => [b.name, b])); const fe = () => props.frontend; const title = ( {fe().name} {formatVIPAddress(fe().address, fe().port)} {fe().protocol.toUpperCase()} {fe().src_ip_sticky && sticky} {fe().description && {fe().description}} ); return ( {(pool) => { // A pool is "active" when at least one of its backends // is currently serving traffic (effective_weight > 0). // Inactive pools — standby fallbacks, fully-drained // primaries — have every row rendered dimmer so the // operator can see at a glance which pool is actually // carrying traffic right now. const poolActive = () => pool.backends.some((pb) => pb.effective_weight > 0); return ( {(pb, idx) => { const backend = backendByName()[pb.name]; if (!backend) return null; return ( ); }} ); }}
pool backend address state weight effective last transition
); }; export default FrontendCard;