-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1e104e8
commit 0530410
Showing
3 changed files
with
241 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<div className="flex flex-col gap-2 w-80 px-5"> | ||
<span className="text-xs text-custom-sidebar-text-400 font-semibold">My Account</span> | ||
<div className="flex flex-col gap-1 w-full"> | ||
{PROFILE_LINKS.map((link) => ( | ||
<Link key={link.href} href={link.href}> | ||
<a> | ||
<div | ||
className={`px-4 py-2 text-sm font-medium rounded-md ${ | ||
router.asPath === link.href | ||
? "bg-custom-primary-100/10 text-custom-primary-100" | ||
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80" | ||
}`} | ||
> | ||
{link.label} | ||
</div> | ||
</a> | ||
</Link> | ||
))} | ||
{PROFILE_LINKS.map((link) => { | ||
if (link.key === "change-password" && !enableEmailPassword) return; | ||
return ( | ||
<Link key={link.key} href={link.href}> | ||
<a> | ||
<div | ||
className={`px-4 py-2 text-sm font-medium rounded-md ${ | ||
router.asPath === link.href | ||
? "bg-custom-primary-100/10 text-custom-primary-100" | ||
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80" | ||
}`} | ||
> | ||
{link.label} | ||
</div> | ||
</a> | ||
</Link> | ||
); | ||
})} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<FormValues>({ 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 ( | ||
<div className="grid place-items-center h-screen w-full"> | ||
<Spinner /> | ||
</div> | ||
); | ||
|
||
return ( | ||
<form onSubmit={handleSubmit(handleChangePassword)} className="flex flex-col gap-8 my-10 w-full mr-9"> | ||
<div className="grid grid-col grid-cols-1 xl:grid-cols-2 2xl:grid-cols-3 items-center justify-between gap-10 w-full"> | ||
<div className="flex flex-col gap-1 "> | ||
<h4 className="text-sm">Current password</h4> | ||
<Controller | ||
control={control} | ||
name="old_password" | ||
rules={{ | ||
required: "This field is required", | ||
}} | ||
render={({ field: { value, onChange } }) => ( | ||
<Input | ||
id="old_password" | ||
type="password" | ||
value={value} | ||
onChange={onChange} | ||
placeholder="Old password" | ||
className="rounded-md font-medium w-full" | ||
hasError={Boolean(errors.old_password)} | ||
/> | ||
)} | ||
/> | ||
{errors.old_password && <span className="text-red-500 text-xs">{errors.old_password.message}</span>} | ||
</div> | ||
|
||
<div className="flex flex-col gap-1 "> | ||
<h4 className="text-sm">New password</h4> | ||
<Controller | ||
control={control} | ||
name="new_password" | ||
rules={{ | ||
required: "This field is required", | ||
}} | ||
render={({ field: { value, onChange } }) => ( | ||
<Input | ||
id="new_password" | ||
type="password" | ||
value={value} | ||
placeholder="New password" | ||
onChange={onChange} | ||
className="w-full" | ||
hasError={Boolean(errors.new_password)} | ||
/> | ||
)} | ||
/> | ||
{errors.new_password && <span className="text-red-500 text-xs">{errors.new_password.message}</span>} | ||
</div> | ||
|
||
<div className="flex flex-col gap-1 "> | ||
<h4 className="text-sm">Confirm password</h4> | ||
<Controller | ||
control={control} | ||
name="confirm_password" | ||
rules={{ | ||
required: "This field is required", | ||
}} | ||
render={({ field: { value, onChange } }) => ( | ||
<Input | ||
id="confirm_password" | ||
type="password" | ||
placeholder="Confirm password" | ||
value={value} | ||
onChange={onChange} | ||
className="w-full" | ||
hasError={Boolean(errors.confirm_password)} | ||
/> | ||
)} | ||
/> | ||
{errors.confirm_password && <span className="text-red-500 text-xs">{errors.confirm_password.message}</span>} | ||
</div> | ||
</div> | ||
|
||
<div className="flex items-center justify-between py-2"> | ||
<Button variant="primary" type="submit" loading={isSubmitting}> | ||
{isSubmitting ? "Changing password..." : "Change password"} | ||
</Button> | ||
</div> | ||
</form> | ||
); | ||
}); | ||
|
||
ChangePasswordPage.getLayout = function getLayout(page: ReactElement) { | ||
return ( | ||
<ProfileSettingsLayout header={<ProfileSettingsHeader title="Change Password" />}>{page}</ProfileSettingsLayout> | ||
); | ||
}; | ||
|
||
export default ChangePasswordPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters