feat(auth): hook up verification code sending
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { IconInput } from "@/components/ui/icon-input"
|
import { IconInput } from "@/components/ui/icon-input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
import { resetPassword } from "@/lib/api"
|
import { resetPassword, sendForgotPasswordCode } from "@/lib/api"
|
||||||
import { toApiError } from "@/lib/errors"
|
import { toApiError } from "@/lib/errors"
|
||||||
import { notifyInfo, notifySuccess } from "@/lib/toast"
|
import { notifyInfo, notifySuccess } from "@/lib/toast"
|
||||||
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema"
|
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema"
|
||||||
@@ -33,6 +33,7 @@ export default function ForgotPasswordPage() {
|
|||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
getValues,
|
||||||
trigger,
|
trigger,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting },
|
||||||
} = useForm({
|
} = useForm({
|
||||||
@@ -49,9 +50,14 @@ export default function ForgotPasswordPage() {
|
|||||||
const isValid = await trigger("email")
|
const isValid = await trigger("email")
|
||||||
if (!isValid) return
|
if (!isValid) return
|
||||||
|
|
||||||
// Mock sending code
|
try {
|
||||||
|
const email = getValues("email")
|
||||||
|
await sendForgotPasswordCode(email)
|
||||||
setCountdown(60)
|
setCountdown(60)
|
||||||
notifySuccess("验证码已发送到你的邮箱")
|
notifySuccess("验证码已发送到你的邮箱")
|
||||||
|
} catch (err) {
|
||||||
|
notifyInfo(toApiError(err).msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof forgotSchema>) => {
|
const onSubmit = async (data: z.infer<typeof forgotSchema>) => {
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ import { Button } from "@/components/ui/button"
|
|||||||
import { Checkbox } from "@/components/ui/checkbox"
|
import { Checkbox } from "@/components/ui/checkbox"
|
||||||
import { IconInput } from "@/components/ui/icon-input"
|
import { IconInput } from "@/components/ui/icon-input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
import { register as registerApi } from "@/lib/api"
|
import { register as registerApi, sendEmailVerificationCode } from "@/lib/api"
|
||||||
import { toApiError } from "@/lib/errors"
|
import { toApiError } from "@/lib/errors"
|
||||||
import { notifyInfo } from "@/lib/toast"
|
import { notifyInfo, notifySuccess } from "@/lib/toast"
|
||||||
import { useAuthStore } from "@/store/auth"
|
import { useAuthStore } from "@/store/auth"
|
||||||
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema"
|
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema"
|
||||||
import { Eye, EyeOff, Lock, Mail, Shield, User } from "lucide-react"
|
import { Eye, EyeOff, Lock, Mail, Shield, User } from "lucide-react"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { Controller, useForm } from "react-hook-form"
|
import { Controller, useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
@@ -39,16 +39,40 @@ export default function RegisterPage() {
|
|||||||
const { login: storeLogin } = useAuthStore()
|
const { login: storeLogin } = useAuthStore()
|
||||||
const [showPassword, setShowPassword] = useState(false)
|
const [showPassword, setShowPassword] = useState(false)
|
||||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false)
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false)
|
||||||
|
const [countdown, setCountdown] = useState(0)
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
control,
|
control,
|
||||||
|
getValues,
|
||||||
|
trigger,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting },
|
||||||
} = useForm({
|
} = useForm({
|
||||||
resolver: standardSchemaResolver(registerSchema),
|
resolver: standardSchemaResolver(registerSchema),
|
||||||
defaultValues: { agreeTerms: false },
|
defaultValues: { agreeTerms: false },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (countdown <= 0) return
|
||||||
|
const timer = setInterval(() => setCountdown((c) => c - 1), 1000)
|
||||||
|
return () => clearInterval(timer)
|
||||||
|
}, [countdown])
|
||||||
|
|
||||||
|
const handleSendCode = async () => {
|
||||||
|
const isValid = await trigger("email")
|
||||||
|
if (!isValid) return
|
||||||
|
|
||||||
|
const email = String(getValues("email") ?? "")
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendEmailVerificationCode({ email, scene: "register" })
|
||||||
|
setCountdown(60)
|
||||||
|
notifySuccess("验证码已发送到你的邮箱")
|
||||||
|
} catch (err) {
|
||||||
|
notifyInfo(toApiError(err).msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof registerSchema>) => {
|
const onSubmit = async (data: z.infer<typeof registerSchema>) => {
|
||||||
try {
|
try {
|
||||||
const user = await registerApi({
|
const user = await registerApi({
|
||||||
@@ -100,8 +124,14 @@ export default function RegisterPage() {
|
|||||||
{...register("vcode")}
|
{...register("vcode")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button type="button" variant="outline" onClick={() => {}}>
|
<Button
|
||||||
发送验证码
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
onClick={handleSendCode}
|
||||||
|
disabled={countdown > 0}
|
||||||
|
className="w-[110px]"
|
||||||
|
>
|
||||||
|
{countdown > 0 ? `${countdown}s` : "发送验证码"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{errors.vcode && <p className="text-xs text-destructive">{errors.vcode.message}</p>}
|
{errors.vcode && <p className="text-xs text-destructive">{errors.vcode.message}</p>}
|
||||||
|
|||||||
Reference in New Issue
Block a user