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 (
+
+ );
+});
+
+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)