{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "auth-mfa",
  "title": "Auth Mfa",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/auth-mfa/AuthMfa.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { ShieldCheck, RotateCw } from 'lucide-vue-next'\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Button } from '@/components/ui/button'\nimport { PinInput, PinInputGroup, PinInputSlot } from '@/components/ui/pin-input'\n\nconst props = withDefaults(\n  defineProps<{\n    title?: string\n    description?: string\n    continueHref?: string\n    recoveryHref?: string\n    demoCode?: string\n  }>(),\n  {\n    title: 'Two-step verification',\n    description: 'Enter the 6-digit code from your authenticator app.',\n    continueHref: '/',\n    recoveryHref: '#',\n    demoCode: '123456',\n  },\n)\n\nconst emit = defineEmits<{\n  (e: 'verify', code: string): void\n  (e: 'resend'): void\n  (e: 'continue'): void\n}>()\n\nconst code = ref<string[]>([])\nconst verifying = ref(false)\nconst verified = ref(false)\nconst error = ref('')\nconst resendIn = ref(0)\n\nconst joined = computed(() => code.value.join(''))\n\nwatch(joined, (val) => {\n  if (val.length === 6) {\n    error.value = ''\n    verifying.value = true\n    emit('verify', val)\n    setTimeout(() => {\n      verifying.value = false\n      if (val === props.demoCode) verified.value = true\n      else {\n        error.value = `Invalid code. Try ${props.demoCode} for the demo.`\n        code.value = []\n      }\n    }, 700)\n  }\n})\n\nfunction startResendCooldown() {\n  resendIn.value = 30\n  emit('resend')\n  const t = setInterval(() => {\n    resendIn.value -= 1\n    if (resendIn.value <= 0) clearInterval(t)\n  }, 1000)\n}\n</script>\n\n<template>\n  <div class=\"bg-background flex min-h-svh items-center justify-center p-6\">\n    <Card class=\"w-full max-w-sm\">\n      <template v-if=\"!verified\">\n        <CardHeader class=\"text-center\">\n          <div class=\"bg-primary/10 text-primary mx-auto mb-2 flex size-12 items-center justify-center rounded-full\">\n            <ShieldCheck class=\"size-6\" />\n          </div>\n          <CardTitle class=\"text-2xl\">{{ title }}</CardTitle>\n          <CardDescription>{{ description }}</CardDescription>\n        </CardHeader>\n        <CardContent class=\"space-y-4\">\n          <div class=\"flex justify-center\">\n            <PinInput v-model=\"code\" otp :disabled=\"verifying\">\n              <PinInputGroup>\n                <PinInputSlot v-for=\"i in 6\" :key=\"i\" :index=\"i - 1\" />\n              </PinInputGroup>\n            </PinInput>\n          </div>\n          <p v-if=\"verifying\" class=\"text-muted-foreground text-center text-sm\">Verifying…</p>\n          <p v-if=\"error\" class=\"text-destructive text-center text-sm\">{{ error }}</p>\n          <div class=\"text-center\">\n            <button\n              v-if=\"resendIn === 0\"\n              type=\"button\"\n              class=\"text-muted-foreground hover:text-foreground inline-flex items-center gap-1 text-xs underline-offset-4 hover:underline\"\n              @click=\"startResendCooldown\"\n            >\n              <RotateCw class=\"size-3\" />Resend code\n            </button>\n            <p v-else class=\"text-muted-foreground text-xs\">Resend available in {{ resendIn }}s</p>\n          </div>\n        </CardContent>\n        <CardFooter class=\"justify-center\">\n          <p class=\"text-muted-foreground text-xs\">\n            Lost your device?\n            <a :href=\"recoveryHref\" class=\"text-foreground underline-offset-4 hover:underline\">Use a recovery code</a>\n          </p>\n        </CardFooter>\n      </template>\n\n      <template v-else>\n        <CardContent class=\"space-y-4 pt-6 text-center\">\n          <div class=\"bg-success/10 text-success mx-auto flex size-12 items-center justify-center rounded-full\">\n            <ShieldCheck class=\"size-6\" />\n          </div>\n          <div class=\"space-y-1\">\n            <h3 class=\"text-lg font-semibold\">Verified</h3>\n            <p class=\"text-muted-foreground text-sm\">You're all set. Continuing to your dashboard…</p>\n          </div>\n          <a :href=\"continueHref\" @click=\"emit('continue')\"><Button class=\"w-full\">Continue</Button></a>\n        </CardContent>\n      </template>\n    </Card>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/AuthMfa.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/pin-input.json"
  ],
  "description": "Two-step verification surface. 6-digit OTP pin input auto-submits on entry, shows verifying state and an inline error on mismatch, has a 30-second resend cooldown, and swaps to a Verified card with a Continue button on success. Emits `verify` (code), `resend`, and `continue`. `demoCode` prop controls the value the built-in mock validator accepts.",
  "categories": [
    "auth"
  ]
}