{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "roadmap-3",
  "title": "Upvotable Feature Requests",
  "description": "Public roadmap card where visitors upvote feature requests, each row with a live vote count, status and category badges, and progress on in development work.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "avatar",
    "badge",
    "card",
    "progress"
  ],
  "files": [
    {
      "path": "registry/blocks/roadmap/3/roadmap-block.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport { Badge } from \"@/components/ui/badge\"\nimport {\n  Card,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\"\nimport { Progress } from \"@/components/ui/progress\"\nimport { cn } from \"@/lib/utils\"\nimport { IconPlaceholder } from \"@/components/icons/icon-placeholder\"\n\ntype Status = \"Planned\" | \"In Progress\" | \"Shipped\"\n\ntype Contributor = { name: string; src: string }\n\ntype Feature = {\n  id: string\n  title: string\n  description: string\n  category: string\n  status: Status\n  votes: number\n  progress?: number\n  team?: Contributor[]\n}\n\nconst team: Record<string, Contributor> = {\n  ada: { name: \"Ada\", src: \"https://i.pravatar.cc/64?img=47\" },\n  leo: { name: \"Leo\", src: \"https://i.pravatar.cc/64?img=12\" },\n  mara: { name: \"Mara\", src: \"https://i.pravatar.cc/64?img=32\" },\n}\n\nconst features: Feature[] = [\n  {\n    id: \"mobile\",\n    title: \"Native mobile apps\",\n    description: \"Offline-first iOS and Android with push notifications.\",\n    category: \"Mobile\",\n    status: \"In Progress\",\n    votes: 342,\n    progress: 72,\n    team: [team.ada, team.leo, team.mara],\n  },\n  {\n    id: \"dashboards\",\n    title: \"Custom dashboards\",\n    description: \"Build the metric views your team needs with drag and drop.\",\n    category: \"Analytics\",\n    status: \"Planned\",\n    votes: 218,\n  },\n  {\n    id: \"sso\",\n    title: \"SAML single sign-on\",\n    description: \"Enterprise login through your identity provider.\",\n    category: \"Security\",\n    status: \"Planned\",\n    votes: 156,\n  },\n  {\n    id: \"api\",\n    title: \"Public API v2\",\n    description: \"A faster, fully typed API with per-key rate limits.\",\n    category: \"API\",\n    status: \"Shipped\",\n    votes: 489,\n  },\n]\n\nconst statusVariant: Record<Status, \"default\" | \"secondary\" | \"outline\"> = {\n  Planned: \"outline\",\n  \"In Progress\": \"secondary\",\n  Shipped: \"default\",\n}\n\nexport default function RoadmapBlock() {\n  const [voted, setVoted] = React.useState<Record<string, boolean>>({})\n\n  const toggle = (id: string) =>\n    setVoted((prev) => ({ ...prev, [id]: !prev[id] }))\n\n  return (\n    <section className=\"flex w-full items-center justify-center bg-background px-6 py-16 text-foreground\">\n      <Card className=\"w-full max-w-xl\">\n        <CardHeader>\n          <CardTitle>Roadmap</CardTitle>\n          <CardDescription>\n            Vote on what matters most. The features with the most support move\n            up the list.\n          </CardDescription>\n        </CardHeader>\n\n        <ul className=\"flex flex-col divide-y divide-border border-t border-border\">\n          {features.map((feature) => {\n            const isVoted = voted[feature.id] ?? false\n            const count = feature.votes + (isVoted ? 1 : 0)\n            return (\n              <li key={feature.id} className=\"flex items-start gap-4 px-6 py-4\">\n                <button\n                  type=\"button\"\n                  onClick={() => toggle(feature.id)}\n                  aria-pressed={isVoted}\n                  aria-label={`Upvote ${feature.title}`}\n                  className={cn(\n                    \"flex w-12 shrink-0 flex-col items-center gap-0.5 rounded-md border py-1.5 text-xs font-medium tabular-nums transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n                    isVoted\n                      ? \"border-primary bg-primary/10 text-primary\"\n                      : \"border-border text-muted-foreground hover:bg-muted/50 hover:text-foreground\"\n                  )}\n                >\n                  <IconPlaceholder\n                    lucide=\"ChevronUp\"\n                    tabler=\"IconChevronUp\"\n                    hugeicons=\"ArrowUp01Icon\"\n                    phosphor=\"CaretUp\"\n                    remixicon=\"RiArrowUpSLine\"\n                    className=\"size-4\"\n                    aria-hidden=\"true\"\n                  />\n                  {count}\n                </button>\n\n                <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n                  <div className=\"flex flex-wrap items-center gap-2\">\n                    <h3 className=\"font-heading text-sm font-semibold\">\n                      {feature.title}\n                    </h3>\n                    <Badge variant={statusVariant[feature.status]}>\n                      {feature.status}\n                    </Badge>\n                    <Badge variant=\"secondary\">{feature.category}</Badge>\n                  </div>\n                  <p className=\"text-xs/relaxed text-muted-foreground\">\n                    {feature.description}\n                  </p>\n\n                  {feature.progress != null && (\n                    <div className=\"mt-0.5 flex flex-col gap-1.5\">\n                      <div className=\"flex items-center justify-between gap-3\">\n                        {feature.team && (\n                          <div className=\"flex -space-x-2\">\n                            {feature.team.map((member) => (\n                              <Avatar\n                                key={member.name}\n                                className=\"size-6 border-2 border-card\"\n                              >\n                                <AvatarImage\n                                  src={member.src}\n                                  alt={member.name}\n                                  className=\"grayscale\"\n                                />\n                                <AvatarFallback className=\"text-[10px]\">\n                                  {member.name[0]}\n                                </AvatarFallback>\n                              </Avatar>\n                            ))}\n                          </div>\n                        )}\n                        <span className=\"text-xs font-medium text-muted-foreground tabular-nums\">\n                          {feature.progress}%\n                        </span>\n                      </div>\n                      <Progress\n                        value={feature.progress}\n                        aria-label={`${feature.title} progress`}\n                      />\n                    </div>\n                  )}\n                </div>\n              </li>\n            )\n          })}\n        </ul>\n      </Card>\n    </section>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/blocks/roadmap-3.tsx"
    }
  ],
  "meta": {
    "height": "595px",
    "tier": "free"
  },
  "categories": [
    "roadmap"
  ],
  "type": "registry:block"
}