hku-class/frontend/src/app/activate/page.tsx
2026-04-27 15:35:38 +08:00

167 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useEffect, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { getErrorMessage, postAPI } from "@/lib/api";
import Link from "next/link";
import type { LoginResponse } from "@/lib/types";
export default function ActivatePage() {
const [inviteCode, setInviteCode] = useState("");
const [studentId, setStudentId] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const router = useRouter();
const searchParams = useSearchParams();
useEffect(() => {
const code = searchParams.get("code")?.trim();
if (code) {
setInviteCode(code.toUpperCase());
}
}, [searchParams]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
if (password !== confirmPassword) {
setError("两次密码输入不一致");
return;
}
if (password.length < 8) {
setError("密码至少8位");
return;
}
setLoading(true);
try {
const res = await postAPI<LoginResponse>("/api/auth/activate", {
invite_code: inviteCode,
student_id: studentId,
email,
password,
});
if (res.token) {
localStorage.setItem("auth_token", res.token);
localStorage.setItem("auth_user", JSON.stringify(res.user));
router.push("/");
} else {
router.push("/login");
}
} catch (err: unknown) {
setError(getErrorMessage(err, "激活失败"));
} finally {
setLoading(false);
}
};
return (
<div className="relative min-h-screen overflow-hidden bg-[linear-gradient(180deg,#f7efe2_0%,#f4ebdf_38%,#f9f5ee_100%)] px-4 py-10">
<div className="absolute inset-x-0 top-0 h-72 bg-[radial-gradient(circle_at_top,rgba(115,25,37,0.18),transparent_60%)]" />
<div className="relative mx-auto flex min-h-[calc(100vh-5rem)] max-w-6xl items-center justify-center">
<div className="grid w-full items-center gap-10 lg:grid-cols-[1.05fr_0.95fr]">
<div className="hidden lg:block">
<div className="inline-flex items-center rounded-full border border-[#c9ac82] bg-white/55 px-4 py-1.5 text-[11px] uppercase tracking-[0.28em] text-[#84553c]">
HKU ICB Cohort Access
</div>
<h1 className="mt-6 max-w-2xl text-5xl font-semibold leading-tight text-[#4e1d1a]">
</h1>
<p className="mt-5 max-w-xl text-base leading-8 text-[#775a4a]">
使
</p>
</div>
<Card className="mx-auto w-full max-w-md bg-[#fffaf3]">
<CardHeader className="text-center">
<div className="text-[11px] uppercase tracking-[0.24em] text-[#976746]">Activate Account</div>
<CardTitle className="text-3xl text-[#4e1d1a]"></CardTitle>
<CardDescription className="text-[#7a5e4f]"></CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="inviteCode"></Label>
<Input
id="inviteCode"
placeholder="请输入班级邀请码"
value={inviteCode}
onChange={(e) => setInviteCode(e.target.value.toUpperCase())}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="studentId"></Label>
<Input
id="studentId"
placeholder="请输入学号"
value={studentId}
onChange={(e) => setStudentId(e.target.value)}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="email"></Label>
<Input
id="email"
type="email"
placeholder="your@email.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="password"></Label>
<Input
id="password"
type="password"
placeholder="至少8位"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="confirmPassword"></Label>
<Input
id="confirmPassword"
type="password"
placeholder="再次输入密码"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
/>
</div>
{error && (
<p className="rounded-2xl bg-red-50 p-3 text-sm text-red-600">
{error}
</p>
)}
<Button type="submit" className="w-full" disabled={loading}>
{loading ? "激活中..." : "激活账号"}
</Button>
</form>
<div className="mt-5 text-center text-sm text-[#6f5648]">
{" "}
<Link href="/login" className="text-[#8a4527] hover:underline">
</Link>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
);
}