Skip to content

Commit

Permalink
feat(web): reset password (#974)
Browse files Browse the repository at this point in the history
  • Loading branch information
walle233 authored Mar 28, 2023
1 parent 260b335 commit 13a2a9f
Show file tree
Hide file tree
Showing 9 changed files with 317 additions and 19 deletions.
7 changes: 5 additions & 2 deletions web/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@
"LoginTip": "Please input your phone number and verification code",
"LoginWithGithub": "Login with GitHub",
"ValidationCodeTip": "Please input the verification code",
"ForgotPassword": "Forgot Password",
"ForgotPassword": "Forgot Password?",
"Register": "Register",
"ToRegister": "Go to Register",
"ToLogin": "Go to Login",
Expand All @@ -308,7 +308,10 @@
"PasswordNotMatch": "The two passwords you entered do not match",
"SmsCodeSendSuccess": "Verification code sent successfully",
"ValidationCodePlaceholder": "Please input the verification code",
"SignupSuccess": "Sign up successfully"
"SignupSuccess": "Sign up successfully",
"ResetPassword": "Reset Password",
"ResetPasswordSuccess": "Reset password successfully",
"NewPassword": "New Password"
},
"Time": "Time",
"CreateTime": "Created",
Expand Down
9 changes: 6 additions & 3 deletions web/public/locales/zh-CN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@
"LoginTip": "请输入手机号和验证码",
"LoginWithGithub": "GitHub 登录",
"ValidationCodeTip": "请输入验证码",
"ForgotPassword": "忘记密码",
"ForgotPassword": "忘记密码",
"Register": "注册",
"ToRegister": "去注册",
"ToLogin": "去登录",
Expand All @@ -308,7 +308,10 @@
"PasswordNotMatch": "两次输入的密码不一致",
"SmsCodeSendSuccess": "验证码发送成功",
"ValidationCodePlaceholder": "请输入验证码",
"SignupSuccess": "注册成功"
"SignupSuccess": "注册成功",
"ResetPassword": "重置密码",
"ResetPasswordSuccess": "重置密码成功",
"NewPassword": "新密码"
},
"Time": "时间",
"CreateTime": "创建时间",
Expand All @@ -327,4 +330,4 @@
"CreateFolder": "新建文件夹",
"Or": "",
"BeforeunloadTip": "确定要离开当前编辑页面吗?"
}
}
9 changes: 6 additions & 3 deletions web/public/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@
"LoginTip": "请输入手机号和验证码",
"LoginWithGithub": "GitHub 登录",
"ValidationCodeTip": "请输入验证码",
"ForgotPassword": "忘记密码",
"ForgotPassword": "忘记密码",
"Register": "注册",
"ToRegister": "去注册",
"ToLogin": "去登录",
Expand All @@ -308,7 +308,10 @@
"PasswordNotMatch": "两次输入的密码不一致",
"SmsCodeSendSuccess": "验证码发送成功",
"ValidationCodePlaceholder": "请输入验证码",
"SignupSuccess": "注册成功"
"SignupSuccess": "注册成功",
"ResetPassword": "重置密码",
"ResetPasswordSuccess": "重置密码成功",
"NewPassword": "新密码"
},
"Time": "时间",
"CreateTime": "创建时间",
Expand All @@ -327,4 +330,4 @@
"CreateFolder": "新建文件夹",
"Or": "",
"BeforeunloadTip": "确定要离开当前编辑页面吗?"
}
}
16 changes: 16 additions & 0 deletions web/src/apis/v1/api-auto.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,22 @@ declare namespace Paths {
export type Responses = any;
}

namespace AuthControllerBindPhone {
export type QueryParameters = any;

export type BodyParameters = any;

export type Responses = any;
}

namespace AuthControllerBindUsername {
export type QueryParameters = any;

export type BodyParameters = any;

export type Responses = any;
}

namespace SubscriptionControllerCreate {
export type QueryParameters = any;

Expand Down
26 changes: 26 additions & 0 deletions web/src/apis/v1/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,29 @@ export async function AuthControllerGetProviders(
params: params,
});
}

/**
* bind phone
* @param {string} phone
* @param {string} code
*/
export async function AuthControllerBindPhone(
params: Paths.AuthControllerBindPhone.BodyParameters | any,
): Promise<Paths.AuthControllerBindPhone.Responses> {
return request(`/v1/auth/bind/phone`, {
method: "POST",
data: params,
});
}

/**
* bind username
*/
export async function AuthControllerBindUsername(
params: Paths.AuthControllerBindUsername.BodyParameters | any,
): Promise<Paths.AuthControllerBindUsername.Responses> {
return request(`/v1/auth/bind/username`, {
method: "POST",
data: params,
});
}
2 changes: 1 addition & 1 deletion web/src/components/DependenceList/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
vertical-align: middle;
}
}
}
}
224 changes: 224 additions & 0 deletions web/src/pages/auth/reset-password/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import { useState } from "react";
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
import { ViewIcon, ViewOffIcon } from "@chakra-ui/icons";
import {
Button,
FormControl,
FormLabel,
Input,
InputGroup,
InputRightElement,
} from "@chakra-ui/react";
import { t } from "i18next";

import { useResetPasswordMutation, useSendSmsCodeMutation } from "@/pages/auth/service";
import useGlobalStore from "@/pages/globalStore";

type FormData = {
phone?: string;
validationCode?: string;
account: string;
password: string;
confirmPassword: string;
};

