{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "auth-recovery",
  "title": "Auth Recovery",
  "description": "The account-recovery flow as composable cards — forgotten-password request, check-your-email confirmation, new-password reset and an OTP verification step — sharing one idiom with a back-to-login link.",
  "dependencies": [
    "@untitledui/icons@^0.0.22"
  ],
  "registryDependencies": [
    "@nx-ui/auth-signup",
    "@nx-ui/button",
    "@nx-ui/featured-icon",
    "@nx-ui/field",
    "@nx-ui/input",
    "@nx-ui/input-otp",
    "@nx-ui/nx-logo",
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/auth-recovery.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowNarrowLeft, Mail01 } from \"@untitledui/icons\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Field,\n  FieldError,\n  FieldGroup,\n  FieldLabel,\n} from \"@/components/ui/field\"\nimport { FeaturedIcon } from \"@/components/blocks/featured-icon\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  InputOTP,\n  InputOTPGroup,\n  InputOTPSlot,\n} from \"@/components/ui/input-otp\"\nimport { NxLogo } from \"@/components/blocks/nx-logo\"\nimport { PasswordStrengthMeter } from \"@/components/blocks/auth-signup\"\n\n/**\n * auth-recovery — the account-recovery flow, one small card component per\n * step: `ForgotPasswordForm` (enter email), `CheckEmailCard` (confirmation +\n * resend), `ResetPasswordForm` (new password + confirm, with the signup\n * strength meter) and `VerifyOtpForm` (6-digit code via InputOTP). Each\n * shares the same centred-card idiom and a \"back to login\" link, and is\n * driven entirely by callback props.\n */\n\nfunction BackToLogin({\n  onBack,\n  label = \"Back to log in\",\n}: {\n  onBack?: () => void\n  label?: string\n}) {\n  return (\n    <Button\n      type=\"button\"\n      variant=\"link-gray\"\n      size=\"sm\"\n      onClick={onBack}\n      className=\"mx-auto\"\n    >\n      <ArrowNarrowLeft className=\"size-4\" />\n      {label}\n    </Button>\n  )\n}\n\nfunction RecoveryHeader({\n  icon,\n  title,\n  description,\n}: {\n  icon?: React.ReactNode\n  title: React.ReactNode\n  description?: React.ReactNode\n}) {\n  return (\n    <div className=\"flex flex-col items-center gap-4 text-center\">\n      {icon}\n      <div className=\"flex flex-col gap-1\">\n        <h1 className=\"text-display-xs font-semibold text-foreground\">{title}</h1>\n        {description ? (\n          <p className=\"text-sm text-muted-foreground\">{description}</p>\n        ) : null}\n      </div>\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// ForgotPasswordForm\n// ---------------------------------------------------------------------------\n\nfunction ForgotPasswordForm({\n  className,\n  logo = <NxLogo variant=\"lockup\" className=\"h-8\" />,\n  onSubmit,\n  onBack,\n  error,\n  loading = false,\n}: {\n  className?: string\n  logo?: React.ReactNode\n  onSubmit?: (values: { email: string }) => void\n  onBack?: () => void\n  error?: string\n  loading?: boolean\n}) {\n  function handleSubmit(event: React.FormEvent<HTMLFormElement>) {\n    event.preventDefault()\n    const data = new FormData(event.currentTarget)\n    onSubmit?.({ email: String(data.get(\"email\") ?? \"\") })\n  }\n\n  return (\n    <div className={cn(\"flex w-full max-w-sm flex-col gap-6\", className)}>\n      <div className=\"flex flex-col items-center gap-4 text-center\">\n        {logo}\n      </div>\n      <RecoveryHeader\n        title=\"Forgot password?\"\n        description=\"No worries, we'll send you reset instructions.\"\n      />\n\n      <form onSubmit={handleSubmit} noValidate>\n        <FieldGroup>\n          <Field data-invalid={!!error}>\n            <FieldLabel htmlFor=\"forgot-email\">Email</FieldLabel>\n            <Input\n              id=\"forgot-email\"\n              name=\"email\"\n              type=\"email\"\n              autoComplete=\"email\"\n              placeholder=\"you@company.com\"\n              aria-invalid={!!error}\n              required\n            />\n            <FieldError>{error}</FieldError>\n          </Field>\n\n          <Button type=\"submit\" className=\"w-full\" disabled={loading}>\n            {loading ? \"Sending…\" : \"Reset password\"}\n          </Button>\n        </FieldGroup>\n      </form>\n\n      <BackToLogin onBack={onBack} />\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// CheckEmailCard\n// ---------------------------------------------------------------------------\n\nfunction CheckEmailCard({\n  className,\n  email,\n  onResend,\n  onBack,\n  resending = false,\n}: {\n  className?: string\n  email?: string\n  onResend?: () => void\n  onBack?: () => void\n  resending?: boolean\n}) {\n  return (\n    <div className={cn(\"flex w-full max-w-sm flex-col gap-6\", className)}>\n      <RecoveryHeader\n        icon={<FeaturedIcon icon={Mail01} color=\"brand\" theme=\"modern\" />}\n        title=\"Check your email\"\n        description={\n          email ? (\n            <>\n              We sent a password reset link to{\" \"}\n              <span className=\"font-medium text-foreground\">{email}</span>\n            </>\n          ) : (\n            \"We sent a password reset link to your inbox.\"\n          )\n        }\n      />\n\n      <Button type=\"button\" variant=\"secondary\" className=\"w-full\" onClick={onResend}>\n        {resending ? \"Resending…\" : \"Resend email\"}\n      </Button>\n\n      <BackToLogin onBack={onBack} />\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// ResetPasswordForm\n// ---------------------------------------------------------------------------\n\nfunction ResetPasswordForm({\n  className,\n  onSubmit,\n  onBack,\n  errors,\n  loading = false,\n}: {\n  className?: string\n  onSubmit?: (values: { password: string; confirmPassword: string }) => void\n  onBack?: () => void\n  errors?: Partial<Record<\"password\" | \"confirmPassword\", string>>\n  loading?: boolean\n}) {\n  const [password, setPassword] = React.useState(\"\")\n  const [confirmPassword, setConfirmPassword] = React.useState(\"\")\n\n  function handleSubmit(event: React.FormEvent<HTMLFormElement>) {\n    event.preventDefault()\n    onSubmit?.({ password, confirmPassword })\n  }\n\n  return (\n    <div className={cn(\"flex w-full max-w-sm flex-col gap-6\", className)}>\n      <RecoveryHeader\n        title=\"Set new password\"\n        description=\"Your new password must be different from previously used passwords.\"\n      />\n\n      <form onSubmit={handleSubmit} noValidate>\n        <FieldGroup>\n          <Field data-invalid={!!errors?.password}>\n            <FieldLabel htmlFor=\"reset-password\">New password</FieldLabel>\n            <Input\n              id=\"reset-password\"\n              name=\"password\"\n              type=\"password\"\n              autoComplete=\"new-password\"\n              placeholder=\"••••••••\"\n              value={password}\n              onChange={(event) => setPassword(event.target.value)}\n              aria-invalid={!!errors?.password}\n              required\n            />\n            <FieldError>{errors?.password}</FieldError>\n            <PasswordStrengthMeter value={password} />\n          </Field>\n\n          <Field data-invalid={!!errors?.confirmPassword}>\n            <FieldLabel htmlFor=\"reset-confirm-password\">Confirm password</FieldLabel>\n            <Input\n              id=\"reset-confirm-password\"\n              name=\"confirmPassword\"\n              type=\"password\"\n              autoComplete=\"new-password\"\n              placeholder=\"••••••••\"\n              value={confirmPassword}\n              onChange={(event) => setConfirmPassword(event.target.value)}\n              aria-invalid={!!errors?.confirmPassword}\n              required\n            />\n            <FieldError>{errors?.confirmPassword}</FieldError>\n          </Field>\n\n          <Button type=\"submit\" className=\"w-full\" disabled={loading}>\n            {loading ? \"Resetting…\" : \"Reset password\"}\n          </Button>\n        </FieldGroup>\n      </form>\n\n      <BackToLogin onBack={onBack} />\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// VerifyOtpForm\n// ---------------------------------------------------------------------------\n\nfunction VerifyOtpForm({\n  className,\n  email,\n  length = 6,\n  onSubmit,\n  onResend,\n  onBack,\n  error,\n  loading = false,\n  resending = false,\n}: {\n  className?: string\n  email?: string\n  length?: number\n  onSubmit?: (values: { code: string }) => void\n  onResend?: () => void\n  onBack?: () => void\n  error?: string\n  loading?: boolean\n  resending?: boolean\n}) {\n  const [code, setCode] = React.useState(\"\")\n  const otpId = React.useId()\n\n  function handleSubmit(event: React.FormEvent<HTMLFormElement>) {\n    event.preventDefault()\n    onSubmit?.({ code })\n  }\n\n  return (\n    <div className={cn(\"flex w-full max-w-sm flex-col gap-6\", className)}>\n      <RecoveryHeader\n        title=\"Verify your email\"\n        description={\n          email ? (\n            <>\n              Enter the {length}-digit code we sent to{\" \"}\n              <span className=\"font-medium text-foreground\">{email}</span>\n            </>\n          ) : (\n            `Enter the ${length}-digit code we sent to your email.`\n          )\n        }\n      />\n\n      <form onSubmit={handleSubmit} noValidate>\n        <FieldGroup>\n          <Field data-invalid={!!error} className=\"items-center\">\n            <InputOTP\n              id={otpId}\n              name=\"code\"\n              maxLength={length}\n              value={code}\n              onChange={setCode}\n              aria-label=\"Verification code\"\n              containerClassName=\"mx-auto\"\n            >\n              <InputOTPGroup>\n                {Array.from({ length }, (_, index) => (\n                  <InputOTPSlot key={index} index={index} />\n                ))}\n              </InputOTPGroup>\n            </InputOTP>\n            <FieldError className=\"text-center\">{error}</FieldError>\n          </Field>\n\n          <Button type=\"submit\" className=\"w-full\" disabled={loading}>\n            {loading ? \"Verifying…\" : \"Verify\"}\n          </Button>\n        </FieldGroup>\n      </form>\n\n      <p className=\"text-center text-sm text-muted-foreground\">\n        Didn&rsquo;t get a code?{\" \"}\n        <Button type=\"button\" variant=\"link-color\" size=\"sm\" onClick={onResend}>\n          {resending ? \"Resending…\" : \"Resend\"}\n        </Button>\n      </p>\n\n      <BackToLogin onBack={onBack} />\n    </div>\n  )\n}\n\nexport {\n  ForgotPasswordForm,\n  CheckEmailCard,\n  ResetPasswordForm,\n  VerifyOtpForm,\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}