import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { trpc } from "@/lib/trpc"; import { useLocation } from "wouter"; import { toast } from "sonner"; import { Target, Loader2 } from "lucide-react"; export default function Login() { const [username, setUsername] = useState(""); const [, setLocation] = useLocation(); const utils = trpc.useUtils(); const loginMutation = trpc.auth.loginWithUsername.useMutation(); const syncAuthenticatedUser = async (fallbackUser: Awaited>["user"]) => { // Seed the cache immediately so protected routes do not bounce back to /login. utils.auth.me.setData(undefined, fallbackUser); for (let attempt = 0; attempt < 3; attempt++) { const user = await utils.auth.me.fetch(); if (user) { utils.auth.me.setData(undefined, user); return user; } await new Promise(resolve => window.setTimeout(resolve, 120 * (attempt + 1))); } return fallbackUser; }; const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); if (!username.trim()) { toast.error("请输入用户名"); return; } try { const data = await loginMutation.mutateAsync({ username: username.trim() }); const user = await syncAuthenticatedUser(data.user); toast.success(data.isNew ? `欢迎加入,${user.name}!` : `欢迎回来,${user.name}!`); setLocation("/dashboard"); } catch (err) { const message = err instanceof Error ? err.message : "未知错误"; toast.error("登录失败: " + message); } }; return (

Tennis Hub

训练与分析入口

登录 输入用户名后进入系统
setUsername(e.target.value)} className="h-12 text-base" autoFocus maxLength={64} />
AI
姿势识别
📊
训练计划
🎯
NTRP评分

输入用户名后进入系统

); }