SPB Git
2.3 KB · 35 lines tsx
1import Link from "next/link";2import { LogoMark } from "./ui/logo-mark";3import { Badge } from "./ui/badge";4import { label, fmtDate } from "@/lib/utils";5import type { ToolCard as T } from "@/lib/public/queries";6import { ShieldCheck, BookOpenText } from "lucide-react";78export function ToolCard({ t, blurb, className = "" }: { t: T; blurb?: string | null; className?: string }) {9  return (10    <article className={`card group relative flex flex-col p-4 ${className}`}>11      <div className="flex items-start gap-3">12        <LogoMark name={t.name} size={44} />13        <div className="min-w-0 flex-1">14          <h3 className="truncate text-[1.02rem] font-semibold leading-tight tracking-tight">15            <Link href={`/tools/${t.slug}`} className="after:absolute after:inset-0 after:rounded-2xl focus-visible:outline-none">{t.name}</Link>16          </h3>17          <p className="truncate text-xs text-fg-3">{t.vendorName ?? label(t.productType)}{t.primaryCategory ? ` · ${t.primaryCategory.name}` : ""}</p>18        </div>19        {t.depth === "deep" && <span title="In-depth editorial entry" className="text-accent"><BookOpenText className="h-4 w-4" aria-hidden /><span className="sr-only">In-depth entry</span></span>}20      </div>21      <p className="mt-3 line-clamp-3 text-sm leading-relaxed text-fg-2">{blurb ?? t.summary}</p>22      <div className="mt-auto flex flex-wrap items-center gap-1.5 pt-3">23        <Badge tone={t.sourceModel === "open-source" ? "ok" : "neutral"}>{t.licenseSpdx ?? label(t.sourceModel)}</Badge>24        {t.pricingModel.slice(0, 2).map((p) => <Badge key={p}>{label(p)}</Badge>)}25        {t.executionModes.includes("local") && <Badge tone="info">Local</Badge>}26        {t.executionModes.includes("self-hosted") && <Badge tone="info">Self-hosted</Badge>}27        {t.productStatus !== "active" && <Badge tone={t.productStatus === "unknown" ? "warn" : "bad"}>{label(t.productStatus)}</Badge>}28        <span className="ml-auto inline-flex items-center gap-1 text-[11px] text-fg-3" title={t.linkState === "ok" ? `Links verified ${fmtDate(t.checkedAt)}` : label(t.linkState)}>29          <ShieldCheck className={`h-3.5 w-3.5 ${t.linkState === "ok" ? "text-ok" : t.linkState === "broken" ? "text-bad" : "text-fg-3"}`} aria-hidden />{t.linkState === "ok" ? fmtDate(t.checkedAt) : label(t.linkState)}30        </span>31      </div>32    </article>33  );34}35
Files · laptop-initial-20261102
Menu

Theme