{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "onboarding-01",
  "type": "registry:block",
  "title": "Onboarding",
  "description": "A onboarding block.",
  "author": "ephraim duncan <https://ephraimduncan.com>",
  "registryDependencies": [
    "button",
    "collapsible",
    "dropdown-menu"
  ],
  "dependencies": [
    "@tabler/icons-react"
  ],
  "files": [
    {
      "path": "content/components/onboarding/onboarding-01.tsx",
      "type": "registry:component",
      "target": "components/onboarding-01.tsx",
      "content": "'use client';\n\nimport {\n  IconArchive,\n  IconChevronRight,\n  IconCircleCheckFilled,\n  IconCircleDashed,\n  IconDots,\n  IconMail,\n} from '@tabler/icons-react';\nimport { useState } from 'react';\nimport { Button, buttonVariants } from '@/components/ui/button';\nimport { Collapsible, CollapsibleContent } from '@/components/ui/collapsible';\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { cn } from '@/lib/utils';\n\nconst steps = [\n  {\n    id: 'profile',\n    title: 'Complete your profile',\n    description:\n      'Add your name, photo, and role so your team knows who you are.',\n    completed: true,\n    actionLabel: 'Edit profile',\n    actionHref: '#',\n  },\n  {\n    id: 'workspace',\n    title: 'Set up your workspace',\n    description:\n      'Customize your workspace with a name, icon, and default settings.',\n    completed: false,\n    actionLabel: 'Configure workspace',\n    actionHref: '#',\n  },\n  {\n    id: 'invite',\n    title: 'Invite your team',\n    description:\n      'Bring your teammates on board so you can collaborate in real time.',\n    completed: false,\n    actionLabel: 'Send invites',\n    actionHref: '#',\n  },\n  {\n    id: 'integrations',\n    title: 'Connect integrations',\n    description:\n      'Link your favorite tools like Slack, GitHub, and Linear to streamline workflows.',\n    completed: false,\n    actionLabel: 'Browse integrations',\n    actionHref: '#',\n  },\n  {\n    id: 'workflow',\n    title: 'Create your first workflow',\n    description:\n      'Automate a repetitive task with a simple drag-and-drop workflow builder.',\n    completed: false,\n    actionLabel: 'Build workflow',\n    actionHref: '#',\n  },\n  {\n    id: 'notifications',\n    title: 'Set up notifications',\n    description:\n      'Choose how and when you want to be notified about updates and mentions.',\n    completed: false,\n    actionLabel: 'Manage notifications',\n    actionHref: '#',\n  },\n];\n\ninterface OnboardingStep {\n  id: string;\n  title: string;\n  description: string;\n  completed: boolean;\n  actionLabel: string;\n  actionHref: string;\n}\n\nfunction CircularProgress({\n  completed,\n  total,\n}: {\n  completed: number;\n  total: number;\n}) {\n  const progress = total > 0 ? ((total - completed) / total) * 100 : 0;\n  const strokeDashoffset = 100 - progress;\n\n  return (\n    <svg className=\"-rotate-90\" height=\"14\" viewBox=\"0 0 14 14\" width=\"14\">\n      <circle\n        className=\"stroke-muted\"\n        cx=\"7\"\n        cy=\"7\"\n        fill=\"none\"\n        pathLength=\"100\"\n        r=\"6\"\n        strokeWidth=\"2\"\n      />\n      <circle\n        className=\"stroke-primary\"\n        cx=\"7\"\n        cy=\"7\"\n        fill=\"none\"\n        pathLength=\"100\"\n        r=\"6\"\n        strokeDasharray=\"100\"\n        strokeLinecap=\"round\"\n        strokeWidth=\"2\"\n        style={{ strokeDashoffset }}\n      />\n    </svg>\n  );\n}\n\nfunction StepIndicator({ completed }: { completed: boolean }) {\n  if (completed) {\n    return (\n      <IconCircleCheckFilled\n        aria-hidden=\"true\"\n        className=\"mt-1 size-4.5 shrink-0 text-primary\"\n      />\n    );\n  }\n  return (\n    <IconCircleDashed\n      aria-hidden=\"true\"\n      className=\"mt-1 size-5 shrink-0 stroke-muted-foreground/40\"\n      strokeWidth={2}\n    />\n  );\n}\n\nexport function Onboarding01() {\n  const [currentSteps, setCurrentSteps] = useState<OnboardingStep[]>(steps);\n  const [openStepId, setOpenStepId] = useState<string | null>(() => {\n    const firstIncomplete = steps.find((s) => !s.completed);\n    return firstIncomplete?.id ?? steps[0]?.id ?? null;\n  });\n  const [dismissed, setDismissed] = useState(false);\n\n  const completedCount = currentSteps.filter((s) => s.completed).length;\n  const remainingCount = currentSteps.length - completedCount;\n\n  const handleStepClick = (stepId: string) => {\n    setOpenStepId(openStepId === stepId ? null : stepId);\n  };\n\n  const handleStepAction = (step: OnboardingStep) => {\n    const updated = currentSteps.map((s) =>\n      s.id === step.id ? { ...s, completed: true } : s\n    );\n    setCurrentSteps(updated);\n    const nextIncomplete = updated.find((s) => !s.completed);\n    setOpenStepId(nextIncomplete?.id ?? null);\n  };\n\n  if (dismissed) {\n    return (\n      <div className=\"flex min-h-dvh items-center justify-center bg-background p-4\">\n        <div className=\"text-center\">\n          <p className=\"text-pretty text-muted-foreground\">\n            Checklist dismissed\n          </p>\n          <button\n            className=\"mt-2 text-primary text-sm underline\"\n            onClick={() => setDismissed(false)}\n          >\n            Show again\n          </button>\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center bg-background p-4\">\n      <div className=\"w-full max-w-lg\">\n        <div className=\"w-md rounded-lg border bg-card p-4 text-card-foreground shadow-xs\">\n          <div className=\"mr-2 mb-4 flex flex-col justify-between sm:flex-row sm:items-center\">\n            <h3 className=\"ml-2 text-balance font-semibold text-foreground\">\n              Get started with Acme\n            </h3>\n            <div className=\"mt-2 flex items-center justify-end sm:mt-0\">\n              <CircularProgress\n                completed={remainingCount}\n                total={currentSteps.length}\n              />\n              <div className=\"mr-3 ml-1.5 text-muted-foreground text-sm\">\n                <span className=\"font-medium text-foreground\">\n                  {completedCount}\n                </span>\n                {' / '}\n                <span className=\"font-medium text-foreground\">\n                  {currentSteps.length}\n                </span>{' '}\n                completed\n              </div>\n              <DropdownMenu>\n                <DropdownMenuTrigger\n                  render={\n                    <Button className=\"h-6 w-6\" size=\"icon\" variant=\"ghost\" />\n                  }\n                >\n                  <IconDots aria-hidden=\"true\" className=\"h-4 w-4 shrink-0\" />\n                  <span className=\"sr-only\">Options</span>\n                </DropdownMenuTrigger>\n                <DropdownMenuContent align=\"end\" className=\"w-40\">\n                  <DropdownMenuItem onClick={() => setDismissed(true)}>\n                    <IconArchive\n                      aria-hidden=\"true\"\n                      className=\"mr-2 h-4 w-4 shrink-0\"\n                    />\n                    Dismiss\n                  </DropdownMenuItem>\n                  <DropdownMenuItem\n                    onClick={() =>\n                      window.open('mailto:support@acme.com?subject=Feedback')\n                    }\n                  >\n                    <IconMail\n                      aria-hidden=\"true\"\n                      className=\"mr-2 h-4 w-4 shrink-0\"\n                    />\n                    Give feedback\n                  </DropdownMenuItem>\n                </DropdownMenuContent>\n              </DropdownMenu>\n            </div>\n          </div>\n\n          <div className=\"space-y-0\">\n            {currentSteps.map((step, index) => {\n              const isOpen = openStepId === step.id;\n              const isFirst = index === 0;\n              const prevStep = currentSteps[index - 1];\n              const isPrevOpen = prevStep && openStepId === prevStep.id;\n\n              const showBorderTop = !(isFirst || isOpen || isPrevOpen);\n\n              return (\n                <div\n                  className={cn(\n                    'group',\n                    isOpen && 'rounded-lg',\n                    showBorderTop && 'border-border border-t'\n                  )}\n                  key={step.id}\n                >\n                  <div\n                    className={cn(\n                      'block w-full cursor-pointer text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n                      isOpen && 'rounded-lg'\n                    )}\n                    onClick={() => handleStepClick(step.id)}\n                    onKeyDown={(e) => {\n                      if (e.key === 'Enter' || e.key === ' ') {\n                        e.preventDefault();\n                        handleStepClick(step.id);\n                      }\n                    }}\n                    role=\"button\"\n                    tabIndex={0}\n                  >\n                    <div\n                      className={cn(\n                        'relative overflow-hidden rounded-lg transition-colors',\n                        isOpen && 'border border-border bg-muted'\n                      )}\n                    >\n                      <div className=\"relative flex items-center justify-between gap-3 py-3 pr-2 pl-4\">\n                        <div className=\"flex w-full gap-3\">\n                          <div className=\"shrink-0\">\n                            <StepIndicator completed={step.completed} />\n                          </div>\n                          <div className=\"mt-0.5 grow\">\n                            <h4\n                              className={cn(\n                                'font-semibold',\n                                step.completed\n                                  ? 'text-primary'\n                                  : 'text-foreground'\n                              )}\n                            >\n                              {step.title}\n                            </h4>\n                            <Collapsible open={isOpen}>\n                              <CollapsibleContent>\n                                <p className=\"mt-2 text-pretty text-muted-foreground text-sm sm:max-w-64 md:max-w-xs\">\n                                  {step.description}\n                                </p>\n                                <a\n                                  className={cn(\n                                    buttonVariants({ size: 'sm' }),\n                                    'mt-3'\n                                  )}\n                                  href={step.actionHref}\n                                  onClick={(e) => {\n                                    e.stopPropagation();\n                                    handleStepAction(step);\n                                  }}\n                                >\n                                  {step.actionLabel}\n                                </a>\n                              </CollapsibleContent>\n                            </Collapsible>\n                          </div>\n                        </div>\n                        {!isOpen && (\n                          <IconChevronRight\n                            aria-hidden=\"true\"\n                            className=\"h-4 w-4 shrink-0 text-muted-foreground\"\n                          />\n                        )}\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              );\n            })}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
    }
  ],
  "categories": [
    "onboarding"
  ]
}