{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "onboarding-checklist",
  "title": "Onboarding Checklist",
  "description": "A setup-progress card — overall progress bar, ordered step rows with done/current/locked states, a collapsible completed group, a dismiss control and a compact variant for docking in a sidebar.",
  "dependencies": [
    "@untitledui/icons@^0.0.22"
  ],
  "registryDependencies": [
    "@nx-ui/button",
    "@nx-ui/card",
    "@nx-ui/collapsible",
    "@nx-ui/progress",
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/onboarding-checklist.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Check, ChevronDown, ChevronRight, Lock01, X } from \"@untitledui/icons\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { Card, CardContent, CardHeader } from \"@/components/ui/card\"\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\"\nimport { Progress } from \"@/components/ui/progress\"\n\n/** A single setup step in an {@link OnboardingChecklist}. */\nexport type OnboardingStep = {\n  id: string\n  title: string\n  description?: string\n  icon?: React.ComponentType<{ className?: string }>\n  status: \"done\" | \"current\" | \"locked\"\n  /** Label + handler for the row's chevron action (e.g. \"Continue\"). Omitted for locked steps. */\n  onAction?: () => void\n}\n\nfunction StepRow({\n  step,\n  onAction,\n}: {\n  step: OnboardingStep\n  onAction?: (step: OnboardingStep) => void\n}) {\n  const Icon = step.icon\n  const locked = step.status === \"locked\"\n  const done = step.status === \"done\"\n  const clickable = !locked && (step.onAction || onAction)\n\n  return (\n    <div\n      data-slot=\"onboarding-step\"\n      data-status={step.status}\n      className={cn(\n        \"group/step flex items-center gap-3 px-4 py-3 sm:px-5\",\n        clickable && \"cursor-pointer hover:bg-muted\",\n        locked && \"opacity-50\"\n      )}\n      role={clickable ? \"button\" : undefined}\n      tabIndex={clickable ? 0 : undefined}\n      onClick={clickable ? () => (step.onAction ?? onAction)?.(step) : undefined}\n      onKeyDown={\n        clickable\n          ? (event) => {\n              if (event.key === \"Enter\" || event.key === \" \") {\n                event.preventDefault()\n                ;(step.onAction ?? onAction)?.(step)\n              }\n            }\n          : undefined\n      }\n    >\n      <span\n        data-slot=\"onboarding-step-marker\"\n        className={cn(\n          \"flex size-8 shrink-0 items-center justify-center rounded-full border\",\n          done && \"border-transparent bg-primary text-primary-foreground\",\n          step.status === \"current\" && \"border-primary/40 bg-primary/5 text-primary\",\n          locked && \"border-border bg-bg-tertiary text-muted-foreground\"\n        )}\n      >\n        {done ? (\n          <Check className=\"size-4\" />\n        ) : locked ? (\n          <Lock01 className=\"size-3.5\" />\n        ) : Icon ? (\n          <Icon className=\"size-4\" />\n        ) : (\n          <span className=\"size-2 rounded-full bg-current\" />\n        )}\n      </span>\n\n      <div className=\"flex min-w-0 flex-1 flex-col\">\n        <span\n          className={cn(\n            \"text-sm font-semibold text-foreground\",\n            done && \"text-muted-foreground line-through decoration-muted-foreground/60\"\n          )}\n        >\n          {step.title}\n        </span>\n        {step.description ? (\n          <span className=\"truncate text-sm text-muted-foreground\">\n            {step.description}\n          </span>\n        ) : null}\n      </div>\n\n      {clickable ? (\n        <ChevronRight className=\"size-4 shrink-0 text-fg-quaternary transition-transform group-hover/step:translate-x-0.5\" />\n      ) : null}\n    </div>\n  )\n}\n\n/**\n * OnboardingChecklist — a setup-progress card: an overall {@link Progress}\n * bar with \"N of M complete\", a list of {@link OnboardingStep} rows (icon,\n * title, description, done/current/locked states — done steps get a\n * strikethrough), a collapsible group for completed steps once any exist,\n * and an optional dismiss control. Pass `compact` for a smaller variant sized\n * to dock in a sidebar or page corner.\n *\n * Purely props/callback-driven: `steps` is the full ordered list, `onDismiss`\n * fires from the close control, and each row's chevron calls either its own\n * `onAction` or the shared `onStepAction`.\n *\n * @example\n * <OnboardingChecklist\n *   title=\"Finish setting up\"\n *   steps={steps}\n *   onStepAction={(step) => router.push(`/setup/${step.id}`)}\n *   onDismiss={() => setShow(false)}\n * />\n */\nfunction OnboardingChecklist({\n  title = \"Get started\",\n  description,\n  steps,\n  onStepAction,\n  onDismiss,\n  compact = false,\n  className,\n}: {\n  title?: React.ReactNode\n  description?: React.ReactNode\n  steps: OnboardingStep[]\n  onStepAction?: (step: OnboardingStep) => void\n  onDismiss?: () => void\n  compact?: boolean\n  className?: string\n}) {\n  const completed = steps.filter((step) => step.status === \"done\")\n  const pending = steps.filter((step) => step.status !== \"done\")\n  const total = steps.length\n  const pct = total === 0 ? 0 : Math.round((completed.length / total) * 100)\n\n  return (\n    <Card\n      data-slot=\"onboarding-checklist\"\n      data-compact={compact}\n      className={cn(\"gap-0 p-0\", compact && \"max-w-sm\", className)}\n    >\n      <CardHeader className=\"gap-3 px-4 pt-4 pb-3 sm:px-5 sm:pt-5\">\n        <div className=\"flex items-start justify-between gap-3\">\n          <div className=\"flex flex-col gap-0.5\">\n            <h2 className={cn(\"font-semibold text-foreground\", compact ? \"text-sm\" : \"text-md\")}>\n              {title}\n            </h2>\n            {description ? (\n              <p className=\"text-sm text-muted-foreground\">{description}</p>\n            ) : null}\n          </div>\n          {onDismiss ? (\n            <Button\n              type=\"button\"\n              variant=\"ghost\"\n              size=\"icon-xs\"\n              aria-label=\"Dismiss checklist\"\n              onClick={onDismiss}\n              className=\"shrink-0\"\n            >\n              <X />\n            </Button>\n          ) : null}\n        </div>\n\n        <div className=\"flex flex-col gap-1.5\">\n          <Progress value={pct} />\n          <span className=\"text-xs font-medium text-muted-foreground\">\n            {completed.length} of {total} complete\n          </span>\n        </div>\n      </CardHeader>\n\n      <CardContent className=\"flex flex-col p-0\">\n        <div className=\"flex flex-col divide-y divide-border border-t border-border\">\n          {pending.map((step) => (\n            <StepRow key={step.id} step={step} onAction={onStepAction} />\n          ))}\n        </div>\n\n        {completed.length > 0 ? (\n          <Collapsible className=\"border-t border-border\">\n            <CollapsibleTrigger className=\"group flex w-full cursor-pointer items-center gap-2 px-4 py-2.5 text-left text-sm font-semibold text-muted-foreground hover:text-foreground sm:px-5\">\n              <ChevronDown className=\"size-4 shrink-0 -rotate-90 transition-transform group-data-panel-open:rotate-0\" />\n              Completed ({completed.length})\n            </CollapsibleTrigger>\n            <CollapsibleContent>\n              <div className=\"flex flex-col divide-y divide-border\">\n                {completed.map((step) => (\n                  <StepRow key={step.id} step={step} onAction={onStepAction} />\n                ))}\n              </div>\n            </CollapsibleContent>\n          </Collapsible>\n        ) : null}\n      </CardContent>\n    </Card>\n  )\n}\n\nexport { OnboardingChecklist }\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}