export default function ResetPassword() {
const resetPasswordMutation = useResetPasswordMutation();
const sendSmsCodeMutation = useSendSmsCodeMutation();

const { showSuccess, showError } = useGlobalStore();
const navigate = useNavigate();

const [isSendSmsCode, setIsSendSmsCode] = useState(false);
const [countdown, setCountdown] = useState(60);
const [isShowPassword, setIsShowPassword] = useState(false);

const {
register,
getValues,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
defaultValues: {
phone: "",
validationCode: "",
password: "",
confirmPassword: "",
},
});

const onSubmit = async (data: FormData) => {
if (data.password !== data.confirmPassword) {
showError(t("AuthPanel.PasswordNotMatch"));
return;
}

const params = {
phone: data.phone,
code: data.validationCode,
password: data.password,
type: "ResetPassword",
};

const res = await resetPasswordMutation.mutateAsync(params);

if (res?.data) {
showSuccess(t("AuthPanel.ResetPasswordSuccess"));
navigate("/login", { replace: true });
}
};

const handleSendSmsCode = async () => {
if (isSendSmsCode) {
return;
}

const phone = getValues("phone") || "";
const isValidate = /^1[2-9]\d{9}$/.test(phone);
if (!isValidate) {
showError(t("AuthPanel.PhoneTip"));
return;
}

switchSmsCodeStatus();

const res = await sendSmsCodeMutation.mutateAsync({
phone,
type: "ResetPassword",
});

if (res?.data) {
showSuccess(t("AuthPanel.SmsCodeSendSuccess"));
}
};

const switchSmsCodeStatus = () => {
setIsSendSmsCode(true);
setCountdown(60);
const timer = setInterval(() => {
setCountdown((countdown) => {
if (countdown === 0) {
clearInterval(timer);
setIsSendSmsCode(false);
return 0;
}
return countdown - 1;
});
}, 1000);
};

return (
<div className="bg-white absolute left-1/2 top-1/2 -translate-y-1/2 w-[560px] rounded-[10px] p-[65px]">
<div className="mb-[45px]">
<img src="/logo.png" alt="logo" width={40} className="mr-4" />
</div>
<div className="mb-[65px]">
<FormControl isInvalid={!!errors?.phone} className="flex mb-6 items-center">
<FormLabel className="w-20" htmlFor="phone">
{t("AuthPanel.Phone")}
</FormLabel>
<InputGroup>
<Input
{...register("phone", {
required: true,
pattern: {
value: /^1[2-9]\d{9}$/,
message: t("AuthPanel.PhoneTip"),
},
})}
type="tel"
id="phone"
placeholder={t("AuthPanel.PhonePlaceholder") || ""}
/>
<InputRightElement width="6rem">
<Button
className="w-20"
variant={isSendSmsCode ? "thirdly_disabled" : "thirdly"}
onClick={handleSendSmsCode}
>
{isSendSmsCode ? `${countdown}s` : t("AuthPanel.getValidationCode")}
</Button>
</InputRightElement>
</InputGroup>
</FormControl>
<FormControl isInvalid={!!errors.validationCode} className="flex mb-6 items-center">
<FormLabel className="w-20" htmlFor="validationCode">
{t("AuthPanel.ValidationCode")}
</FormLabel>
<Input
type="number"
{...register("validationCode", {
required: true,
pattern: {
value: /^\d{6}$/,
message: t("AuthPanel.ValidationCodePlaceholder"),
},
})}
id="validationCode"
placeholder={t("AuthPanel.ValidationCodePlaceholder") || ""}
/>
</FormControl>
<FormControl isInvalid={!!errors.password} className="flex mb-6 items-center">
<FormLabel className="w-20" htmlFor="password">
{t("AuthPanel.NewPassword")}
</FormLabel>
<InputGroup>
<Input
type={isShowPassword ? "text" : "password"}
{...register("password", {
required: true,
})}
id="password"
placeholder={t("AuthPanel.PasswordPlaceholder") || ""}
/>
<InputRightElement width="2rem">
{isShowPassword ? (
<ViewOffIcon className="cursor-pointer" onClick={() => setIsShowPassword(false)} />
) : (
<ViewIcon className="cursor-pointer" onClick={() => setIsShowPassword(true)} />
)}
</InputRightElement>
</InputGroup>
</FormControl>
<FormControl isInvalid={!!errors.confirmPassword} className="flex mb-6 items-center">
<FormLabel className="w-20" htmlFor="confirmPassword">
{t("AuthPanel.ConfirmPassword")}
</FormLabel>
<InputGroup>
<Input
type={isShowPassword ? "text" : "password"}
{...register("confirmPassword", {
required: true,
})}
id="confirmPassword"
placeholder={t("AuthPanel.ConfirmPassword") || ""}
/>
<InputRightElement width="2rem">
{isShowPassword ? (
<ViewOffIcon className="cursor-pointer" onClick={() => setIsShowPassword(false)} />
) : (
<ViewIcon className="cursor-pointer" onClick={() => setIsShowPassword(true)} />
)}
</InputRightElement>
</InputGroup>
</FormControl>
<div className="mb-6">
<Button
type="submit"
className="w-full pt-5 pb-5"
isLoading={resetPasswordMutation.isLoading}
onClick={handleSubmit(onSubmit)}
>
{t("AuthPanel.ResetPassword")}
</Button>
</div>
<div className="mt-2 flex justify-end">
<Button size="xs" variant={"text"} onClick={() => navigate("/login", { replace: true })}>
{t("AuthPanel.ToLogin")}
</Button>
</div>
</div>
</div>
);
}
Loading

0 comments on commit 13a2a9f

Please sign in to comment.