"use client"; import { useCallback, useEffect, useState } from "react"; import { Alert, Button, CloseButton, Spinner } from "@heroui/react"; import { Fingerprint, TrashBin } from "@gravity-ui/icons"; import { authClient } from "@/lib/auth-client"; type Passkey = { id: string; name?: string | null; createdAt: Date | string; deviceType?: string | null; }; export default function SettingsPage() { const [passkeys, setPasskeys] = useState([]); const [loading, setLoading] = useState(true); const [adding, setAdding] = useState(false); const [deletingId, setDeletingId] = useState(null); const [error, setError] = useState(""); const [success, setSuccess] = useState(""); const loadPasskeys = useCallback(async () => { setLoading(true); try { const res = await authClient.passkey.listUserPasskeys(); setPasskeys((res.data as Passkey[] | null) ?? []); } catch { setError("获取 Passkey 列表失败"); } finally { setLoading(false); } }, []); useEffect(() => { void loadPasskeys(); }, [loadPasskeys]); const handleAdd = async () => { setError(""); setSuccess(""); setAdding(true); try { const res = await authClient.passkey.addPasskey(); if (res?.error) { setError(res.error.message ?? "注册 Passkey 失败"); } else { setSuccess("Passkey 注册成功"); await loadPasskeys(); } } catch { setError("注册 Passkey 失败,请重试"); } finally { setAdding(false); } }; const handleDelete = async (id: string) => { setError(""); setSuccess(""); setDeletingId(id); try { const res = await authClient.passkey.deletePasskey({ id }); if (res?.error) { setError(res.error.message ?? "删除 Passkey 失败"); } else { setPasskeys((prev) => prev.filter((p) => p.id !== id)); setSuccess("Passkey 已删除"); } } catch { setError("删除 Passkey 失败,请重试"); } finally { setDeletingId(null); } }; return (

账号设置

管理您的安全凭据

{error && ( {error} setError("")} /> )} {success && ( {success} setSuccess("")} /> )} {/* Passkey section */}

Passkey

使用设备生物识别或 PIN 免密登录

{loading ? (
) : passkeys.length === 0 ? (
尚未添加任何 Passkey
) : (
    {passkeys.map((pk) => (
  • {pk.name ?? pk.deviceType ?? "Passkey"}

    添加于{" "} {new Date(pk.createdAt).toLocaleString("zh-CN", { year: "numeric", month: "short", day: "numeric", })}

  • ))}
)}
); }