{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "recommendation-card", "type": "registry:component", "title": "Recommendation Card", "description": "Agent suggestion with a confidence meter and actions.", "registryDependencies": [ "https://www.beautifului.dev/r/foundation.json", "https://www.beautifului.dev/r/button.json", "https://www.beautifului.dev/r/entity-chip.json", "https://www.beautifului.dev/r/value-pill.json" ], "files": [ { "path": "components/primitives/RecommendationCard.tsx", "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { Button, type ButtonVariant } from \"@/components/atoms/Button\";\nimport { EntityChip } from \"@/components/atoms/EntityChip\";\nimport { ValuePill } from \"@/components/atoms/ValuePill\";\n\n/* ─────────────────────────────────────────────────────────\n * RECOMMENDATION CARD\n * The card holds its shape. Pressing \"Alternatives\" opens a\n * new drawer listing the other options; picking one promotes\n * it to the recommendation. The primary action confirms.\n * ───────────────────────────────────────────────────────── */\n\nexport type RecommendationOption = {\n key: string;\n body: React.ReactNode;\n short: string;\n signal: number;\n tone: string;\n label: string;\n cta: string;\n ctaVariant: ButtonVariant;\n};\n\nexport type RecommendationLabels = {\n title: string;\n alternatives: string;\n otherOptions: string;\n accepted: string;\n};\n\nconst DEFAULT_LABELS: RecommendationLabels = {\n title: \"Want me to place this restock order?\",\n alternatives: \"Alternatives\",\n otherOptions: \"Other options\",\n accepted: \"Accepted\",\n};\n\nconst OPTIONS: RecommendationOption[] = [\n {\n key: \"high\",\n body: (\n <>\n Reorder waffle cones from{\" \"}\n {\" \"}\n with lead time 7 days\n \n ),\n short: \"Reorder from Cone King · 7-day lead\",\n signal: 3,\n tone: \"var(--green)\",\n label: \"High confidence\",\n cta: \"Accept\",\n ctaVariant: \"accent\",\n },\n {\n key: \"review\",\n body: (\n <>\n Switch vanilla to Vanilla Madagascar for peak season.\n \n ),\n short: \"Switch to Vanilla Madagascar\",\n signal: 2,\n tone: \"var(--orange)\",\n label: \"Needs review\",\n cta: \"Configure\",\n ctaVariant: \"primary\",\n },\n {\n key: \"none\",\n body: (\n <>\n Fall back to a full restock across every SKU.\n \n ),\n short: \"Full restock across every SKU\",\n signal: 0,\n tone: \"var(--ink-3)\",\n label: \"No signal\",\n cta: \"Accept full restock\",\n ctaVariant: \"primary\",\n },\n];\n\nfunction Meter({ signal, tone }: { signal: number; tone: string }) {\n return (\n \n {[0, 1, 2].map((bar) => (\n \n ))}\n \n );\n}\n\nexport default function RecommendationCard({\n options = OPTIONS,\n labels,\n}: {\n options?: RecommendationOption[];\n labels?: Partial;\n variant?: string;\n} = {}) {\n const t = { ...DEFAULT_LABELS, ...labels };\n const [selected, setSelected] = useState(0);\n const [open, setOpen] = useState(false);\n const [accepted, setAccepted] = useState(false);\n\n const active = options[selected];\n const others = options.map((o, i) => ({ o, i })).filter(({ i }) => i !== selected);\n\n return (\n
\n
\n \n {t.title}\n \n \n {active.body}\n

\n
\n\n {/* alternatives drawer — a distinctly new section of the card */}\n \n
\n
\n

\n {t.otherOptions}\n

\n {others.map(({ o, i }) => (\n {\n setSelected(i);\n setAccepted(false);\n }}\n className=\"flex w-full items-center gap-2.5 rounded-control px-1.5 py-1.5\n text-left transition-colors duration-100 hover:bg-hover\"\n >\n \n {o.short}\n {o.label}\n \n ))}\n
\n
\n
\n\n
\n \n \n {active.label}\n \n\n \n setOpen((current) => !current)}\n className=\"px-2.5 text-[12.5px]\"\n >\n {t.alternatives}\n \n setAccepted(true)}\n className=\"text-[12.5px]\"\n >\n {accepted ? t.accepted : active.cta}\n \n \n
\n \n );\n}\n", "type": "registry:component", "target": "components/primitives/RecommendationCard.tsx" } ] }