Skip to content

Commit

Permalink
feat(auth): add useChangePassword hook
Browse files Browse the repository at this point in the history
  • Loading branch information
suvashvishowkarma committed Feb 16, 2024
1 parent 16f10fc commit 9cefb1b
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 162 deletions.
1 change: 1 addition & 0 deletions packages/i18n/src/locales/en/user.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
}
},
"messages": {
"error": "Could not change your password.",
"success": "Your password was successfully changed.",
"validation": {
"oldPassword": "Old password is required",
Expand Down
1 change: 1 addition & 0 deletions packages/i18n/src/locales/fr/user.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
}
},
"messages": {
"error": "Could not change your password. (fr)",
"success": "Your password was successfully changed. (fr)",
"validation": {
"oldPassword": "Old password is required (fr)",
Expand Down
20 changes: 20 additions & 0 deletions packages/user/src/api/user/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
import client from "../axios";

import type {
ChangePasswordInputType,
LoginCredentials,
UpdateProfileInputType,
UserType,
} from "@/types";

export const changePassword = async (
{ newPassword, oldPassword }: ChangePasswordInputType,
apiBaseUrl: string,
) => {
const response = await client(apiBaseUrl).post(
"/change_password",
{ oldPassword, newPassword },
{
withCredentials: true,
},
);

if (response.data.status === "ERROR") {
throw new Error(response.data.message);
} else {
return response;
}
};

export const getIsFirstUser = async (
apiBaseUrl: string,
): Promise<{
Expand Down
4 changes: 2 additions & 2 deletions packages/user/src/components/Login/LoginWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export const LoginWrapper: FC<IProperties> = ({
const { user: userConfig } = useConfig();

const [loginUser, { isLoading: loginLoading }] = useLogin({
onLoginFailed,
onLoginSuccess,
onSuccess: onLoginFailed,
onFailed: onLoginSuccess,
});

const handleLoginSubmit = async (credentials: LoginCredentials) => {
Expand Down
1 change: 1 addition & 0 deletions packages/user/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./useAcceptInvitation";
export * from "./useChangePassword";
export * from "./useConfig";
export * from "./useEmailVerification";
export * from "./useFirstUserSignup";
Expand Down
28 changes: 23 additions & 5 deletions packages/user/src/hooks/useAcceptInvitation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ import { useLogin } from "./useLogin";
import type { Invitation, LoginCredentials } from "../types";

type UseAcceptInvitationConfig = {
autoLogin?: boolean;
showToasts?: boolean;
tokenParamKey?: string;
onSuccess?: (user: any) => Promise<void> | void;
onFailed?: (error?: any) => Promise<void> | void;
};

type UseAcceptInvitationMeta = {
Expand All @@ -26,8 +29,13 @@ type UseAcceptInvitationMeta = {
};

export function useAcceptInvitation(config?: UseAcceptInvitationConfig) {
const { showToasts = true, tokenParamKey: tokenParameterKey = "token" } =
config || {};
const {
autoLogin = true,
showToasts = true,
tokenParamKey: tokenParameterKey = "token",
onSuccess,
onFailed,
} = config || {};

const { t } = useTranslation("invitations");
const appConfig: any = useConfig();
Expand Down Expand Up @@ -77,15 +85,25 @@ export function useAcceptInvitation(config?: UseAcceptInvitationConfig) {
if ("data" in response && response.data.status === "ERROR") {
// TODO better handle errors
setIsError(true);

onFailed && (await onFailed(response));

showToasts && toast.error(response.data.message);
} else {
// TODO acceptInvitation should return authenticated user from api
await loginUser(credentials);
onSuccess && (await onSuccess(response));

if (autoLogin) {
// TODO acceptInvitation should return authenticated user from api
await loginUser(credentials);
}
}
})
.catch(() => {
.catch(async (error) => {
setIsError(true);
setIsLoading(false);

onFailed && (await onFailed(error));

showToasts &&
toast.error(`${t("invitations.messages.errorAcceptingInvitation")}`);
});
Expand Down
64 changes: 64 additions & 0 deletions packages/user/src/hooks/useChangePassword.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useTranslation } from "@dzangolab/react-i18n";
import { useState } from "react";
import { toast } from "react-toastify";

import { useConfig } from "./useConfig";
import { changePassword as changePasswordApi } from "../api/user";

import type { ChangePasswordInputType } from "../types";

type UseChangePasswordConfig = {
showToasts?: boolean;
onSuccess?: (user: any) => Promise<void> | void;
onFailed?: (error: any) => Promise<void> | void;
};

type UseChangePasswordMeta = {
isError: boolean;
isLoading: boolean;
};

export function useChangePassword(config?: UseChangePasswordConfig) {
const { showToasts = true, onFailed, onSuccess } = config || {};

const { t } = useTranslation(["user", "errors"]);
const appConfig = useConfig();

const [isError, setIsError] = useState(false);
const [isLoading, setIsLoading] = useState<boolean>(false);

const changePassword = (passwords: ChangePasswordInputType) => {
setIsLoading(true);

return changePasswordApi(passwords, appConfig?.apiBaseUrl || "")
.then(async (response) => {
setIsLoading(false);

if ("data" in response && response.data.status === "OK") {
onSuccess && (await onSuccess(response));

showToasts && toast.success(t("changePassword.messages.success"));
} else {
// TODO better handle errors
setIsError(true);

onFailed && (await onFailed(response));

showToasts && toast.error(response.data.message);
}
})
.catch(async (error) => {
setIsError(true);
setIsLoading(false);

onFailed && (await onFailed(error));

showToasts && toast.error(`${t("changePassword.messages.error")}`);
});
};

return [changePassword, { isError, isLoading }] as [
(passwords: ChangePasswordInputType) => Promise<any>,
UseChangePasswordMeta,
];
}
18 changes: 10 additions & 8 deletions packages/user/src/hooks/useLogin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import type { LoginCredentials } from "../types";

type UseLoginConfig = {
showToasts?: boolean;
onLoginSuccess?: (user: any) => Promise<void> | void;
onLoginFailed?: (error: any) => Promise<void> | void;
onSuccess?: (user: any) => Promise<void> | void;
onFailed?: (error?: any) => Promise<void> | void;
};

type UseLoginMeta = {
Expand All @@ -21,7 +21,7 @@ type UseLoginMeta = {
};

export function useLogin(config?: UseLoginConfig) {
const { showToasts = true, onLoginFailed, onLoginSuccess } = config || {};
const { showToasts = true, onFailed, onSuccess } = config || {};

const { t } = useTranslation(["user", "errors"]);
const { setUser } = useUser();
Expand All @@ -34,26 +34,28 @@ export function useLogin(config?: UseLoginConfig) {
setIsLoading(true);

return login(credentials)
.then(async (result) => {
if (result?.user) {
.then(async (response) => {
if (response?.user) {
if (
userConfig &&
(await verifySessionRoles(userConfig.supportedRoles))
) {
setUser(result.user);
setUser(response.user);

onLoginSuccess && (await onLoginSuccess(result));
onSuccess && (await onSuccess(response));

showToasts && toast.success(t("login.messages.success"));
} else {
setIsError(true);
onFailed && (await onFailed(response));

showToasts && toast.error(t("login.messages.permissionDenied"));
}
}
})
.catch(async (error) => {
setIsError(true);
onLoginFailed && (await onLoginFailed(error));
onFailed && (await onFailed(error));

if (showToasts) {
let errorMessage = "errors.otherErrors";
Expand Down
42 changes: 37 additions & 5 deletions packages/user/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
import {
acceptInvitation,
addInvitation,
getInvitationByToken,
resendInvitation,
revokeInvitation,
} from "./api/invitation";
import {
changePassword,
disableUser,
enableUser,
getIsFirstUser,
signUpFirstUser,
updateUserProfile,
} from "./api/user";
import AuthGoogleCallback from "./components/AuthGoogleCallback";
import DropdownUserMenu from "./components/DropdownUserMenu";
import {
Expand All @@ -18,6 +33,8 @@ import {
import UserProvider, { userContext } from "./context/UserProvider";
import { getUserData, removeUserData, setUserData } from "./helpers";
import {
useAcceptInvitation,
useChangePassword,
useEmailVerification,
useFirstUserSignup,
useLogin,
Expand All @@ -29,7 +46,6 @@ import {
UserEnabledSidebarOnlyLayout,
} from "./layouts";
import superTokens from "./supertokens";
import changePassword from "./supertokens/change-password";
import { forgotPassword } from "./supertokens/forgot-password";
import googleLogin from "./supertokens/google-login";
import { verifySessionRoles } from "./supertokens/helpers";
Expand Down Expand Up @@ -81,9 +97,12 @@ export {
SignupWrapper,
TermsAndConditions,
UserMenu,
UserProvider,
UsersTable,

// context
userContext,
UserProvider,

// layouts
UserEnabledBasicLayout,
UserEnabledSidebarLayout,
Expand All @@ -101,8 +120,7 @@ export {
Signup,
VerifyEmail,

// utilities
changePassword,
// supertoken utilities
forgotPassword,
getUserData,
googleLogin,
Expand All @@ -113,15 +131,29 @@ export {
setUserData,
signup,
superTokens,
userContext,
verifyEmail,
verifySessionRoles,

// hooks
useAcceptInvitation,
useChangePassword,
useEmailVerification,
useFirstUserSignup,
useLogin,
useUser,

// apis
changePassword,
disableUser,
enableUser,
getIsFirstUser,
signUpFirstUser,
updateUserProfile,
acceptInvitation,
addInvitation,
getInvitationByToken,
resendInvitation,
revokeInvitation,
};

export type {
Expand Down
37 changes: 0 additions & 37 deletions packages/user/src/supertokens/change-password.ts

This file was deleted.

2 changes: 2 additions & 0 deletions packages/user/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from "./invitation";
import {
AuthState,
ChangePasswordInputType,
ErrorResponse,
ExtendedUser,
LoginCredentials,
Expand All @@ -27,6 +28,7 @@ export type {
AccpetInvitationResponse,
AddInvitationResponse,
AuthState,
ChangePasswordInputType,
DzangolabReactUserConfig,
ErrorResponse,
ExtendedUser,
Expand Down
5 changes: 5 additions & 0 deletions packages/user/src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,8 @@ export type UpdateProfileInputType = {
givenName: string;
surname: string;
};

export type ChangePasswordInputType = {
newPassword: string;
oldPassword: string;
};
Loading

0 comments on commit 9cefb1b

Please sign in to comment.