{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "auth-login",
  "title": "Auth Login",
  "description": "The sign-in surface — a LoginForm card (email, password, remember me, forgot-password link, social sign-in, sign-up prompt) plus split-screen and centred-card page layouts.",
  "registryDependencies": [
    "@nx-ui/button",
    "@nx-ui/checkbox",
    "@nx-ui/field",
    "@nx-ui/input",
    "@nx-ui/label",
    "@nx-ui/nx-logo",
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/auth-login.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { Checkbox } from \"@/components/ui/checkbox\"\nimport {\n  Field,\n  FieldContent,\n  FieldError,\n  FieldGroup,\n  FieldLabel,\n  FieldSeparator,\n} from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport { Label } from \"@/components/ui/label\"\nimport { NxLogo } from \"@/components/blocks/nx-logo\"\n\n/**\n * auth-login — the sign-in surface: a `LoginForm` card (email + password,\n * remember me, forgot-password link, social sign-in row, sign-up prompt)\n * plus two page-level wrappers that place it on the screen: `AuthLayout`\n * (split screen, form left / brand panel right) and `AuthCenteredLayout`\n * (a single centred card). Purely visual — every side effect is a callback\n * prop, there is no wired-up auth behind this.\n */\n\n// ---------------------------------------------------------------------------\n// Social provider row\n// ---------------------------------------------------------------------------\n\ntype SocialProvider = \"google\" | \"microsoft\" | \"apple\"\n\nfunction GoogleGlyph(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" {...props}>\n      <path\n        fill=\"#4285F4\"\n        d=\"M23.52 12.273c0-.851-.076-1.67-.218-2.455H12v4.645h6.458c-.28 1.5-1.128 2.77-2.402 3.622v3.010h3.887c2.274-2.093 3.585-5.176 3.585-8.822Z\"\n      />\n      <path\n        fill=\"#34A853\"\n        d=\"M12 24c3.24 0 5.956-1.075 7.944-2.905l-3.887-3.01c-1.076.722-2.452 1.15-4.057 1.15-3.122 0-5.767-2.108-6.712-4.943H1.27v3.106C3.247 21.31 7.31 24 12 24Z\"\n      />\n      <path\n        fill=\"#FBBC05\"\n        d=\"M5.288 14.292A7.203 7.203 0 0 1 4.909 12c0-.796.137-1.57.38-2.292V6.602H1.27A11.996 11.996 0 0 0 0 12c0 1.936.464 3.767 1.27 5.398l4.018-3.106Z\"\n      />\n      <path\n        fill=\"#EA4335\"\n        d=\"M12 4.766c1.762 0 3.344.606 4.59 1.795l3.444-3.444C17.95 1.19 15.236 0 12 0 7.31 0 3.247 2.69 1.27 6.602l4.018 3.106C6.233 6.873 8.878 4.766 12 4.766Z\"\n      />\n    </svg>\n  )\n}\n\nfunction MicrosoftGlyph(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" {...props}>\n      <path fill=\"#F25022\" d=\"M1 1h10.4v10.4H1z\" />\n      <path fill=\"#7FBA00\" d=\"M12.6 1H23v10.4H12.6z\" />\n      <path fill=\"#00A4EF\" d=\"M1 12.6h10.4V23H1z\" />\n      <path fill=\"#FFB900\" d=\"M12.6 12.6H23V23H12.6z\" />\n    </svg>\n  )\n}\n\nfunction AppleGlyph(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n      <path d=\"M16.365 1.43c0 1.14-.415 2.208-1.243 3.166-.988 1.13-2.19 1.788-3.494 1.68-.132-1.13.417-2.313 1.203-3.166.897-.996 2.42-1.723 3.534-1.68ZM20.5 17.06c-.53 1.226-.79 1.774-1.475 2.86-.955 1.512-2.304 3.396-3.973 3.41-1.484.014-1.865-.964-3.877-.953-2.012.011-2.43.973-3.916.958-1.669-.017-2.947-1.72-3.902-3.232C.71 16.71-.166 11.55 1.02 8.176c.83-2.38 2.6-3.87 4.47-3.9 1.53-.026 2.972 1.033 3.905 1.033.933 0 2.68-1.278 4.52-1.09.77.032 2.93.31 4.32 2.352-.112.07-2.58 1.51-2.55 4.502.03 3.574 3.135 4.766 3.17 4.782-.026.086-.5 1.71-.955 2.205Z\" />\n    </svg>\n  )\n}\n\nconst SOCIAL_PROVIDERS: Record<\n  SocialProvider,\n  { label: string; glyph: (props: React.SVGProps<SVGSVGElement>) => React.JSX.Element }\n> = {\n  google: { label: \"Google\", glyph: GoogleGlyph },\n  microsoft: { label: \"Microsoft\", glyph: MicrosoftGlyph },\n  apple: { label: \"Apple\", glyph: AppleGlyph },\n}\n\nfunction SocialButtons({\n  providers,\n  onSocialSignIn,\n}: {\n  providers: SocialProvider[]\n  onSocialSignIn?: (provider: SocialProvider) => void\n}) {\n  if (!providers.length) return null\n\n  return (\n    <div\n      className={cn(\n        \"grid gap-3\",\n        providers.length === 1 && \"grid-cols-1\",\n        providers.length === 2 && \"grid-cols-2\",\n        providers.length >= 3 && \"grid-cols-3\"\n      )}\n    >\n      {providers.map((provider) => {\n        const { label, glyph: Glyph } = SOCIAL_PROVIDERS[provider]\n        return (\n          <Button\n            key={provider}\n            type=\"button\"\n            variant=\"outline\"\n            className=\"w-full\"\n            onClick={() => onSocialSignIn?.(provider)}\n          >\n            <Glyph className=\"size-4\" />\n            {providers.length === 1 ? `Continue with ${label}` : null}\n            <span className=\"sr-only\">{providers.length > 1 ? label : null}</span>\n          </Button>\n        )\n      })}\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// LoginForm\n// ---------------------------------------------------------------------------\n\ntype LoginFormValues = {\n  email: string\n  password: string\n  remember: boolean\n}\n\nfunction LoginForm({\n  className,\n  logo = <NxLogo variant=\"lockup\" className=\"h-8\" />,\n  title = \"Log in to your account\",\n  description = \"Welcome back — please enter your details.\",\n  socialProviders = [\"google\", \"microsoft\", \"apple\"],\n  onSubmit,\n  onSocialSignIn,\n  onForgotPassword,\n  onSignUp,\n  errors,\n  loading = false,\n}: {\n  className?: string\n  /** Logo slot shown above the title, defaults to the Nexus ReGen lockup. */\n  logo?: React.ReactNode\n  title?: React.ReactNode\n  description?: React.ReactNode\n  /** Which social sign-in buttons to render, in order. Empty array hides the row. */\n  socialProviders?: SocialProvider[]\n  onSubmit?: (values: LoginFormValues) => void\n  onSocialSignIn?: (provider: SocialProvider) => void\n  onForgotPassword?: () => void\n  onSignUp?: () => void\n  errors?: Partial<Record<\"email\" | \"password\", string>>\n  loading?: boolean\n}) {\n  const [remember, setRemember] = React.useState(false)\n  const fieldId = React.useId()\n  const emailId = `${fieldId}-email`\n  const passwordId = `${fieldId}-password`\n  const rememberId = `${fieldId}-remember`\n\n  function handleSubmit(event: React.FormEvent<HTMLFormElement>) {\n    event.preventDefault()\n    const data = new FormData(event.currentTarget)\n    onSubmit?.({\n      email: String(data.get(\"email\") ?? \"\"),\n      password: String(data.get(\"password\") ?? \"\"),\n      remember,\n    })\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 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      <form onSubmit={handleSubmit} noValidate>\n        <FieldGroup>\n          <Field data-invalid={!!errors?.email}>\n            <FieldLabel htmlFor={emailId}>Email</FieldLabel>\n            <Input\n              id={emailId}\n              name=\"email\"\n              type=\"email\"\n              autoComplete=\"email\"\n              placeholder=\"you@company.com\"\n              aria-invalid={!!errors?.email}\n              required\n            />\n            <FieldError>{errors?.email}</FieldError>\n          </Field>\n\n          <Field data-invalid={!!errors?.password}>\n            <div className=\"flex items-center justify-between gap-2\">\n              <FieldLabel htmlFor={passwordId}>Password</FieldLabel>\n              <Button\n                type=\"button\"\n                variant=\"link-color\"\n                size=\"sm\"\n                onClick={onForgotPassword}\n              >\n                Forgot password?\n              </Button>\n            </div>\n            <Input\n              id={passwordId}\n              name=\"password\"\n              type=\"password\"\n              autoComplete=\"current-password\"\n              placeholder=\"••••••••\"\n              aria-invalid={!!errors?.password}\n              required\n            />\n            <FieldError>{errors?.password}</FieldError>\n          </Field>\n\n          <Field orientation=\"horizontal\">\n            <Checkbox\n              id={rememberId}\n              checked={remember}\n              onCheckedChange={(checked) => setRemember(checked === true)}\n            />\n            <FieldContent>\n              <Label htmlFor={rememberId}>Remember me for 30 days</Label>\n            </FieldContent>\n          </Field>\n\n          <Button type=\"submit\" className=\"w-full\" disabled={loading}>\n            {loading ? \"Logging in…\" : \"Log in\"}\n          </Button>\n\n          {socialProviders.length ? (\n            <>\n              <FieldSeparator>Or continue with</FieldSeparator>\n              <SocialButtons\n                providers={socialProviders}\n                onSocialSignIn={onSocialSignIn}\n              />\n            </>\n          ) : null}\n        </FieldGroup>\n      </form>\n\n      <p className=\"text-center text-sm text-muted-foreground\">\n        Don&rsquo;t have an account?{\" \"}\n        <Button type=\"button\" variant=\"link-color\" size=\"sm\" onClick={onSignUp}>\n          Sign up\n        </Button>\n      </p>\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// AuthLayout — split screen (form left, brand panel right)\n// ---------------------------------------------------------------------------\n\nfunction AuthLayout({\n  children,\n  testimonial,\n  panelClassName,\n  className,\n}: {\n  children: React.ReactNode\n  /** Optional quote/testimonial slot shown at the base of the brand panel. */\n  testimonial?: React.ReactNode\n  panelClassName?: string\n  className?: string\n}) {\n  return (\n    <div className={cn(\"grid min-h-screen w-full lg:grid-cols-2\", className)}>\n      <div className=\"flex items-center justify-center p-6 sm:p-10\">{children}</div>\n\n      <div\n        className={cn(\n          \"relative hidden overflow-hidden bg-brand-600 lg:flex lg:flex-col lg:justify-end lg:p-12\",\n          panelClassName\n        )}\n      >\n        <div\n          aria-hidden=\"true\"\n          className=\"absolute inset-0 bg-[radial-gradient(circle_at_20%_20%,rgba(247,236,51,0.16),transparent_55%),radial-gradient(circle_at_80%_75%,rgba(255,255,255,0.08),transparent_45%)]\"\n        />\n        <NxLogo\n          variant=\"lockup\"\n          monochrome\n          className=\"relative z-10 h-8 text-white\"\n        />\n        {testimonial ? (\n          <div className=\"relative z-10 mt-auto pt-16 text-white\">{testimonial}</div>\n        ) : null}\n      </div>\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// AuthCenteredLayout — single centred card\n// ---------------------------------------------------------------------------\n\nfunction AuthCenteredLayout({\n  children,\n  className,\n}: {\n  children: React.ReactNode\n  className?: string\n}) {\n  return (\n    <div\n      className={cn(\n        \"flex min-h-screen w-full items-center justify-center bg-muted/40 p-6\",\n        className\n      )}\n    >\n      <div className=\"w-full max-w-sm rounded-2xl border border-border bg-card p-8 shadow-lg\">\n        {children}\n      </div>\n    </div>\n  )\n}\n\nexport {\n  LoginForm,\n  AuthLayout,\n  AuthCenteredLayout,\n  SocialButtons,\n  type SocialProvider,\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}