From 85907b32d1f2bf1c4e18195d66350600c0e96b93 Mon Sep 17 00:00:00 2001 From: Ramesh Kumar Chandra <31303617+rameshkumarchandra@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:38:50 +0530 Subject: [PATCH] feat: change password page (#2847) --- .../profile/settings-sidebar.tsx | 61 ++++-- web/pages/me/profile/change-password.tsx | 189 ++++++++++++++++++ web/services/user.service.ts | 8 + 3 files changed, 241 insertions(+), 17 deletions(-) create mode 100644 web/pages/me/profile/change-password.tsx diff --git a/web/layouts/settings-layout/profile/settings-sidebar.tsx b/web/layouts/settings-layout/profile/settings-sidebar.tsx index 1f87a0382..f5160cd5b 100644 --- a/web/layouts/settings-layout/profile/settings-sidebar.tsx +++ b/web/layouts/settings-layout/profile/settings-sidebar.tsx @@ -1,48 +1,75 @@ import React from "react"; import { useRouter } from "next/router"; import Link from "next/link"; +import { observer } from "mobx-react-lite"; +// mobx +import { useMobxStore } from "lib/mobx/store-provider"; const PROFILE_LINKS: Array<{ + key: string; label: string; href: string; }> = [ { + key: "profile", label: "Profile", href: `/me/profile`, }, { + key: "change-password", + label: "Change password", + href: `/me/profile/change-password`, + }, + { + key: "activity", label: "Activity", href: `/me/profile/activity`, }, { + key: "preferences", label: "Preferences", href: `/me/profile/preferences`, }, ]; -export const ProfileSettingsSidebar = () => { +export const ProfileSettingsSidebar = observer(() => { const router = useRouter(); + const { + appConfig: { envConfig }, + } = useMobxStore(); + const enableEmailPassword = + envConfig && + (envConfig?.email_password_login || + !( + envConfig?.email_password_login || + envConfig?.magic_login || + envConfig?.google_client_id || + envConfig?.github_client_id + )); return (
My Account
- {PROFILE_LINKS.map((link) => ( - - -
- {link.label} -
-
- - ))} + {PROFILE_LINKS.map((link) => { + if (link.key === "change-password" && !enableEmailPassword) return; + return ( + + +
+ {link.label} +
+
+ + ); + })}
); -}; +}); diff --git a/web/pages/me/profile/change-password.tsx b/web/pages/me/profile/change-password.tsx new file mode 100644 index 000000000..e7bc60a77 --- /dev/null +++ b/web/pages/me/profile/change-password.tsx @@ -0,0 +1,189 @@ +import { ReactElement, useEffect, useState } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; +import { Controller, useForm } from "react-hook-form"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// services +import { UserService } from "services/user.service"; +// hooks +import useToast from "hooks/use-toast"; +// layout +import { ProfileSettingsLayout } from "layouts/settings-layout"; +// components +import { ProfileSettingsHeader } from "components/headers"; +// ui +import { Button, Input, Spinner } from "@plane/ui"; +// types +import { NextPageWithLayout } from "types/app"; + +interface FormValues { + old_password: string; + new_password: string; + confirm_password: string; +} + +const defaultValues: FormValues = { + old_password: "", + new_password: "", + confirm_password: "", +}; + +const userService = new UserService(); + +const ChangePasswordPage: NextPageWithLayout = observer(() => { + const [isPageLoading, setIsPageLoading] = useState(true); + + const { + appConfig: { envConfig }, + } = useMobxStore(); + + const router = useRouter(); + + // use form + const { + control, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ defaultValues }); + const { setToastAlert } = useToast(); + + const handleChangePassword = async (formData: FormValues) => { + if (formData.new_password !== formData.confirm_password) { + setToastAlert({ + type: "error", + title: "Error!", + message: "The new password and the confirm password don't match.", + }); + return; + } + await userService + .changePassword(formData) + .then(() => { + setToastAlert({ + type: "success", + title: "Success!", + message: "Password changed successfully.", + }); + }) + .catch((error) => { + setToastAlert({ + type: "error", + title: "Error!", + message: error?.error ?? "Something went wrong. Please try again.", + }); + }); + }; + + useEffect(() => { + if (!envConfig) return; + + const enableEmailPassword = + envConfig?.email_password_login || + !( + envConfig?.email_password_login || + envConfig?.magic_login || + envConfig?.google_client_id || + envConfig?.github_client_id + ); + + if (!enableEmailPassword) router.push("/me/profile"); + else setIsPageLoading(false); + }, []); + + if (isPageLoading) + return ( +
+ +
+ ); + + return ( +
+
+
+

Current password

+ ( + + )} + /> + {errors.old_password && {errors.old_password.message}} +
+ +
+

New password

+ ( + + )} + /> + {errors.new_password && {errors.new_password.message}} +
+ +
+

Confirm password

+ ( + + )} + /> + {errors.confirm_password && {errors.confirm_password.message}} +
+
+ +
+ +
+
+ ); +}); + +ChangePasswordPage.getLayout = function getLayout(page: ReactElement) { + return ( + }>{page} + ); +}; + +export default ChangePasswordPage; diff --git a/web/services/user.service.ts b/web/services/user.service.ts index 4b9037287..7d7175153 100644 --- a/web/services/user.service.ts +++ b/web/services/user.service.ts @@ -139,6 +139,14 @@ export class UserService extends APIService { }); } + async changePassword(data: { old_password: string; new_password: string; confirm_password: string }): Promise { + return this.post(`/api/users/me/change-password/`, data) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } + async getUserProfileData(workspaceSlug: string, userId: string): Promise { return this.get(`/api/workspaces/${workspaceSlug}/user-stats/${userId}/`) .then((response) => response?.data)