diff --git a/src/components/ApiCard.tsx b/src/components/ApiCard.tsx index 71ad9f3..6d6eda1 100644 --- a/src/components/ApiCard.tsx +++ b/src/components/ApiCard.tsx @@ -15,6 +15,7 @@ import type { APIItem } from "../data/mockApis"; import RatingHistogram from "./RatingHistogram"; import { useCompareStore, compareStore } from "../state/compareStore"; import Sparkline from "./Sparkline"; +import WhyApi from "./WhyApi"; // ─── Skeleton ──────────────────────────────────────────────────────────────── @@ -506,6 +507,9 @@ export default function ApiCard({ ))} + {/* "Why this API?" rationale — hidden in compact rows to keep them dense. */} + {!isCompact && } +
= 4.5) { + reasons.push(`Highly rated by developers (${api.rating.toFixed(1)} / 5)`); + } + if (api.uptimePercent !== undefined && api.uptimePercent >= 99.9) { + reasons.push(`Reliable uptime (${api.uptimePercent.toFixed(2)}%)`); + } + if (api.avgLatencyMs !== undefined && api.avgLatencyMs <= 150) { + reasons.push(`Fast responses (~${api.avgLatencyMs} ms average latency)`); + } + + const price = api.pricePerCall ?? api.pricePerRequest; + if (price !== undefined && price <= 0.005) { + reasons.push("Cost-effective pricing per call"); + } + if (api.category) { + reasons.push(`Popular choice in ${api.category}`); + } + + // Always provide at least one reason so the affordance is never empty. + if (reasons.length === 0) { + reasons.push("Matches your search and category filters"); + } + return reasons; +} + +interface WhyApiProps { + api: APIItem; +} + +export default function WhyApi({ api }: WhyApiProps) { + const [open, setOpen] = useState(false); + const regionId = useId(); + const reasons = buildReasons(api); + + return ( +
e.stopPropagation()} + style={{ marginTop: 4 }} + > + + + {open && ( +
    + {reasons.map((reason, i) => ( +
  • + + {reason} +
  • + ))} +
+ )} +
+ ); +}