Skip to content

Commit

Permalink
feat: 1639 - add password view/hide functionality on the login/regist…
Browse files Browse the repository at this point in the history
…er pages (#1642)

* 1639 - add password view/hide functionality on the login/register pages

* fix lint issues

* fix filename import

* remove unused prop

* add password view/hide functionality on the reset password form

* add password view/hide functionality on the change password form
  • Loading branch information
adrianboros authored Sep 23, 2024
1 parent 105ea1e commit 2839dac
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 64 deletions.
36 changes: 36 additions & 0 deletions packages/wallet/frontend/src/components/icons/Eye.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { SVGProps } from 'react'

export const Eye = (props: SVGProps<SVGSVGElement>) => {
return (
<svg
width="30px"
height="30px"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M1 12C1 12 5 4 12 4C19 4 23 12 23 12"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M1 12C1 12 5 20 12 20C19 20 23 12 23 12"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
/>
<circle
cx="12"
cy="12"
r="3"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
}
34 changes: 34 additions & 0 deletions packages/wallet/frontend/src/components/icons/SlashEye.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { SVGProps } from 'react'

export const SlashEye = (props: SVGProps<SVGSVGElement>) => {
return (
<svg
width="30px"
height="30px"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M2 2L22 22"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M6.71277 6.7226C3.66479 8.79527 2 12 2 12C2 12 5.63636 19 12 19C14.0503 19 15.8174 18.2734 17.2711 17.2884M11 5.05822C11.3254 5.02013 11.6588 5 12 5C18.3636 5 22 12 22 12C22 12 21.3082 13.3317 20 14.8335"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M14 14.2362C13.4692 14.7112 12.7684 15.0001 12 15.0001C10.3431 15.0001 9 13.657 9 12.0001C9 11.1764 9.33193 10.4303 9.86932 9.88818"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,37 @@ import { Button } from '@/ui/Button'
import { Form } from '@/ui/forms/Form'
import { useZodForm } from '@/lib/hooks/useZodForm'
import { Input } from '@/ui/forms/Input'
import { Eye } from '@/components/icons/Eye'
import { SlashEye } from '@/components/icons/SlashEye'
import { changePasswordSchema, userService } from '@/lib/api/user'
import { useDialog } from '@/lib/hooks/useDialog'
import { ErrorDialog } from '../dialogs/ErrorDialog'
import { getObjectKeys } from '@/utils/helpers'
import { SuccessDialog } from '../dialogs/SuccessDialog'
import { usePasswordContext } from '@/lib/context/password'
import { useEffect } from 'react'
import { useEffect, useState } from 'react'

export const ChangePasswordForm = () => {
const [isCurrentPasswordVisible, setCurrentPasswordVisible] =
useState<boolean>(false)
const [isNewPasswordVisible, setNewPasswordVisible] = useState<boolean>(false)
const [isConfirmNewPasswordVisible, setConfirmNewPasswordVisible] =
useState<boolean>(false)

const changePasswordForm = useZodForm({
schema: changePasswordSchema
})
const [openDialog, closeDialog] = useDialog()
const { setIsChangePassword } = usePasswordContext()
const toggleCurrentPasswordVisibility = () => {
setCurrentPasswordVisible(!isCurrentPasswordVisible)
}
const toggleNewPasswordVisibility = () => {
setNewPasswordVisible(!isNewPasswordVisible)
}
const toggleConfirmNewPasswordVisibility = () => {
setConfirmNewPasswordVisible(!isConfirmNewPasswordVisible)
}

useEffect(() => {
changePasswordForm.setFocus('oldPassword')
Expand Down Expand Up @@ -67,29 +84,53 @@ export const ChangePasswordForm = () => {
}
}}
>
<Input
required
type="password"
label="Old Password"
error={changePasswordForm.formState.errors.oldPassword?.message}
{...changePasswordForm.register('oldPassword')}
/>
<Input
required
type="password"
label="New Password"
error={changePasswordForm.formState.errors.newPassword?.message}
{...changePasswordForm.register('newPassword')}
/>
<Input
required
type="password"
label="Confirm New Password"
error={
changePasswordForm.formState.errors.confirmNewPassword?.message
}
{...changePasswordForm.register('confirmNewPassword')}
/>
<div className="relative">
<Input
required
type={isCurrentPasswordVisible ? 'text' : 'password'}
label="Old Password"
error={changePasswordForm.formState.errors.oldPassword?.message}
{...changePasswordForm.register('oldPassword')}
/>
<span
onClick={toggleCurrentPasswordVisibility}
className="absolute right-2.5 top-1/2 cursor-pointer"
>
{isCurrentPasswordVisible ? <SlashEye /> : <Eye />}
</span>
</div>
<div className="relative">
<Input
required
type={isNewPasswordVisible ? 'text' : 'password'}
label="New Password"
error={changePasswordForm.formState.errors.newPassword?.message}
{...changePasswordForm.register('newPassword')}
/>
<span
onClick={toggleNewPasswordVisibility}
className="absolute right-2.5 top-1/2 cursor-pointer"
>
{isNewPasswordVisible ? <SlashEye /> : <Eye />}
</span>
</div>
<div className="relative">
<Input
required
type={isConfirmNewPasswordVisible ? 'text' : 'password'}
label="Confirm New Password"
error={
changePasswordForm.formState.errors.confirmNewPassword?.message
}
{...changePasswordForm.register('confirmNewPassword')}
/>
<span
onClick={toggleConfirmNewPasswordVisibility}
className="absolute right-2.5 top-1/2 cursor-pointer"
>
{isConfirmNewPasswordVisible ? <SlashEye /> : <Eye />}
</span>
</div>
<div className="mt-2 flex justify-end">
<Button type="submit" aria-label="Save password">
Save password
Expand Down
30 changes: 22 additions & 8 deletions packages/wallet/frontend/src/pages/auth/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@ import { useZodForm } from '@/lib/hooks/useZodForm'
import { Input } from '@/ui/forms/Input'
import { Link } from '@/ui/Link'
import { Play } from '@/components/icons/Play'
import { Eye } from '@/components/icons/Eye'
import { SlashEye } from '@/components/icons/SlashEye'
import { useRouter } from 'next/router'
import Image from 'next/image'
import { userService } from '@/lib/api/user'
import { useDialog } from '@/lib/hooks/useDialog'
import { SuccessDialog } from '@/components/dialogs/SuccessDialog'
import { ErrorDialog } from '@/components/dialogs/ErrorDialog'
import { NextPageWithLayout } from '@/lib/types/app'
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import { useTheme } from 'next-themes'
import { loginSchema } from '@wallet/shared'

const LoginPage: NextPageWithLayout = () => {
const [openDialog, closeDialog] = useDialog()
const [isPasswordVisible, setPasswordVisible] = useState<boolean>(false)
const router = useRouter()
const callBackUrl =
router.asPath.indexOf('callbackUrl') !== -1
Expand Down Expand Up @@ -55,6 +58,9 @@ const LoginPage: NextPageWithLayout = () => {
)
}
}
const togglePasswordVisibility = () => {
setPasswordVisible(!isPasswordVisible)
}
useEffect(() => {
loginForm.setFocus('email')
}, [loginForm])
Expand Down Expand Up @@ -105,13 +111,21 @@ const LoginPage: NextPageWithLayout = () => {
error={loginForm.formState.errors.email?.message}
label="E-mail"
/>
<Input
required
type="password"
{...loginForm.register('password')}
error={loginForm.formState.errors.password?.message}
label="Password"
/>
<div className="relative">
<Input
required
type={isPasswordVisible ? 'text' : 'password'}
{...loginForm.register('password')}
error={loginForm.formState.errors.password?.message}
label="Password"
/>
<span
onClick={togglePasswordVisibility}
className="absolute right-2.5 top-1/2 cursor-pointer"
>
{isPasswordVisible ? <SlashEye /> : <Eye />}
</span>
</div>
<Link
href="forgot"
className="text-sm font-extralight text-green underline dark:text-green-neon"
Expand Down
61 changes: 44 additions & 17 deletions packages/wallet/frontend/src/pages/auth/reset/[token].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { useZodForm } from '@/lib/hooks/useZodForm'
import { Input } from '@/ui/forms/Input'
import { Link } from '@/ui/Link'
import Image from 'next/image'
import { Eye } from '@/components/icons/Eye'
import { SlashEye } from '@/components/icons/SlashEye'
import { resetPasswordSchema, userService } from '@/lib/api/user'
import { getObjectKeys } from '@/utils/helpers'
import { NextPageWithLayout } from '@/lib/types/app'
Expand All @@ -14,6 +16,7 @@ import { SuccessDialog } from '@/components/dialogs/SuccessDialog'
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'
import { z } from 'zod'
import { useTheme } from 'next-themes'
import { useState } from 'react'

type ResetPasswordPageProps = InferGetServerSidePropsType<
typeof getServerSideProps
Expand All @@ -24,6 +27,9 @@ const ResetPasswordPage: NextPageWithLayout<ResetPasswordPageProps> = ({
isValid
}) => {
const [openDialog, closeDialog] = useDialog()
const [isPasswordVisible, setPasswordVisible] = useState<boolean>(false)
const [isConfirmPasswordVisible, setConfirmPasswordVisible] =
useState<boolean>(false)
const resetPasswordForm = useZodForm({
schema: resetPasswordSchema,
defaultValues: {
Expand All @@ -35,7 +41,12 @@ const ResetPasswordPage: NextPageWithLayout<ResetPasswordPageProps> = ({
theme.theme === 'dark'
? '/bird-envelope-dark.webp'
: '/bird-envelope-light.webp'

const togglePasswordVisibility = () => {
setPasswordVisible(!isPasswordVisible)
}
const toggleConfirmPasswordVisibility = () => {
setConfirmPasswordVisible(!isConfirmPasswordVisible)
}
return (
<>
<HeaderLogo header="Reset Password" />
Expand Down Expand Up @@ -74,22 +85,38 @@ const ResetPasswordPage: NextPageWithLayout<ResetPasswordPageProps> = ({
}
}}
>
<Input
required
type="password"
{...resetPasswordForm.register('password')}
error={resetPasswordForm.formState.errors.password?.message}
label="Password"
/>
<Input
required
type="password"
{...resetPasswordForm.register('confirmPassword')}
error={
resetPasswordForm.formState.errors.confirmPassword?.message
}
label="Confirm password"
/>
<div className="relative">
<Input
required
type={isPasswordVisible ? 'text' : 'password'}
{...resetPasswordForm.register('password')}
error={resetPasswordForm.formState.errors.password?.message}
label="Password"
/>
<span
onClick={togglePasswordVisibility}
className="absolute right-2.5 top-1/2 cursor-pointer"
>
{isPasswordVisible ? <SlashEye /> : <Eye />}
</span>
</div>
<div className="relative">
<Input
required
type={isConfirmPasswordVisible ? 'text' : 'password'}
{...resetPasswordForm.register('confirmPassword')}
error={
resetPasswordForm.formState.errors.confirmPassword?.message
}
label="Confirm password"
/>
<span
onClick={toggleConfirmPasswordVisibility}
className="absolute right-2.5 top-1/2 cursor-pointer"
>
{isConfirmPasswordVisible ? <SlashEye /> : <Eye />}
</span>
</div>
<Input
required
type="hidden"
Expand Down
Loading

0 comments on commit 2839dac

Please sign in to comment.