From a76b15aa7d8f4934eadcd11c9e10265d9e9ca4b1 Mon Sep 17 00:00:00 2001 From: BoYanZh Date: Wed, 20 Mar 2024 22:33:59 -0400 Subject: [PATCH] feat: reset & change password (#13) Co-authored-by: gjzuloaga --- src/client/index.ts | 87 ++++++++++++++++++++ src/pages/ChangePassword/index.tsx | 122 +++++++++++++++++++++++++++++ src/pages/EditProfile/index.tsx | 23 +++--- src/pages/ForgotPassword/index.tsx | 58 ++++++++++++++ src/pages/Login/index.tsx | 14 +++- src/pages/ResetPassword/index.tsx | 104 ++++++++++++++++++++++++ src/routes.tsx | 15 ++++ 7 files changed, 408 insertions(+), 15 deletions(-) create mode 100644 src/pages/ChangePassword/index.tsx create mode 100644 src/pages/ForgotPassword/index.tsx create mode 100644 src/pages/ResetPassword/index.tsx diff --git a/src/client/index.ts b/src/client/index.ts index 5552541..ecc0520 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -211,6 +211,12 @@ export interface SchemaUser { username?: string } +export interface SchemaUserChangePassword { + currentPassword?: string + newPassword?: string + username?: string +} + export interface SchemaUserDetail { address?: string address2?: string @@ -242,6 +248,11 @@ export interface SchemaUserListResponse { offset?: number } +export interface SchemaUserResetPassword { + newPassword?: string + username?: string +} + export type QueryParamsType = Record export type ResponseFormat = keyof Omit @@ -642,6 +653,56 @@ export class Api< ...params }), + /** + * @description Change the user's password. + * + * @tags Auth + * @name V1AuthChangepasswordCreate + * @summary change password + * @request POST:/api/v1/auth/changepassword + */ + v1AuthChangepasswordCreate: ( + changePassword: SchemaUserChangePassword, + query?: { + /** Redirect url after login */ + redirect_url?: string + }, + params: RequestParams = {} + ) => + this.request({ + path: `/api/v1/auth/changepassword`, + method: 'POST', + query: query, + body: changePassword, + type: ContentType.Json, + format: 'json', + ...params + }), + + /** + * @description Initiate the password reset process. + * + * @tags Auth + * @name V1AuthForgotpasswordCreate + * @summary forgot password + * @request POST:/api/v1/auth/forgotpassword + */ + v1AuthForgotpasswordCreate: ( + query?: { + /** Email */ + email?: string + }, + params: RequestParams = {} + ) => + this.request({ + path: `/api/v1/auth/forgotpassword`, + method: 'POST', + query: query, + type: ContentType.Json, + format: 'json', + ...params + }), + /** * @description Get current JWT. * @@ -725,6 +786,32 @@ export class Api< type: ContentType.Json, format: 'json', ...params + }), + + /** + * @description Reset the user's password. + * + * @tags Auth + * @name V1AuthResetpasswordCreate + * @summary reset password + * @request POST:/api/v1/auth/resetpassword + */ + v1AuthResetpasswordCreate: ( + resetPassword: SchemaUserResetPassword, + query?: { + /** Redirect url after login */ + redirect_url?: string + }, + params: RequestParams = {} + ) => + this.request({ + path: `/api/v1/auth/resetpassword`, + method: 'POST', + query: query, + body: resetPassword, + type: ContentType.Json, + format: 'json', + ...params }) } movie = { diff --git a/src/pages/ChangePassword/index.tsx b/src/pages/ChangePassword/index.tsx new file mode 100644 index 0000000..104f514 --- /dev/null +++ b/src/pages/ChangePassword/index.tsx @@ -0,0 +1,122 @@ +import { useRequest } from 'ahooks' +import PageContainer from 'components/PageContainer' +import { useAuth } from 'hooks/useAuth' +import { useState } from 'react' +import { Col, Row } from 'react-bootstrap' +import Button from 'react-bootstrap/Button' +import Form from 'react-bootstrap/Form' +import { Navigate, useNavigate, useSearchParams } from 'react-router-dom' +import { DOMAIN_HOST } from 'utils/constants' +import Backend from 'utils/service' + +const ChangeForm: React.FC = () => { + const { user } = useAuth() + const [searchParams] = useSearchParams() + const [username, setUsername] = useState('') + const [currentPassword, setCurrentPassword] = useState('') + const [newPassword, setNewPassword] = useState('') + const navigate = useNavigate() + const { run: changePasssword } = useRequest( + async () => { + const from = searchParams.get('from') ?? '/' + return Backend.auth.v1AuthChangepasswordCreate( + { + username, + currentPassword, + newPassword + }, + { + redirect_url: `${DOMAIN_HOST}${from}` + } + ) + }, + { + manual: true, + onSuccess: () => { + navigate('/logout') // Navigate to the login page + } + } + ) + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + changePasssword() + } + + return user ? ( + <> +
+

Reset Password

+
+ +
+ + + Username + + + { + setUsername(e.target.value) + }} + /> + + + + + Current Password + + + { + setCurrentPassword(e.target.value) + }} + /> + + + + + New Password + + + { + setNewPassword(e.target.value) + }} + /> + + + + + + + +
+ + + ) : ( + + ) +} + +const Index: React.FC = () => ( + + + +) + +export default Index diff --git a/src/pages/EditProfile/index.tsx b/src/pages/EditProfile/index.tsx index 3e80974..e94a676 100644 --- a/src/pages/EditProfile/index.tsx +++ b/src/pages/EditProfile/index.tsx @@ -4,10 +4,12 @@ import PageContainer from 'components/PageContainer' import type React from 'react' import { useState } from 'react' import { Form, Button, Col, Row, Accordion } from 'react-bootstrap' +import { useNavigate } from 'react-router-dom' import Backend from 'utils/service' const UserProfileForm: React.FC = () => { // TODO: submit update user + const navigate = useNavigate() const { run: submit } = useRequest(async () => null) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() @@ -37,18 +39,15 @@ const UserProfileForm: React.FC = () => { disabled /> - - Password - { - setUser(prevUser => ({ - ...prevUser, - password: e.target.value - })) - }} - /> + + Password + diff --git a/src/pages/ForgotPassword/index.tsx b/src/pages/ForgotPassword/index.tsx new file mode 100644 index 0000000..06ec760 --- /dev/null +++ b/src/pages/ForgotPassword/index.tsx @@ -0,0 +1,58 @@ +import { useRequest } from 'ahooks' +import PageContainer from 'components/PageContainer' +import { useState } from 'react' +import { Col, Row, Form, Button, Alert } from 'react-bootstrap' +import Backend from 'utils/service' + +const ForgotPassword: React.FC = () => { + const [email, setEmail] = useState('') + const [info, setInfo] = useState('') + const { run } = useRequest( + async () => Backend.auth.v1AuthForgotpasswordCreate({ email }), + { + manual: true, + onSuccess: () => { + setInfo('Done! Check you email!') + } + } + ) + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + run() + } + return ( + +
+

Forgot Password

+
+ + {info ? {info} : null} +
+ + + Email + + + setEmail(e.target.value)} + /> + + + + + + + +
+ +
+ ) +} + +export default ForgotPassword diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx index 28c6dcf..4a002c1 100644 --- a/src/pages/Login/index.tsx +++ b/src/pages/Login/index.tsx @@ -6,7 +6,7 @@ import { useState } from 'react' import { Alert, Col, Row } from 'react-bootstrap' import Button from 'react-bootstrap/Button' import Form from 'react-bootstrap/Form' -import { Navigate, useSearchParams } from 'react-router-dom' +import { Navigate, useSearchParams, useNavigate } from 'react-router-dom' import { DOMAIN_HOST } from 'utils/constants' import Backend from 'utils/service' @@ -17,14 +17,15 @@ const LoginForm: React.FC = () => { const [username, setUsername] = useState('demo') const [password, setPassword] = useState('123456') const [remember, setRemember] = useState(false) + const navigate = useNavigate() const { run: login } = useRequest( async () => { const from = searchParams.get('from') ?? '/' return Backend.auth.v1AuthLoginCreate( { username, - password, - remember + password + // remember }, { redirect_url: `${DOMAIN_HOST}${from}` @@ -48,6 +49,10 @@ const LoginForm: React.FC = () => { login() } + const handleForgotPassword = () => { + navigate('/forgotPassword') // Navigate to the Forgot Password page + } + return user ? ( user.is_admin ? ( @@ -104,6 +109,9 @@ const LoginForm: React.FC = () => { }} name='rememberme' /> + + +
+ + + + ) +} + +const Index: React.FC = () => ( + + + +) + +export default Index diff --git a/src/routes.tsx b/src/routes.tsx index d968f8f..bff91c7 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -15,6 +15,9 @@ import OrderSummary from 'pages/OrderSummary' import PromoManage from 'pages/PromoManage' import Register from 'pages/Register' import RegisterConfirmation from 'pages/RegisterConfirmation' +import ForgotPassword from 'pages/ForgotPassword' +import ResetPassword from 'pages/ResetPassword' +import ChangePassword from 'pages/ChangePassword' import UserManage from 'pages/UserManage' import type { RouteObject } from 'react-router-dom' @@ -31,6 +34,18 @@ const children: RouteObject[] = [ path: '/register', element: }, + { + path: '/forgotPassword', + element: + }, + { + path: '/resetPassword', + element: + }, + { + path: '/changePassword', + element: + }, { path: '/activate', element: