spb/market-atlas
Public
TypeScript 96.7%
SQL 1.6%
CSS 0.8%
JavaScript 0.5%
1"use client";23import Link from "next/link";4import { useMemo } from "react";5import { FreshnessLabel } from "@/components/ui/freshness";6import { LivePrice } from "@/components/ui/price";7import { StatusBadge } from "@/components/ui/status-badge";8import { cx, instrumentHref } from "@/lib/format";9import { useMarketStream } from "@/lib/stream";10import type { InstrumentWithQuote } from "@/lib/types";1112/** Compact quote tiles for the homepage pulse. One row of tiles per asset class, scrollable on mobile. */13export function PulseRow({ title, href, items, className }: { title: string; href: string; items: InstrumentWithQuote[]; className?: string }) {14 const channels = useMemo(() => items.map((i) => `quotes:${i.id}`), [items]);15 useMarketStream(channels);16 if (!items.length) return null;17 return (18 <div className={cx("rule-b py-3", className)}>19 <div className="mb-2 flex items-baseline justify-between">20 <h3 className="text-[11px] font-medium uppercase tracking-wide text-ink-3">{title}</h3>21 <Link href={href} className="text-xs text-accent hover:underline">22 All →23 </Link>24 </div>25 <div className="-mx-3 flex gap-2 overflow-x-auto px-3 pb-1 [scrollbar-width:none] sm:mx-0 sm:grid grid-cols-1 [&>*]:min-w-0 sm:grid-cols-3 sm:px-0 lg:grid-cols-6 [&::-webkit-scrollbar]:hidden">26 {items.slice(0, 6).map((i) => (27 <Link key={i.id} href={instrumentHref(i.id)} className="min-w-[164px] shrink-0 rounded-md border border-rule bg-surface px-3 py-2 hover:border-rule-strong sm:min-w-0">28 <div className="flex items-center justify-between gap-2">29 <span className="mono text-xs font-medium">{i.symbol}</span>30 <StatusBadge status={i.quote?.data_status} />31 </div>32 <div className="mt-1 truncate text-[11px] text-ink-3">{i.name}</div>33 <div className="mt-1.5">34 <LivePrice instrumentId={i.id} quote={i.quote} assetClass={i.asset_class} className="text-[15px]" />35 </div>36 <FreshnessLabel quote={i.quote} className="mt-1 !text-[10.5px]" />37 </Link>38 ))}39 </div>40 </div>41 );42}43