Skip to content

Commit 588f4d8

Browse files
added reset password flow
1 parent c8a85c3 commit 588f4d8

File tree

10 files changed

+284
-9
lines changed

10 files changed

+284
-9
lines changed

client/packages/lowcoder/src/api/userApi.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ export interface ApiKeyPayload {
4343
description?: string;
4444
}
4545

46+
export interface ResetLostPasswordPayload {
47+
token: string;
48+
userEmail: string;
49+
newPassword: string;
50+
}
51+
4652
export interface FetchApiKeysResponse extends ApiResponse {
4753
data: {
4854
id: string;
@@ -69,6 +75,8 @@ class UserApi extends Api {
6975
static markUserStatusURL = "/users/mark-status";
7076
static userDetailURL = (id: string) => `/users/userDetail/${id}`;
7177
static resetPasswordURL = `/users/reset-password`;
78+
static forgotPasswordURL = `/users/lost-password`;
79+
static resetLostPasswordURL = `/users/reset-lost-password`;
7280
static fetchApiKeysURL = `/auth/api-keys`;
7381
static createApiKeyURL = `/auth/api-key`;
7482
static deleteApiKeyURL = (id: string) => `/auth/api-key/${id}`;
@@ -138,6 +146,15 @@ class UserApi extends Api {
138146
return Api.post(UserApi.resetPasswordURL, { userId: userId });
139147
}
140148

149+
static forgotPassword(userEmail: string): AxiosPromise<ApiResponse> {
150+
return Api.post(UserApi.forgotPasswordURL, { userEmail });
151+
}
152+
153+
static resetLostPassword(request: ResetLostPasswordPayload): AxiosPromise<ApiResponse> {
154+
console.log(request);
155+
return Api.post(UserApi.resetLostPasswordURL, request);
156+
}
157+
141158
static createApiKey({
142159
name,
143160
description = ''

client/packages/lowcoder/src/app.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import {
2424
TRASH_URL,
2525
USER_AUTH_URL,
2626
ADMIN_APP_URL,
27+
ORG_AUTH_FORGOT_PASSWORD_URL,
28+
ORG_AUTH_RESET_PASSWORD_URL,
2729
} from "constants/routesURL";
2830

2931
import React from "react";
@@ -173,6 +175,8 @@ class AppIndex extends React.Component<AppIndexProps, any> {
173175
<LazyRoute path={USER_AUTH_URL} component={LazyUserAuthComp} />
174176
<LazyRoute path={ORG_AUTH_LOGIN_URL} component={LazyUserAuthComp} />
175177
<LazyRoute path={ORG_AUTH_REGISTER_URL} component={LazyUserAuthComp} />
178+
<LazyRoute path={ORG_AUTH_FORGOT_PASSWORD_URL} component={LazyUserAuthComp} />
179+
<LazyRoute path={ORG_AUTH_RESET_PASSWORD_URL} component={LazyUserAuthComp} />
176180
<LazyRoute path={INVITE_LANDING_URL} component={LazyInviteLanding} />
177181
<LazyRoute path={`${COMPONENT_DOC_URL}/:name`} component={LazyComponentDoc} />
178182
<LazyRoute path={`/playground/:name/:dsl`} component={LazyComponentPlayground} />

client/packages/lowcoder/src/constants/authConstants.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import {
22
AUTH_BIND_URL,
3+
AUTH_FORGOT_PASSWORD_URL,
34
AUTH_LOGIN_URL,
45
AUTH_REGISTER_URL,
6+
AUTH_RESET_PASSWORD_URL,
57
OAUTH_REDIRECT,
8+
ORG_AUTH_FORGOT_PASSWORD_URL,
69
ORG_AUTH_LOGIN_URL,
710
ORG_AUTH_REGISTER_URL,
11+
ORG_AUTH_RESET_PASSWORD_URL,
812
} from "constants/routesURL";
913
import { InviteInfo } from "api/inviteApi";
1014
import Login, { ThirdPartyBindCard } from "pages/userAuth/login";
@@ -18,6 +22,8 @@ import {
1822
KeyCloakLoginIcon,
1923
EmailLoginIcon
2024
} from "assets/icons";
25+
import ForgotPassword from "pages/userAuth/forgotPassword";
26+
import ResetPassword from "pages/userAuth/resetPassword";
2127

2228
export type AuthInviteInfo = InviteInfo & { invitationId: string };
2329
export type AuthLocationState = { inviteInfo?: AuthInviteInfo; thirdPartyAuthError?: boolean };
@@ -85,9 +91,13 @@ export const AuthRoutes: Array<{ path: string; component: React.ComponentType<an
8591
{ path: AUTH_LOGIN_URL, component: Login },
8692
{ path: AUTH_BIND_URL, component: ThirdPartyBindCard },
8793
{ path: AUTH_REGISTER_URL, component: UserRegister },
94+
{ path: AUTH_FORGOT_PASSWORD_URL, component: ForgotPassword },
95+
{ path: AUTH_RESET_PASSWORD_URL, component: ResetPassword },
8896
{ path: OAUTH_REDIRECT, component: AuthRedirect },
8997
{ path: ORG_AUTH_LOGIN_URL, component: Login },
9098
{ path: ORG_AUTH_REGISTER_URL, component: UserRegister },
99+
{ path: ORG_AUTH_FORGOT_PASSWORD_URL, component: ForgotPassword },
100+
{ path: ORG_AUTH_RESET_PASSWORD_URL, component: ResetPassword },
91101
];
92102

93103
export type ServerAuthType = "GOOGLE" | "GITHUB" | "FORM" | "KEYCLOAK" | "ORY" | "GENERIC";

client/packages/lowcoder/src/constants/routesURL.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,17 @@ export const APP_EDITOR_URL = `${ALL_APPLICATIONS_URL}/:applicationId/:viewMode/
4040
export const AUTH_BIND_URL = `${USER_AUTH_URL}/bind`;
4141
export const AUTH_LOGIN_URL = `${USER_AUTH_URL}/login`;
4242
export const AUTH_REGISTER_URL = `${USER_AUTH_URL}/register`;
43+
export const AUTH_FORGOT_PASSWORD_URL = `${USER_AUTH_URL}/forgot-password`;
44+
export const AUTH_RESET_PASSWORD_URL = `${USER_AUTH_URL}/lost-password`;
4345
export const QR_CODE_OAUTH_URL = `${USER_AUTH_URL}/oauth/qrcode`;
4446
export const OAUTH_REDIRECT = `${USER_AUTH_URL}/oauth/redirect`;
4547
export const CAS_AUTH_REDIRECT = `${USER_AUTH_URL}/cas/redirect`;
4648
export const LDAP_AUTH_LOGIN_URL = `${USER_AUTH_URL}/ldap/login`;
4749
export const INVITE_LANDING_URL = "/invite/:invitationId";
4850
export const ORG_AUTH_LOGIN_URL = `/org/:orgId/auth/login`;
4951
export const ORG_AUTH_REGISTER_URL = `/org/:orgId/auth/register`;
52+
export const ORG_AUTH_FORGOT_PASSWORD_URL = `/org/:orgId/auth/forgot-password`;
53+
export const ORG_AUTH_RESET_PASSWORD_URL = `/org/:orgId/auth/lost-password`;
5054
export const MARKETPLACE_TYPE_URL = `${MARKETPLACE_URL}/:marketplaceType`;
5155

5256
export const APPLICATION_VIEW_URL = (appId: string, viewMode: AppViewMode) =>

client/packages/lowcoder/src/i18n/locales/en.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2810,8 +2810,12 @@ export const en = {
28102810
"userAuth": {
28112811
"registerByEmail": "Sign Up",
28122812
"email": "Email:",
2813-
"inputEmail": "Please Enter Your Email",
2814-
"inputValidEmail": "Please Enter a Valid Email",
2813+
"inputEmail": "Please enter your email",
2814+
"inputValidEmail": "Please enter a valid email",
2815+
"forgotPassword": "Forgot Password",
2816+
"forgotPasswordInfo": "Enter your email and we'll send you a link to reset your password.",
2817+
"forgotPasswordSuccess": "Please check your email for reset password link.",
2818+
"forgotPasswordError": "Something went wrong. Please try again.",
28152819
"register": "Sign Up",
28162820
"userLogin": "Sign In",
28172821
"login": "Sign In",
@@ -2836,6 +2840,7 @@ export const en = {
28362840
"resetPasswordDesc": "Reset User {name}'s Password. A New Password Will Be Generated After Reset.",
28372841
"resetSuccess": "Reset Succeeded",
28382842
"resetSuccessDesc": "Password Reset Succeeded. The New Password is: {password}",
2843+
"resetLostPasswordSuccess": "Password Reset Succeeded. Please login again.",
28392844
"copyPassword": "Copy Password",
28402845
"poweredByLowcoder": "Powered by: Lowcoder.cloud"
28412846
},

client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,8 @@ export function HomeLayout(props: HomeLayoutProps) {
335335
const resList: HomeRes[] = displayElements
336336
.filter((e) =>
337337
searchValue
338-
? e.name.toLocaleLowerCase().includes(searchValue) ||
339-
e.createBy.toLocaleLowerCase().includes(searchValue)
338+
? e.name.toLocaleLowerCase().includes(searchValue.toLocaleLowerCase()) ||
339+
e.createBy.toLocaleLowerCase().includes(searchValue.toLocaleLowerCase())
340340
: true
341341
)
342342
.filter((e) => {
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import React, { useContext, useState, useMemo } from "react";
2+
import {
3+
AuthContainer,
4+
ConfirmButton,
5+
FormWrapperMobile,
6+
StyledRouteLinkLogin,
7+
} from "pages/userAuth/authComponents";
8+
import { FormInput, PasswordInput, messageInstance } from "lowcoder-design";
9+
import { AUTH_LOGIN_URL, ORG_AUTH_LOGIN_URL } from "constants/routesURL";
10+
import UserApi from "api/userApi";
11+
import { useRedirectUrl } from "util/hooks";
12+
import { checkEmailValid } from "util/stringUtils";
13+
import styled from "styled-components";
14+
import { requiresUnAuth } from "./authHOC";
15+
import { useLocation } from "react-router-dom";
16+
import { UserConnectionSource } from "@lowcoder-ee/constants/userConstants";
17+
import { trans } from "i18n";
18+
import { AuthContext, useAuthSubmit } from "pages/userAuth/authUtils";
19+
import { useParams } from "react-router-dom";
20+
import { Divider } from "antd";
21+
import { validateResponse } from "api/apiUtils";
22+
23+
const StyledFormInput = styled(FormInput)`
24+
margin-bottom: 16px;
25+
`;
26+
27+
const RegisterContent = styled(FormWrapperMobile)`
28+
display: flex;
29+
flex-direction: column;
30+
margin-bottom: 0px;
31+
`;
32+
33+
function ForgotPassword() {
34+
const [account, setAccount] = useState("");
35+
const [loading, setLoading] = useState(false);
36+
const location = useLocation();
37+
38+
const orgId = useParams<any>().orgId;
39+
40+
const onSubmit = () => {
41+
setLoading(true);
42+
UserApi.forgotPassword(account)
43+
.then((resp) => {
44+
// TODO: need proper response from BE
45+
// if (validateResponse(resp)) {
46+
// messageInstance.success(trans("userAuth.forgotPasswordSuccess"));
47+
// }
48+
if (resp.status === 200) {
49+
messageInstance.success(trans("userAuth.forgotPasswordSuccess"));
50+
}
51+
})
52+
.catch((e) => {
53+
messageInstance.error(trans("userAuth.forgotPasswordError"));
54+
})
55+
.finally(() => {
56+
setLoading(false);
57+
})
58+
}
59+
60+
const forgotPasswordHeading = trans("userAuth.forgotPassword")
61+
const subHeading = trans("userAuth.poweredByLowcoder");
62+
63+
return (
64+
<AuthContainer
65+
heading={forgotPasswordHeading}
66+
subHeading={subHeading}
67+
type="large"
68+
>
69+
<p style={{textAlign: 'center'}}>{trans("userAuth.forgotPasswordInfo")}</p>
70+
<RegisterContent>
71+
<StyledFormInput
72+
className="form-input"
73+
label={''}
74+
onChange={(value, valid) => setAccount(valid ? value : "")}
75+
placeholder={trans("userAuth.inputEmail")}
76+
checkRule={{
77+
check: checkEmailValid,
78+
errorMsg: trans("userAuth.inputValidEmail"),
79+
}}
80+
/>
81+
<ConfirmButton
82+
disabled={!account}
83+
onClick={onSubmit}
84+
loading={loading}
85+
>
86+
{trans("button.submit")}
87+
</ConfirmButton>
88+
</RegisterContent>
89+
<Divider/>
90+
<StyledRouteLinkLogin to={{
91+
pathname: orgId
92+
? ORG_AUTH_LOGIN_URL.replace(':orgId', orgId)
93+
: AUTH_LOGIN_URL,
94+
state: location.state
95+
}}>{trans("userAuth.userLogin")}
96+
</StyledRouteLinkLogin>
97+
</AuthContainer>
98+
);
99+
}
100+
101+
export default requiresUnAuth(ForgotPassword);

client/packages/lowcoder/src/pages/userAuth/formLogin.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,19 @@ import { UserConnectionSource } from "@lowcoder-ee/constants/userConstants";
1515
import { trans } from "i18n";
1616
import { AuthContext, useAuthSubmit } from "pages/userAuth/authUtils";
1717
import { ThirdPartyAuth } from "pages/userAuth/thirdParty/thirdPartyAuth";
18-
import { AUTH_REGISTER_URL, ORG_AUTH_REGISTER_URL } from "constants/routesURL";
19-
import { useLocation, useParams } from "react-router-dom";
18+
import { AUTH_FORGOT_PASSWORD_URL, AUTH_REGISTER_URL, ORG_AUTH_FORGOT_PASSWORD_URL, ORG_AUTH_REGISTER_URL } from "constants/routesURL";
19+
import { Link, useLocation, useParams } from "react-router-dom";
2020
import { Divider } from "antd";
21+
import Flex from "antd/es/flex";
2122

2223
const AccountLoginWrapper = styled(FormWrapperMobile)`
2324
display: flex;
2425
flex-direction: column;
25-
margin-bottom: 40px;
26+
margin-bottom: 0px;
27+
28+
.form-input.password-input {
29+
margin-bottom: 0px;
30+
}
2631
`;
2732

2833
type FormLoginProps = {
@@ -70,10 +75,21 @@ export default function FormLogin(props: FormLoginProps) {
7075
}}
7176
/>
7277
<PasswordInput
73-
className="form-input"
78+
className="form-input password-input"
7479
onChange={(value) => setPassword(value)}
7580
valueCheck={() => [true, ""]}
7681
/>
82+
<Flex justify="end" style={{margin: '10px 0'}}>
83+
<Link to={{
84+
pathname: orgId
85+
? ORG_AUTH_FORGOT_PASSWORD_URL.replace(':orgId', orgId)
86+
: AUTH_FORGOT_PASSWORD_URL,
87+
state: location.state
88+
}}
89+
>
90+
{`${trans("userAuth.forgotPassword")}?`}
91+
</Link>
92+
</Flex>
7793
<ConfirmButton loading={loading} disabled={!account || !password} onClick={onSubmit}>
7894
{trans("userAuth.login")}
7995
</ConfirmButton>

client/packages/lowcoder/src/pages/userAuth/register.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const StyledPasswordInput = styled(PasswordInput)`
3333
const RegisterContent = styled(FormWrapperMobile)`
3434
display: flex;
3535
flex-direction: column;
36-
margin-bottom: 40px;
36+
margin-bottom: 0px;
3737
`;
3838

3939
function UserRegister() {

0 commit comments

Comments
 (0)