Untitled
unknown
jsx
2 years ago
12 kB
9
Indexable
import { Field, Formik, Form } from 'formik'; import styles from './signupform.module.scss'; import localFont from 'next/font/local'; import Button from '../Button/Button'; import Container from '../Container/Container'; import parse from 'html-react-parser'; import { useRouter } from 'next/router'; import * as Yup from 'yup'; import { useState } from 'react'; import Image from 'next/image'; import showIcon from '../../../public/images/icons/show.svg'; import hideIcon from '../../../public/images/icons/hide.svg'; import Link from 'next/link'; const sansRegular = localFont({ src: '../../../public/fonts/Soure_Sans_Pro/SourceSansPro-Regular.ttf', }); const sansLight = localFont({ src: '../../../public/fonts/Soure_Sans_Pro/SourceSansPro-Light.ttf', }); const sansBold = localFont({ src: '../../../public/fonts/Soure_Sans_Pro/SourceSansPro-Bold.ttf', }); const SignupForm = () => { const router = useRouter(); const [listOfAddresses, setListOfAddresses] = useState({}); const [postcode, setPostcode] = useState(''); const [enableManualAddress, setEnableManualAddress] = useState(false); const [showPassword, setShowPassword] = useState(false); const [findAddressButtonState, setFindAddressButtonState] = useState(false); const handlePasswordVisiblity = () => { setShowPassword(!showPassword); }; const fieldTypes = { email: 'email', text: 'text', password: 'password', }; const formFieldSchema = { email: { label: 'Email', value: '', type: fieldTypes.email }, password: { label: 'Password', value: '', type: fieldTypes.password }, confirmPassword: { label: 'Confirm Password', value: '', type: fieldTypes.password, }, firstName: { label: 'First Name', value: '', type: fieldTypes.text }, lastName: { label: 'Last Name', value: '', type: fieldTypes.text }, addressLine1: { label: 'Address Line 1', value: '', type: fieldTypes.text }, addressLine2: { label: 'Address Line 2 (optional)', value: '', type: fieldTypes.text, }, addressLine3: { label: 'Address Line 3 (optional)', value: '', type: fieldTypes.text, }, townOrCity: { label: 'Town or City', value: '', type: fieldTypes.text }, country: { label: 'Country', value: '', type: fieldTypes.text }, postcode: { label: 'Postcode', value: '', type: fieldTypes.text }, }; // Reduce formFieldSchema to key: value const formInitialValues = Object.keys(formFieldSchema).reduce((acc, key) => { acc[key] = formFieldSchema[key].value || ''; return acc; }, {}); const reducer = (acc, key) => { acc[key] = formInitialValues[key].value || ''; return acc; }; // Rendered form fields if manual address == false const formPrimarySection = [ 'email', 'password', 'confirmPassword', 'firstName', 'lastName', 'postcode', ].reduce(reducer, {}); // Handle find address onclick const handleFindAddress = () => { if (postcode.length > 1) { fetchPostcode(postcode); } }; // Handle address box list items const handleAddressItem = (e, setFieldValue) => { const addressParts = e.currentTarget.innerHTML.split(', ').map(parse); const [addressLine1, townOrCity, country] = addressParts; setFieldValue('addressLine1', parse(addressLine1)); setFieldValue('townOrCity', parse(townOrCity)); setFieldValue('country', parse(country)); setFindAddressButtonState(true); setListOfAddresses({}); setEnableManualAddress(true); }; const fetchPostcode = async (postcode) => { const response = await fetch( `https://api.ideal-postcodes.co.uk/v1/postcodes/${postcode}?api_key=ak_lfkqb60k8f1NjrwnkczS9p13VTk2N` ); const postcodeData = await response.json(); if (postcodeData?.result?.length < 1) { throw new RangeError('No addresses were found for the given postcode'); } const formattedAddresses = postcodeData.result.map((address) => { return `${address.line_1}, ${address.post_town}, ${address.country}`; }); setListOfAddresses(formattedAddresses); }; // Validate postcode const validatePostcode = (postcode) => { let error; const postcodeRegex = /^[a-zA-Z]{1,2}[0-9]{1,2}[a-zA-Z]?\s?[0-9][a-zA-Z]{2}$/i; if (!postcodeRegex.test(postcode)) { error = 'Invalid UK postcode'; } else { setPostcode(postcode); } return error; }; const handleFormSubmission = (e, email) => { if (!Object.keys(e).length >= 1) { router.push({ pathname: '/signup-success', query: { email: email }, }); } }; // Control field types const getInputFieldType = (field) => { const fieldType = formFieldSchema[field]?.type; if (fieldType === 'password' && showPassword) { return 'text'; } return fieldType || 'text'; }; // Generate form sections (state dependant) const formToRender = !enableManualAddress ? formPrimarySection : formInitialValues; return ( <Formik initialValues={formInitialValues} validationSchema={Yup.object().shape({ email: Yup.string() .email('Please enter a valid email') .required('Email is required'), password: Yup.string() .min(8, 'Password must be at least 8 characters') .required('Password is required'), confirmPassword: Yup.string() .oneOf([Yup.ref('password'), null], 'Passwords must match') .min(8, 'Password must be at least 8 characters') .required('Confirm password is required'), firstName: Yup.string().required('First name is required'), lastName: Yup.string().required('Last name is required'), postcode: Yup.string().required('Postcode is required'), addressLine1: Yup.string().required('Enter an Address'), townOrCity: Yup.string().required('Enter a Town or City'), country: Yup.string().required('Enter a Country'), })} > {({ errors, touched, values, setFieldValue, setFieldTouched, validateField, validateForm, }) => ( <Container type="content_sm" className={styles.component}> <div className={styles.component}> {/* Form Create your Account into */} <div className={styles.form_intro}> <h3 className={sansRegular.className}>Create your Account</h3> <p className={sansLight.className}> In 30 seconds you'll be a sign up pro! </p> </div> {/* Main form */} <Form className={styles.form}> {Object.keys(formToRender).map((field) => ( <div key={field} className={`${ field === 'postcode' ? styles.form_field_variant : styles.form_field } ${sansLight.className}`} > <div> <label htmlFor={field}> {touched[field] && errors[field] ? ( <p className={`${sansLight.className} errorLabel`}> {errors[field]} </p> ) : ( formFieldSchema[field].label )} </label> <Field validate={field === 'postcode' && validatePostcode} name={field} id={field} className={ touched[field] && errors[field] ? 'errorField' : '' } type={getInputFieldType(formFieldSchema[field].type)} onBlur={() => setFieldTouched(field, true)} onChange={(e) => { if (e.currentTarget.name === 'postcode') { const uppercaseValue = e.currentTarget.value.toUpperCase(); setFieldValue('postcode', uppercaseValue); } setFieldValue( e.currentTarget.name, e.currentTarget.value ); }} /> {formFieldSchema[field].type === 'password' && ( <Image className={styles.icon} src={showPassword ? showIcon : hideIcon} alt="" onClick={handlePasswordVisiblity} width={15} height={15} /> )} </div> {field === 'postcode' && ( <div className={styles.findAddress}> <Button text="Find Address" onClick={() => { validateField('postcode'); handleFindAddress(); }} type="button" disabled={findAddressButtonState} /> </div> )} </div> ))} <div className={styles.form_field}> <button className={`${styles.enterAddressManually} ${sansRegular.className}`} onClick={() => setEnableManualAddress(!enableManualAddress)} type="button" > {enableManualAddress ? 'Hide address details' : 'Enter address manually'} </button> </div> {listOfAddresses?.length >= 1 && ( <div className={`${sansRegular.className} ${styles.addressBox}`} > {listOfAddresses.length >= 1 && ( <> <div className={styles.addressBox_info}> <p style={{ margin: 0 }}> Addresses found <span className="hightlight-pink"> {listOfAddresses.length} </span> </p> <p style={{ margin: 0 }}> Postcode <span className="hightlight-pink"> {values.postcode.toUpperCase()} </span> </p> </div> <ul> {listOfAddresses.map((address, idx) => ( <li key={idx} className={sansRegular.className} onClick={(e) => handleAddressItem(e, setFieldValue)} > {address} </li> ))} </ul> </> )} </div> )} {Object.keys(errors).length > 0 && ( <div className={`${styles.errorMessage} ${sansRegular.className}`} > Error. Please review your details </div> )} <Button text="Sign Up" type="submit" onClick={() => validateForm().then((e) => handleFormSubmission(e, values.email) ) } className={styles.signupButton} /> <div className="signin_prompt"> <p className={sansRegular.className}> Already signed in? <Link href="/" className={`${sansBold.className} hightlight-pink`} > Login </Link> </p> </div> </Form> </div> </Container> )} </Formik> ); }; export default SignupForm;
Editor is loading...