{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "onboarding-wizard",
  "title": "Onboarding Wizard",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/onboarding-wizard/OnboardingWizard.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport { Check, ChevronLeft, ChevronRight, Globe, Plus, X } from 'lucide-vue-next'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { SectionCard } from '@/components/ui/section-card'\nimport { Stepper } from '@/components/ui/stepper'\nimport { Switch } from '@/components/ui/switch'\n\ninterface Preference {\n  id: string\n  label: string\n  description: string\n  enabled: boolean\n}\n\nconst props = withDefaults(\n  defineProps<{\n    /** Jump straight to a step (1 Workspace · 2 Team · 3 Preferences · 4 Done). */\n    initialStep?: number\n    /** Pre-fill the workspace name so later steps read naturally in isolation. */\n    initialWorkspaceName?: string\n    /** Pre-seed invited teammates. */\n    initialInvites?: string[]\n    class?: HTMLAttributes['class']\n  }>(),\n  {\n    initialStep: 1,\n    initialWorkspaceName: 'Acme Inc',\n    initialInvites: () => [],\n  },\n)\n\nconst steps = [\n  { id: 1, title: 'Workspace' },\n  { id: 2, title: 'Team' },\n  { id: 3, title: 'Preferences' },\n  { id: 4, title: 'Done' },\n]\n\nconst step = ref(props.initialStep)\nconst workspaceName = ref(props.initialWorkspaceName)\nconst invites = ref<string[]>([...props.initialInvites])\nconst inviteDraft = ref('')\nconst inviteInvalid = ref(false)\n\nconst preferences = ref<Preference[]>([\n  {\n    id: 'digests',\n    label: 'Email digests',\n    description: 'A daily summary of workspace activity.',\n    enabled: true,\n  },\n  {\n    id: 'updates',\n    label: 'Product updates',\n    description: 'Occasional notes about new features and improvements.',\n    enabled: false,\n  },\n  {\n    id: 'reports',\n    label: 'Weekly reports',\n    description: 'Usage analytics delivered every Monday morning.',\n    enabled: true,\n  },\n])\n\nconst slug = computed(() => {\n  const cleaned = workspaceName.value\n    .trim()\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, '-')\n    .replace(/^-+|-+$/g, '')\n  return cleaned || 'your-workspace'\n})\n\nfunction addInvite() {\n  const email = inviteDraft.value.trim()\n  if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email) || invites.value.includes(email)) {\n    inviteInvalid.value = true\n    return\n  }\n  inviteInvalid.value = false\n  invites.value.push(email)\n  inviteDraft.value = ''\n}\n\nfunction removeInvite(email: string) {\n  invites.value = invites.value.filter((e) => e !== email)\n}\n\nfunction onStepperInput(value: number) {\n  // Free backward navigation; forward goes through the footer button only.\n  if (value < step.value && step.value !== 4) step.value = value\n}\n\nfunction goNext() {\n  if (step.value < 4) step.value += 1\n}\n\nfunction reset() {\n  step.value = 1\n  workspaceName.value = ''\n  invites.value = []\n  inviteDraft.value = ''\n}\n</script>\n\n<template>\n  <SectionCard\n    data-slot=\"onboarding-wizard\"\n    title=\"Set up your workspace\"\n    description=\"Four quick steps and your team is ready to collaborate.\"\n    :class=\"props.class\"\n  >\n    <Stepper :steps=\"steps\" :model-value=\"step\" class=\"mb-6\" @update:model-value=\"onStepperInput\" />\n\n    <!-- Step 1 · Workspace -->\n    <div v-if=\"step === 1\" class=\"space-y-4\">\n      <div class=\"space-y-2\">\n        <label for=\"onboarding-workspace-name\" class=\"text-sm font-medium\">Workspace name</label>\n        <Input id=\"onboarding-workspace-name\" v-model=\"workspaceName\" placeholder=\"Acme Inc\" autocomplete=\"off\" />\n      </div>\n      <div class=\"border-border bg-muted/40 flex items-center gap-2 rounded-md border px-3 py-2\">\n        <Globe class=\"text-muted-foreground size-4 shrink-0\" aria-hidden=\"true\" />\n        <p class=\"min-w-0 truncate text-xs\">\n          <span class=\"text-muted-foreground\">uipkge.dev/</span>\n          <span class=\"text-foreground font-medium\">{{ slug }}</span>\n        </p>\n      </div>\n    </div>\n\n    <!-- Step 2 · Team -->\n    <div v-else-if=\"step === 2\" class=\"space-y-4\">\n      <form class=\"flex gap-2\" @submit.prevent=\"addInvite\">\n        <Input\n          v-model=\"inviteDraft\"\n          type=\"email\"\n          placeholder=\"teammate@company.com\"\n          :status=\"inviteInvalid ? 'error' : undefined\"\n          class=\"flex-1\"\n          @input=\"inviteInvalid = false\"\n        />\n        <Button type=\"submit\" variant=\"outline\" size=\"sm\" class=\"shrink-0\">\n          <Plus aria-hidden=\"true\" />\n          Add\n        </Button>\n      </form>\n      <p v-if=\"inviteInvalid\" role=\"alert\" class=\"text-destructive text-xs\">Enter a valid email address.</p>\n      <div v-if=\"invites.length > 0\" class=\"flex flex-wrap gap-2\">\n        <Badge v-for=\"email in invites\" :key=\"email\" variant=\"secondary\" class=\"gap-1 py-1 pr-1 pl-2.5\">\n          {{ email }}\n          <button\n            type=\"button\"\n            :aria-label=\"`Remove ${email}`\"\n            class=\"hover:bg-foreground/10 focus-visible:ring-ring min-h-6 rounded-full p-0.5 transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n            @click=\"removeInvite(email)\"\n          >\n            <X class=\"size-3\" aria-hidden=\"true\" />\n          </button>\n        </Badge>\n      </div>\n      <p v-else class=\"text-muted-foreground text-xs\">No teammates invited yet.</p>\n      <button\n        type=\"button\"\n        class=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring min-h-6 text-xs underline-offset-4 transition-colors hover:underline focus-visible:ring-2 focus-visible:outline-none\"\n        @click=\"goNext\"\n      >\n        Skip for now\n      </button>\n    </div>\n\n    <!-- Step 3 · Preferences -->\n    <div v-else-if=\"step === 3\" class=\"divide-y rounded-lg border\">\n      <div v-for=\"pref in preferences\" :key=\"pref.id\" class=\"flex items-center justify-between gap-4 p-4\">\n        <div class=\"min-w-0\">\n          <p class=\"text-sm font-medium\">{{ pref.label }}</p>\n          <p class=\"text-muted-foreground mt-0.5 text-xs\">{{ pref.description }}</p>\n        </div>\n        <Switch v-model=\"pref.enabled\" :aria-label=\"pref.label\" />\n      </div>\n    </div>\n\n    <!-- Step 4 · Done -->\n    <div v-else class=\"space-y-4 py-6 text-center\">\n      <div\n        class=\"border-success/30 bg-success/10 text-success mx-auto flex size-16 items-center justify-center rounded-full border shadow-xs\"\n      >\n        <Check class=\"size-8\" aria-hidden=\"true\" />\n      </div>\n      <div>\n        <p class=\"text-lg font-semibold\">You're all set</p>\n        <p class=\"text-muted-foreground mx-auto mt-1 max-w-sm text-sm\">\n          <template v-if=\"invites.length > 0\">\n            {{ workspaceName || 'Your workspace' }} is ready and {{ invites.length }} invitation{{\n              invites.length === 1 ? '' : 's'\n            }}\n            sent.\n          </template>\n          <template v-else\n            >{{ workspaceName || 'Your workspace' }} is ready — invite teammates anytime from settings.</template\n          >\n        </p>\n      </div>\n      <Button variant=\"outline\" size=\"sm\" @click=\"reset\">Set up another workspace</Button>\n    </div>\n\n    <template #footer>\n      <div class=\"flex w-full items-center justify-between\">\n        <Button v-if=\"step > 1 && step < 4\" variant=\"ghost\" size=\"sm\" @click=\"step -= 1\">\n          <ChevronLeft aria-hidden=\"true\" />\n          Back\n        </Button>\n        <span v-else aria-hidden=\"true\"></span>\n        <span class=\"text-muted-foreground text-xs\">Step {{ step }} of 4</span>\n        <Button v-if=\"step < 4\" size=\"sm\" :disabled=\"step === 1 && workspaceName.trim() === ''\" @click=\"goNext\">\n          {{ step === 3 ? 'Finish' : 'Continue' }}\n          <ChevronRight aria-hidden=\"true\" />\n        </Button>\n        <span v-else aria-hidden=\"true\"></span>\n      </div>\n    </template>\n  </SectionCard>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/OnboardingWizard.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/input.json",
    "https://uipkge.dev/r/vue/section-card.json",
    "https://uipkge.dev/r/vue/stepper.json",
    "https://uipkge.dev/r/vue/switch.json"
  ],
  "description": "Multi-step workspace setup flow in a SectionCard: a four-step Stepper (Workspace → Team → Preferences → Done) with check icons on completed steps, a workspace-name input with live URL slug preview, teammate invites as removable email chips with a skip link, preference Switch rows, and a success panel with a glowing check circle. Footer carries Back/Continue actions plus a Step X of 4 counter. Data is stubbed inline — wire your API of choice.",
  "categories": [
    "auth",
    "app",
    "onboarding"
  ]
}