{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "settings-security",
  "title": "Settings Security",
  "description": "Manage account security, 2FA, and active sessions.",
  "registryDependencies": [
    "alert-dialog",
    "badge",
    "button",
    "card",
    "dialog",
    "field",
    "input-group",
    "separator"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/settings/settings-security.tsx",
      "content": "\"use client\";\n\nimport {\n  AlertTriangle,\n  Check,\n  Clock,\n  Eye,\n  EyeOff,\n  Key,\n  Loader2,\n  Lock,\n  MapPin,\n  Monitor,\n  Shield,\n  Smartphone,\n  Trash2,\n} from \"lucide-react\";\nimport { useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  AlertDialog,\n  AlertDialogAction,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n  AlertDialogTrigger,\n} from \"@/registry/new-york/ui/alert-dialog\";\nimport { Badge } from \"@/registry/new-york/ui/badge\";\nimport { Button } from \"@/registry/new-york/ui/button\";\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/registry/new-york/ui/card\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"@/registry/new-york/ui/dialog\";\nimport {\n  Field,\n  FieldContent,\n  FieldDescription,\n  FieldError,\n  FieldLabel,\n} from \"@/registry/new-york/ui/field\";\nimport {\n  InputGroup,\n  InputGroupInput,\n} from \"@/registry/new-york/ui/input-group\";\nimport { Separator } from \"@/registry/new-york/ui/separator\";\n\nexport interface SecuritySession {\n  id: string;\n  device: string;\n  location: string;\n  ipAddress: string;\n  lastActive: Date;\n  current: boolean;\n}\n\nexport interface SecurityEvent {\n  id: string;\n  type: \"login\" | \"password_change\" | \"2fa_enabled\" | \"2fa_disabled\" | \"logout\";\n  description: string;\n  ipAddress: string;\n  location: string;\n  timestamp: Date;\n  status: \"success\" | \"failed\" | \"suspicious\";\n}\n\nexport interface SettingsSecurityProps {\n  twoFactorEnabled?: boolean;\n  sessions?: SecuritySession[];\n  securityHistory?: SecurityEvent[];\n  onPasswordChange?: (\n    currentPassword: string,\n    newPassword: string\n  ) => Promise<void>;\n  onEnable2FA?: () => Promise<void>;\n  onDisable2FA?: () => Promise<void>;\n  onRevokeSession?: (sessionId: string) => Promise<void>;\n  onRevokeAllSessions?: () => Promise<void>;\n  onGenerateBackupCodes?: () => Promise<string[]>;\n  className?: string;\n}\n\nfunction formatDate(date: Date): string {\n  return new Intl.DateTimeFormat(\"en-US\", {\n    month: \"short\",\n    day: \"numeric\",\n    year: \"numeric\",\n    hour: \"numeric\",\n    minute: \"2-digit\",\n  }).format(date);\n}\n\nfunction formatRelativeTime(date: Date): string {\n  const now = Date.now();\n  const diff = now - date.getTime();\n  const minutes = Math.floor(diff / 60_000);\n  const hours = Math.floor(minutes / 60);\n  const days = Math.floor(hours / 24);\n\n  if (minutes < 1) return \"Just now\";\n  if (minutes < 60) return `${minutes}m ago`;\n  if (hours < 24) return `${hours}h ago`;\n  if (days < 7) return `${days}d ago`;\n  return formatDate(date);\n}\n\nexport default function SettingsSecurity({\n  twoFactorEnabled = false,\n  sessions = [],\n  securityHistory = [],\n  onPasswordChange,\n  onEnable2FA,\n  onDisable2FA,\n  onRevokeSession,\n  onRevokeAllSessions,\n  onGenerateBackupCodes,\n  className,\n}: SettingsSecurityProps) {\n  const [isChangingPassword, setIsChangingPassword] = useState(false);\n  const [isRevoking, setIsRevoking] = useState<string | null>(null);\n  const [passwordDialogOpen, setPasswordDialogOpen] = useState(false);\n  const [backupCodesDialogOpen, setBackupCodesDialogOpen] = useState(false);\n  const [showBackupCodes, setShowBackupCodes] = useState(false);\n  const [errors, setErrors] = useState<Record<string, string>>({});\n  const [backupCodes, setBackupCodes] = useState<string[]>([]);\n\n  const [passwordData, setPasswordData] = useState({\n    current: \"\",\n    new: \"\",\n    confirm: \"\",\n  });\n\n  const handlePasswordChange = async () => {\n    setErrors({});\n\n    if (!passwordData.current.trim()) {\n      setErrors({ current: \"Current password is required\" });\n      return;\n    }\n\n    if (!passwordData.new.trim()) {\n      setErrors({ new: \"New password is required\" });\n      return;\n    }\n\n    if (passwordData.new.length < 8) {\n      setErrors({ new: \"Password must be at least 8 characters\" });\n      return;\n    }\n\n    if (passwordData.new !== passwordData.confirm) {\n      setErrors({ confirm: \"Passwords do not match\" });\n      return;\n    }\n\n    setIsChangingPassword(true);\n    try {\n      await onPasswordChange?.(passwordData.current, passwordData.new);\n      setPasswordData({ current: \"\", new: \"\", confirm: \"\" });\n      setPasswordDialogOpen(false);\n    } catch (error) {\n      setErrors({\n        _general:\n          error instanceof Error ? error.message : \"Failed to change password\",\n      });\n    } finally {\n      setIsChangingPassword(false);\n    }\n  };\n\n  const handleGenerateBackupCodes = async () => {\n    try {\n      const codes = await onGenerateBackupCodes?.();\n      if (codes) {\n        setBackupCodes(codes);\n        setBackupCodesDialogOpen(true);\n      }\n    } catch (error) {\n      setErrors({\n        backupCodes:\n          error instanceof Error\n            ? error.message\n            : \"Failed to generate backup codes\",\n      });\n    }\n  };\n\n  const getEventIcon = (type: SecurityEvent[\"type\"]) => {\n    switch (type) {\n      case \"login\":\n        return Lock;\n      case \"password_change\":\n        return Key;\n      case \"2fa_enabled\":\n      case \"2fa_disabled\":\n        return Shield;\n      default:\n        return Clock;\n    }\n  };\n\n  const getStatusBadge = (status: SecurityEvent[\"status\"]) => {\n    switch (status) {\n      case \"success\":\n        return (\n          <Badge className=\"text-xs\" variant=\"default\">\n            Success\n          </Badge>\n        );\n      case \"failed\":\n        return (\n          <Badge className=\"text-xs\" variant=\"destructive\">\n            Failed\n          </Badge>\n        );\n      case \"suspicious\":\n        return (\n          <Badge\n            className=\"flex items-center gap-1 text-xs\"\n            variant=\"destructive\"\n          >\n            <AlertTriangle className=\"size-3\" />\n            <span>Suspicious</span>\n          </Badge>\n        );\n    }\n  };\n\n  return (\n    <Card className={cn(\"w-full shadow-xs\", className)}>\n      <CardHeader>\n        <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n          <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n            <CardTitle className=\"wrap-break-word\">Security</CardTitle>\n            <CardDescription className=\"wrap-break-word\">\n              Manage your account security and authentication\n            </CardDescription>\n          </div>\n        </div>\n      </CardHeader>\n      <CardContent>\n        <div className=\"flex flex-col gap-6\">\n          {/* Password & 2FA */}\n          <div className=\"grid gap-4 md:grid-cols-2\">\n            {/* Password Change */}\n            <div className=\"flex flex-col gap-4 rounded-lg border p-4\">\n              <div className=\"flex items-center gap-2\">\n                <div className=\"flex size-8 items-center justify-center rounded-lg bg-muted\">\n                  <Key className=\"size-4\" />\n                </div>\n                <FieldLabel className=\"mb-0\">Password</FieldLabel>\n              </div>\n              <Dialog\n                onOpenChange={setPasswordDialogOpen}\n                open={passwordDialogOpen}\n              >\n                <DialogTrigger asChild>\n                  <Button className=\"w-full\" type=\"button\" variant=\"outline\">\n                    Change Password\n                  </Button>\n                </DialogTrigger>\n                <DialogContent className=\"sm:max-w-md\">\n                  <DialogHeader>\n                    <DialogTitle>Change Password</DialogTitle>\n                    <DialogDescription>\n                      Enter your current password and choose a new one\n                    </DialogDescription>\n                  </DialogHeader>\n                  <div className=\"flex flex-col gap-4\">\n                    {errors._general && (\n                      <div className=\"rounded-lg border border-destructive/50 bg-destructive/10 p-3\">\n                        <p className=\"text-destructive text-sm\">\n                          {errors._general}\n                        </p>\n                      </div>\n                    )}\n\n                    <Field>\n                      <FieldLabel htmlFor=\"current-password\">\n                        Current Password{\" \"}\n                        <span className=\"text-destructive\">*</span>\n                      </FieldLabel>\n                      <FieldContent>\n                        <InputGroup>\n                          <InputGroupInput\n                            id=\"current-password\"\n                            onChange={(e) =>\n                              setPasswordData((prev) => ({\n                                ...prev,\n                                current: e.target.value,\n                              }))\n                            }\n                            type=\"password\"\n                            value={passwordData.current}\n                          />\n                        </InputGroup>\n                        {errors.current && (\n                          <FieldError>{errors.current}</FieldError>\n                        )}\n                      </FieldContent>\n                    </Field>\n\n                    <Field>\n                      <FieldLabel htmlFor=\"new-password\">\n                        New Password <span className=\"text-destructive\">*</span>\n                      </FieldLabel>\n                      <FieldContent>\n                        <InputGroup>\n                          <InputGroupInput\n                            id=\"new-password\"\n                            onChange={(e) =>\n                              setPasswordData((prev) => ({\n                                ...prev,\n                                new: e.target.value,\n                              }))\n                            }\n                            type=\"password\"\n                            value={passwordData.new}\n                          />\n                        </InputGroup>\n                        {errors.new && <FieldError>{errors.new}</FieldError>}\n                        <FieldDescription>\n                          Must be at least 8 characters long\n                        </FieldDescription>\n                      </FieldContent>\n                    </Field>\n\n                    <Field>\n                      <FieldLabel htmlFor=\"confirm-password\">\n                        Confirm Password{\" \"}\n                        <span className=\"text-destructive\">*</span>\n                      </FieldLabel>\n                      <FieldContent>\n                        <InputGroup>\n                          <InputGroupInput\n                            id=\"confirm-password\"\n                            onChange={(e) =>\n                              setPasswordData((prev) => ({\n                                ...prev,\n                                confirm: e.target.value,\n                              }))\n                            }\n                            type=\"password\"\n                            value={passwordData.confirm}\n                          />\n                        </InputGroup>\n                        {errors.confirm && (\n                          <FieldError>{errors.confirm}</FieldError>\n                        )}\n                      </FieldContent>\n                    </Field>\n                  </div>\n                  <DialogFooter>\n                    <Button\n                      onClick={() => setPasswordDialogOpen(false)}\n                      type=\"button\"\n                      variant=\"outline\"\n                    >\n                      Cancel\n                    </Button>\n                    <Button\n                      disabled={isChangingPassword}\n                      onClick={handlePasswordChange}\n                      type=\"button\"\n                    >\n                      {isChangingPassword ? (\n                        <>\n                          <Loader2 className=\"size-4 animate-spin\" />\n                          Changing…\n                        </>\n                      ) : (\n                        \"Change Password\"\n                      )}\n                    </Button>\n                  </DialogFooter>\n                </DialogContent>\n              </Dialog>\n            </div>\n\n            {/* Two-Factor Authentication */}\n            <div className=\"flex flex-col gap-4 rounded-lg border p-4\">\n              <div className=\"flex items-center gap-2\">\n                <div className=\"flex size-8 items-center justify-center rounded-lg bg-muted\">\n                  <Shield className=\"size-4\" />\n                </div>\n                <FieldLabel className=\"mb-0\">\n                  Two-Factor Authentication\n                </FieldLabel>\n              </div>\n              <div className=\"flex flex-col gap-3\">\n                {twoFactorEnabled ? (\n                  <>\n                    <div className=\"flex items-center gap-2\">\n                      <Badge\n                        className=\"flex items-center gap-1 text-xs\"\n                        variant=\"default\"\n                      >\n                        <Check className=\"size-3\" />\n                        <span>Enabled</span>\n                      </Badge>\n                    </div>\n                    <div className=\"flex flex-col gap-2\">\n                      <Button\n                        className=\"w-full\"\n                        onClick={handleGenerateBackupCodes}\n                        type=\"button\"\n                        variant=\"outline\"\n                      >\n                        View Backup Codes\n                      </Button>\n                      <AlertDialog>\n                        <AlertDialogTrigger asChild>\n                          <Button\n                            className=\"w-full\"\n                            type=\"button\"\n                            variant=\"destructive\"\n                          >\n                            Disable 2FA\n                          </Button>\n                        </AlertDialogTrigger>\n                        <AlertDialogContent>\n                          <AlertDialogHeader>\n                            <AlertDialogTitle>\n                              Disable Two-Factor Authentication?\n                            </AlertDialogTitle>\n                            <AlertDialogDescription>\n                              This will reduce the security of your account. You\n                              can re-enable it anytime.\n                            </AlertDialogDescription>\n                          </AlertDialogHeader>\n                          <AlertDialogFooter>\n                            <AlertDialogCancel>Cancel</AlertDialogCancel>\n                            <AlertDialogAction onClick={onDisable2FA}>\n                              Disable 2FA\n                            </AlertDialogAction>\n                          </AlertDialogFooter>\n                        </AlertDialogContent>\n                      </AlertDialog>\n                    </div>\n                  </>\n                ) : (\n                  <Button\n                    className=\"w-full\"\n                    onClick={onEnable2FA}\n                    type=\"button\"\n                  >\n                    <div className=\"flex items-center gap-2\">\n                      <Shield className=\"size-4\" />\n                      <span>Enable 2FA</span>\n                    </div>\n                  </Button>\n                )}\n              </div>\n            </div>\n          </div>\n\n          <Separator />\n\n          {/* Active Sessions */}\n          <div className=\"flex flex-col gap-4\">\n            <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n              <div className=\"flex items-center gap-2\">\n                <Monitor className=\"size-5 text-muted-foreground\" />\n                <h3 className=\"font-semibold text-base\">Active Sessions</h3>\n              </div>\n              {sessions.length > 1 && (\n                <AlertDialog>\n                  <AlertDialogTrigger asChild>\n                    <Button\n                      className=\"w-full sm:w-auto\"\n                      type=\"button\"\n                      variant=\"outline\"\n                    >\n                      Revoke All\n                    </Button>\n                  </AlertDialogTrigger>\n                  <AlertDialogContent>\n                    <AlertDialogHeader>\n                      <AlertDialogTitle>Revoke All Sessions?</AlertDialogTitle>\n                      <AlertDialogDescription>\n                        This will sign you out from all devices except this one.\n                      </AlertDialogDescription>\n                    </AlertDialogHeader>\n                    <AlertDialogFooter>\n                      <AlertDialogCancel>Cancel</AlertDialogCancel>\n                      <AlertDialogAction onClick={onRevokeAllSessions}>\n                        Revoke All\n                      </AlertDialogAction>\n                    </AlertDialogFooter>\n                  </AlertDialogContent>\n                </AlertDialog>\n              )}\n            </div>\n\n            {sessions.length === 0 ? (\n              <p className=\"text-muted-foreground text-sm\">\n                No active sessions\n              </p>\n            ) : (\n              <div className=\"flex flex-col gap-3\">\n                {sessions.map((session) => (\n                  <div\n                    className=\"flex flex-col gap-3 rounded-lg border p-4 sm:flex-row sm:items-center\"\n                    key={session.id}\n                  >\n                    <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n                      <div className=\"flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted\">\n                        {session.device.includes(\"Mobile\") ? (\n                          <Smartphone className=\"size-5\" />\n                        ) : (\n                          <Monitor className=\"size-5\" />\n                        )}\n                      </div>\n                      <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n                        <div className=\"flex flex-wrap items-center gap-2\">\n                          <span className=\"font-medium text-sm\">\n                            {session.device}\n                          </span>\n                          {session.current && (\n                            <Badge className=\"text-xs\" variant=\"default\">\n                              Current\n                            </Badge>\n                          )}\n                        </div>\n                        <div className=\"flex flex-wrap items-center gap-2 text-muted-foreground text-xs\">\n                          <span className=\"flex items-center gap-1\">\n                            <MapPin className=\"size-3\" />\n                            {session.location}\n                          </span>\n                          <span>•</span>\n                          <span>{session.ipAddress}</span>\n                          <span>•</span>\n                          <span>\n                            Active {formatRelativeTime(session.lastActive)}\n                          </span>\n                        </div>\n                      </div>\n                    </div>\n                    {!session.current && (\n                      <Button\n                        className=\"w-full sm:w-auto\"\n                        disabled={isRevoking === session.id}\n                        onClick={() => {\n                          setIsRevoking(session.id);\n                          onRevokeSession?.(session.id).finally(() =>\n                            setIsRevoking(null)\n                          );\n                        }}\n                        type=\"button\"\n                        variant=\"outline\"\n                      >\n                        {isRevoking === session.id ? (\n                          <>\n                            <Loader2 className=\"size-4 animate-spin\" />\n                            Revoking…\n                          </>\n                        ) : (\n                          <>\n                            <Trash2 className=\"size-4\" />\n                            Revoke\n                          </>\n                        )}\n                      </Button>\n                    )}\n                  </div>\n                ))}\n              </div>\n            )}\n          </div>\n\n          <Separator />\n\n          {/* Security History */}\n          <div className=\"flex flex-col gap-4\">\n            <div className=\"flex items-center gap-2\">\n              <Clock className=\"size-5 text-muted-foreground\" />\n              <h3 className=\"font-semibold text-base\">Security History</h3>\n            </div>\n\n            {securityHistory.length === 0 ? (\n              <p className=\"text-muted-foreground text-sm\">\n                No security events\n              </p>\n            ) : (\n              <div className=\"flex flex-col gap-3\">\n                {securityHistory.map((event) => {\n                  const Icon = getEventIcon(event.type);\n                  return (\n                    <div\n                      className=\"flex items-start gap-3 rounded-lg border p-4\"\n                      key={event.id}\n                    >\n                      <div className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted\">\n                        <Icon className=\"size-4\" />\n                      </div>\n                      <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n                        <div className=\"flex flex-wrap items-center gap-2\">\n                          <span className=\"font-medium text-sm\">\n                            {event.description}\n                          </span>\n                          {getStatusBadge(event.status)}\n                        </div>\n                        <div className=\"flex flex-wrap items-center gap-2 text-muted-foreground text-xs\">\n                          <span className=\"flex items-center gap-1\">\n                            <MapPin className=\"size-3\" />\n                            {event.location}\n                          </span>\n                          <span>•</span>\n                          <span>{event.ipAddress}</span>\n                          <span>•</span>\n                          <span>{formatDate(event.timestamp)}</span>\n                        </div>\n                      </div>\n                    </div>\n                  );\n                })}\n              </div>\n            )}\n          </div>\n        </div>\n      </CardContent>\n\n      {/* Backup Codes Dialog */}\n      <Dialog\n        onOpenChange={setBackupCodesDialogOpen}\n        open={backupCodesDialogOpen}\n      >\n        <DialogContent className=\"sm:max-w-md\">\n          <DialogHeader>\n            <DialogTitle>Backup Codes</DialogTitle>\n            <DialogDescription>\n              Save these codes in a safe place. You can use them to access your\n              account if you lose access to your authenticator app.\n            </DialogDescription>\n          </DialogHeader>\n          <div className=\"flex flex-col gap-4\">\n            <div className=\"grid grid-cols-2 gap-2 rounded-lg border bg-muted/30 p-4\">\n              {backupCodes.map((code, index) => (\n                <code className=\"font-medium font-mono text-sm\" key={index}>\n                  {showBackupCodes ? code : \"•\".repeat(8)}\n                </code>\n              ))}\n            </div>\n            <Button\n              onClick={() => setShowBackupCodes(!showBackupCodes)}\n              type=\"button\"\n              variant=\"outline\"\n            >\n              {showBackupCodes ? (\n                <div className=\"flex items-center gap-2\">\n                  <EyeOff className=\"size-4\" />\n                  <span>Hide Codes</span>\n                </div>\n              ) : (\n                <div className=\"flex items-center gap-2\">\n                  <Eye className=\"size-4\" />\n                  <span>Show Codes</span>\n                </div>\n              )}\n            </Button>\n          </div>\n          <DialogFooter>\n            <Button\n              onClick={() => setBackupCodesDialogOpen(false)}\n              type=\"button\"\n            >\n              Done\n            </Button>\n          </DialogFooter>\n        </DialogContent>\n      </Dialog>\n    </Card>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "settings"
  ],
  "type": "registry:ui"
}