Skip to content

Commit

Permalink
SetPassword
Browse files Browse the repository at this point in the history
  • Loading branch information
hoshinotsuyoshi committed Oct 13, 2024
1 parent 979596e commit 3b61cc5
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 9 deletions.
4 changes: 2 additions & 2 deletions backend/app/graphql/mutations/set_password.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ def resolve(password:)
user = User.transaction do
current_user.lock!
if current_user.before_set_own_password_status?
curret_user.update!(onboarding_status: :onboarded)
curret_user
current_user.update!(onboarding_status: :onboarded, password:)
current_user
else
nil
end
Expand Down
6 changes: 6 additions & 0 deletions backend/spec/system/scenarios/sign_up_flow_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@

expect(page).to have_content('Email verification successful!')
expect(page).not_to have_content('Email verification successful!')
expect(page).to have_content('New Password')
expect(page).to have_content('Confirm Password')
password = SecureRandom.uuid
fill_in "password", with: password
fill_in "confirmPassword", with: password
click_button "Set Password"
expect(page).to have_content("hello, It's me!")
expect(page).to have_content(email)
end
Expand Down
160 changes: 158 additions & 2 deletions frontend/src/components/SetPassword/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,160 @@
import React from 'react'
import { useMutation } from '@apollo/client'
import React, { type FormEvent, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { css } from '../../../styled-system/css'
import { SetPasswordInputSchema } from '../../generated/graphql'
import { SetPasswordDocument } from '../../generated/graphql'
import { ROUTES } from '../../routes'

export const SetPassword = () => {
return <div />
const navigate = useNavigate()
const [password, setPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
const [businessLogicError, setBusinessLogicError] = useState('')
const [passwordSet, setPasswordSet] = useState(false)

const [setPasswordMutation, { loading, error }] =
useMutation(SetPasswordDocument)

const handleSubmit = async (e: FormEvent) => {
e.preventDefault()

if (password !== confirmPassword) {
setBusinessLogicError('Passwords do not match.')
return
}

const validationResult = SetPasswordInputSchema().safeParse({
password,
})

if (!validationResult.success) {
setBusinessLogicError('Invalid input. Please check your data.')
console.error(validationResult.error)
return
}

try {
const { data, errors } = await setPasswordMutation({
variables: {
input: validationResult.data,
},
})

if (data?.setPassword?.user) {
setPasswordSet(true)
console.log('Password set successfully')
navigate(ROUTES.ME)
} else if (data?.setPassword?.errors[0].__typename === 'SomeError') {
setBusinessLogicError('Error setting password.')
console.log('Password set error')
} else if (errors?.length) {
console.log('GraphQL failed', errors[0].message)
} else {
console.log('failed')
}
} catch (e) {
console.error('error', e)
}
}

return (
<div
className={css({
maxWidth: '400px',
margin: '0 auto',
padding: '20px',
borderRadius: '8px',
backgroundColor: 'white',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
})}
>
<h2
className={css({
fontSize: '24px',
textAlign: 'center',
marginBottom: '20px',
})}
>
Set Password
</h2>
{passwordSet || (
<form onSubmit={handleSubmit}>
<div className={css({ marginBottom: '15px' })}>
<label
className={css({
display: 'block',
marginBottom: '5px',
fontSize: '16px',
})}
htmlFor="password"
>
New Password:
</label>
<input
type="password"
value={password}
name="password"
onChange={(e) => setPassword(e.target.value)}
required
className={css({
width: '100%',
padding: '10px',
fontSize: '16px',
borderRadius: '4px',
border: '1px solid #ccc',
})}
/>
</div>
<div className={css({ marginBottom: '15px' })}>
<label
className={css({
display: 'block',
marginBottom: '5px',
fontSize: '16px',
})}
htmlFor="confirmPassword"
>
Confirm Password:
</label>
<input
type="password"
value={confirmPassword}
name="confirmPassword"
onChange={(e) => setConfirmPassword(e.target.value)}
required
className={css({
width: '100%',
padding: '10px',
fontSize: '16px',
borderRadius: '4px',
border: '1px solid #ccc',
})}
/>
</div>
<button
type="submit"
className={css({
width: '100%',
padding: '10px',
fontSize: '16px',
borderRadius: '4px',
backgroundColor: '#223344',
color: 'white',
border: 'none',
cursor: 'pointer',
transition: 'background-color 0.3s',
})}
>
{loading ? 'Setting Password...' : 'Set Password'}
</button>
</form>
)}
{passwordSet && <p>Password set successfully!</p>}
{error && <p style={{ color: 'red' }}>Error: {error.message}</p>}
{businessLogicError.length > 0 && (
<p style={{ color: 'red' }}>Error: {businessLogicError}</p>
)}
</div>
)
}
14 changes: 9 additions & 5 deletions frontend/src/components/VerifyEmailAddress/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export const VerifyEmailAddress = () => {
const queryParams = new URLSearchParams(location.search)
const signedId = queryParams.get('signed_id')

const [verifyEmailAddress] = useMutation(VerifyEmailAddressDocument)
const [verifyEmailAddress, { loading }] = useMutation(
VerifyEmailAddressDocument,
)

useEffect(() => {
const verifyEmail = async () => {
Expand All @@ -34,10 +36,10 @@ export const VerifyEmailAddress = () => {
if (response.data?.verifyEmailAddress?.user) {
setSuccess(true)
setTimeout(() => {
navigate(ROUTES.ME)
navigate(ROUTES.SET_PASSWORD)
}, 1000)
} else {
setError('Failed to verify email. Please try again.')
loading || setError('Failed to verify email. Please try again.')
}
} catch (err) {
if (err instanceof Error) {
Expand All @@ -53,13 +55,15 @@ export const VerifyEmailAddress = () => {
}

verifyEmail()
}, [signedId, verifyEmailAddress, navigate])
}, [signedId, verifyEmailAddress, navigate, loading])

return (
<div style={{ textAlign: 'center', marginTop: '50px' }}>
{error && <p style={{ color: 'red' }}>{error}</p>}
{success ? (
<p>Email verification successful! Redirecting to {ROUTES.ME}...</p>
<p>
Email verification successful! Redirecting to {ROUTES.SET_PASSWORD}...
</p>
) : (
!error && <p>Verifying your email...</p>
)}
Expand Down

0 comments on commit 3b61cc5

Please sign in to comment.