-
Notifications
You must be signed in to change notification settings - Fork 3
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
Showing
2 changed files
with
228 additions
and
63 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 |
---|---|---|
@@ -0,0 +1,77 @@ | ||
"use server" | ||
|
||
import {FieldError, GeneralError} from "../../../lib/auth/guards"; | ||
import {z} from "zod"; | ||
import {revalidatePath} from "next/cache"; | ||
|
||
export interface RegisterResponse { | ||
success: boolean; | ||
error?: GeneralError | FieldError; | ||
} | ||
|
||
|
||
export const registerAccount = async (_: RegisterResponse, formData: FormData): Promise<RegisterResponse> => { | ||
const schema = z.object({ | ||
email: z.string().email(), | ||
unique_identifier: z.string().min(3), | ||
username: z.string().min(3), | ||
password: z.string().min(6) | ||
}); | ||
|
||
const parsed = schema.safeParse({ | ||
email: formData.get('email'), | ||
unique_identifier: formData.get('unique_identifier'), | ||
username: formData.get('username'), | ||
password: formData.get('password') | ||
}); | ||
|
||
if (!parsed.success) { | ||
const fieldErrors = parsed.error.errors.reduce((acc, error) => { | ||
return {...acc, [error.path[0]]: error.message} | ||
}, {}); | ||
|
||
return {success: false, error: {status: 400, error: fieldErrors}}; | ||
} | ||
|
||
const password2 = formData.get('password2'); | ||
if (password2 !== parsed.data.password) { | ||
return { | ||
success: false, | ||
error: | ||
{ | ||
status: 400, | ||
error: { | ||
password2: ['Passwords do not match'] | ||
} | ||
} | ||
} | ||
} | ||
|
||
const body = { | ||
email: parsed.data.email, | ||
unique_identifier: parsed.data.unique_identifier, | ||
username: parsed.data.username, | ||
password: parsed.data.password | ||
} | ||
|
||
try { | ||
const response = await fetch(`${process.env.CC_API_URL}/accounts/register`, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify(body), | ||
}); | ||
|
||
if (!response.ok) { | ||
const error = await response.json(); | ||
return {success: false, error: error}; | ||
} | ||
|
||
revalidatePath('register'); | ||
|
||
return {success: true}; | ||
} catch (error) { | ||
return {success: false, error: {status: 500, error: 'An unexpected error occurred'}}; | ||
} | ||
} |
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,77 +1,165 @@ | ||
"use client" | ||
|
||
import React from "react"; | ||
import Button from "../../common/uiLibrary/Button"; | ||
import FormContainer from "../../common/uiLibrary/Layouters/formContainer"; | ||
import TextField from "../../common/uiLibrary/forms/textField"; | ||
import AlternativeActions from "../../common/uiLibrary/forms/alternativeActions"; | ||
import ContactInformation from "../../(home)/contactInformation"; | ||
import {useFormState} from "react-dom"; | ||
import {registerAccount, RegisterResponse} from "./actions"; | ||
import {isFieldError} from "../../../lib/auth/guards"; | ||
import { useFormStatus } from "react-dom"; | ||
import GenericLoading from "../../common/uiLibrary/genericLoading"; | ||
|
||
const RegisterPage = () => { | ||
return ( | ||
|
||
const initialState: RegisterResponse = { | ||
success: false, | ||
error: undefined | ||
} | ||
const [state, formAction] = useFormState(registerAccount, initialState); | ||
|
||
const uniqueIdHelperText = () => | ||
<> | ||
Choose a unique identifier for your account. <b>This identifier is permanent | ||
and cannot be changed later</b>. Please choose carefully! | ||
</> | ||
|
||
const usernameHelperText = () => | ||
<> | ||
Choose a username that will be displayed to other users. This can be changed | ||
later. | ||
</> | ||
|
||
const registerForm = () => { | ||
return ( | ||
<> | ||
<TextField | ||
label='Your email' | ||
id='email' | ||
name='email' | ||
type='email' | ||
placeholder='[email protected]' | ||
shadow | ||
helperText={(state.error && isFieldError(state.error) && state.error?.error.email) ? | ||
<div className='text-red-700'> | ||
{state.error?.error.email} | ||
</div> | ||
: | ||
usernameHelperText() | ||
} | ||
/> | ||
|
||
<TextField | ||
label='Your unique identifier' | ||
id='unique_identifier' | ||
name='unique_identifier' | ||
type='text' | ||
placeholder='YourUniqueID' | ||
required | ||
shadow | ||
helperText={(state.error && isFieldError(state.error) && state.error?.error.unique_identifier) ? | ||
<div className='text-red-700'> | ||
{state.error?.error.unique_identifier} | ||
</div> | ||
: | ||
uniqueIdHelperText() | ||
} | ||
/> | ||
|
||
<TextField | ||
label='Username' | ||
id='username' | ||
name='username' | ||
type='text' | ||
placeholder='YourUsername' | ||
required | ||
shadow | ||
helperText={(state.error && isFieldError(state.error) && state.error?.error.username) ? | ||
<div className='text-red-700'> | ||
{state.error?.error.username} | ||
</div> | ||
: | ||
usernameHelperText() | ||
} | ||
/> | ||
|
||
<TextField | ||
label='Your password' | ||
id='password' | ||
name='password' | ||
type='password' | ||
placeholder='********' | ||
shadow | ||
required | ||
helperText={ state.error && isFieldError(state.error) && state.error?.error.password && | ||
<div className='text-red-700'> | ||
{state.error?.error.password} | ||
</div> | ||
} | ||
/> | ||
|
||
<TextField | ||
label='Confirm your password' | ||
id='password2' | ||
name='password2' | ||
type='password' | ||
placeholder='********' | ||
shadow | ||
required | ||
helperText={(state.error && isFieldError(state.error) && state.error?.error.password2) ? | ||
<div className='text-red-700'> | ||
{state.error?.error.password2} | ||
</div> | ||
: | ||
usernameHelperText() | ||
} | ||
/> | ||
|
||
<Button type="submit" className="mt-4 w-full" filled>Register</Button> | ||
|
||
<AlternativeActions | ||
links={[ | ||
{link: '/login', linkText: 'Already have an account?'} | ||
]} | ||
/> | ||
</> | ||
) | ||
} | ||
const successMessage = () => { | ||
return ( | ||
<div className='flex flex-col gap-4'> | ||
<h3 className="text-lg text-center font-medium text-green-800">Success!</h3> | ||
<p>An email has been sent with instructions to confirm your account.</p> | ||
<p>Please check your inbox and follow the instructions to complete the process.</p> | ||
<p>Didn't receive the email? Check your Spam folder or try resending the email. Ensure your email | ||
address is | ||
entered correctly.</p> | ||
</div> | ||
) | ||
} | ||
|
||
const FormOrLoading = () => { | ||
const {pending} = useFormStatus(); | ||
if (pending) { | ||
return ( | ||
<div className='absolute top-0 left-0 w-full h-full flex flex-col justify-center items-center bg-black bg-opacity-70 z-10'> | ||
<GenericLoading/> | ||
Loading... | ||
</div> | ||
) | ||
} else { | ||
return registerForm() | ||
} | ||
} | ||
|
||
return ( | ||
<div className='flex flex-col ' style={{minHeight: 'calc(100vh - 60px)'}}> | ||
|
||
<div className='flex-grow'> | ||
<FormContainer title='Create a new account'> | ||
<TextField | ||
label='Your email' | ||
id='email' | ||
type='email' | ||
placeholder='[email protected]' | ||
shadow | ||
/> | ||
|
||
<TextField | ||
label='Your unique identifier' | ||
id='unique_identifier' | ||
type='text' | ||
placeholder='YourUniqueID' | ||
required | ||
shadow | ||
helperText={ | ||
<> | ||
Choose a unique identifier for your account. <b>This identifier is permanent | ||
and cannot be changed later</b>. Please choose carefully! | ||
</>} | ||
/> | ||
|
||
<TextField | ||
label='Username' | ||
id='username' | ||
type='text' | ||
placeholder='YourUsername' | ||
required | ||
shadow | ||
helperText={ | ||
<> | ||
Choose a username that will be displayed to other users. This can be changed | ||
later. | ||
</>} | ||
/> | ||
|
||
<TextField | ||
label='Your password' | ||
id='password' | ||
type='password' | ||
placeholder='********' | ||
shadow | ||
required | ||
/> | ||
|
||
<TextField | ||
label='Confirm your password' | ||
id='password2' | ||
type='password' | ||
placeholder='********' | ||
shadow | ||
required | ||
/> | ||
|
||
<Button type="submit" className="mt-4 w-full" filled>Register</Button> | ||
|
||
<AlternativeActions | ||
links={[ | ||
{link: '/login', linkText: 'Already have an account?'} | ||
]} | ||
/> | ||
<FormContainer action={formAction} title='Create a new account'> | ||
{state.success ? successMessage() : FormOrLoading()} | ||
</FormContainer> | ||
</div> | ||
<ContactInformation/> | ||
|