import React, { useState, useEffect } from "react"; import { Col, Form, Row, Button, Collapse, Alert } from "react-bootstrap"; import { useForm } from "react-hook-form"; import { get as getEnv, getStr as getEnvStr } from "../env"; import { StagerComponent } from "../components/stager"; import { DetailsSchema, FormSchema, parseTriggerValues, validate } from "../schema"; import { useAddressLookup } from "../services/address-lookup.service"; import { ContinueButton, FormItem } from "../components/atoms"; import { phoneCountries, sortedCountries } from "../constants"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; import { redirectToSuccess } from "../services/router.service"; import { usePostResource } from "../services/rest-resource.service"; const addressLookupFormSchema = yup.object().shape({ postcode: yup.string().required("We need a postcode to search your postcode") }); const requireAddress = Boolean(getEnv("REQUIRE_ADDRESS")); const hideAddress = Boolean(getEnv("HIDE_ADDRESS")); const homeAddressCopy = getEnvStr("HOME_ADDRESS_COPY"); const passwordPurpose = getEnvStr("PASSWORD_PURPOSE"); const privacyCopy = getEnvStr("PRIVACY_COPY"); const aboutYouHeading = getEnvStr("ABOUT_YOU_HEADING"); const aboutYouCopy = getEnvStr("ABOUT_YOU_COPY"); const contactDetailsHeading = getEnvStr("CONTACT_DETAILS_HEADING"); const contactConsentCopy = getEnvStr("CONTACT_CONSENT_COPY"); const contactDetailsCopy = getEnvStr("CONTACT_DETAILS_COPY"); const dateOfBirthHeading = getEnvStr("DATE_OF_BIRTH_HEADING"); const dateOfBirthCopy = getEnvStr("DATE_OF_BIRTH_COPY"); const customFields = (getEnv("CUSTOM_FIELDS") || []) as any[]; const customFieldsHeading = getEnvStr("CUSTOM_FIELDS_HEADING"); const useHearAboutUs = getEnv("COLLECT_HEAR_ABOUT_US") || false; const hearAboutUsDetails = getEnvStr("HEAR_ABOUT_US_DETAILS"); const hearAboutUsHeading = getEnvStr("HEAR_ABOUT_US_HEADING"); const hearAboutUsOptions = ( (getEnv("HEAR_ABOUT_US_OPTIONS") || []) as any[] ).filter((o) => o.toLowerCase() !== "other"); export const DetailsPage: StagerComponent = ({ data, onCompleted }) => { const form = useForm({ defaultValues: data as {}, resolver: validate(DetailsSchema) }); const [manuallyOpen, setAddressManuallyOpen] = useState(false); const [skippingPayment, setSkippingPayment] = useState(false); const usePostcodeLookup = getEnv("USE_POSTCODE_LOOKUP"); const howDidYouHearAboutUs = form.watch("howDidYouHearAboutUs"); const addressLookupForm = useForm({ resolver: yupResolver(addressLookupFormSchema) }); const addressLookup = useAddressLookup(form); const handleLookupPostcode = addressLookupForm.handleSubmit(({ postcode }) => addressLookup.setPostcode(postcode) ); useEffect(() => { // Check if all we haven't touched the postcode at all or manually entered something, then give an error. if (form.errors && Object.keys(form.errors).length > 0) { setAddressManuallyOpen(true); } if ( Object.keys(form.errors).filter((error) => error.includes("address")) .length === 4 ) { addressLookupForm.setError("postcode", { type: "manual", message: "Please fill in your address. Enter your postcode above then click on Find Address to find it, then use Select Address" }); } else { addressLookupForm.clearErrors("postcode"); } }, [form.errors]); const recordStep = usePostResource>("/step"); const skipPayment = async () => { setSkippingPayment(true); await recordStep({ ...data, stage: "enter-details" }); await redirectToSuccess(data); }; return (

{aboutYouHeading}

{getEnv("COLLECT_DATE_OF_BIRTH") ? (

{dateOfBirthHeading}

) : null} {!hideAddress ? (

Home address

{getEnv("HIDE_HOME_ADDRESS_COPY") ? (
) : null} {usePostcodeLookup ? ( <> Find address } > {addressLookup.message && (
)}

setAddressManuallyOpen((manuallyOpen) => !manuallyOpen) } > If you can't find your address, you can enter it manually.

0}> Select Address addressLookup.setAddress(e.currentTarget.value) } > {addressLookup.options?.map((opt) => ( ))} ) : null}
{getEnv("COLLECT_COUNTY") ? ( ) : null} {sortedCountries.map((c) => ( ))}
) : null} {customFields.length ? (
{customFieldsHeading ?

{customFieldsHeading}

: null} {customFields.map((field) => ( ))}
) : null}

{contactDetailsHeading}

{Boolean(getEnv("ENABLE_INTERNATIONAL_PHONE_NUMBERS")) ? ( {phoneCountries.map((c) => ( ))} ) : ( )} {getEnv("COLLECT_PHONE_AND_EMAIL_CONTACT_CONSENT") ? ( <>
) : null}
{getEnv("CREATE_AUTH0_ACCOUNT") ? (

Password

{passwordPurpose ? (
) : ( "" )}

Your password should contain at least one number, one uppercase letter and one special character. It must be at least 8 characters long.

) : null} {useHearAboutUs ? (

{hearAboutUsHeading}

{hearAboutUsOptions.map((value) => ( ))} {howDidYouHearAboutUs === "other" && ( )}
) : null}
{getEnv("INCLUDE_SKIP_PAYMENT_BUTTON") ? ( ) : null} ); }; const CustomField: React.FC<{ field: any; form: any }> = ({ field, form }) => { const triggerField = field.conditional_trigger_field; const triggerValues = parseTriggerValues(field.conditional_trigger_values); const triggerValue = form.watch(triggerField || ""); const visible = !triggerField || triggerValues.length === 0 || triggerValues.includes(triggerValue); useEffect(() => { if (!visible) { const current = form.getValues(field.id); if (current !== undefined && current !== "" && current !== false) { form.setValue(field.id, field.field_type === "checkbox" ? false : ""); } } }, [visible]); if (!visible) { return null; } return ( <> {field.field_type === "checkbox" ? ( ) : field.field_type === "select" ? ( {parseCustomFieldOptions(field.options).map( (o: { label: string; value: string }) => ( ) )} ) : field.field_type === "radio" ? ( {parseCustomFieldOptions(field.options).map( (o: { label: string; value: string }) => ( ) )} ) : field.field_type === "month_year" ? ( ) : ( )} {field.instructions && (
)} ); }; const MonthYearField: React.FC<{ field: any; form: any }> = ({ field, form }) => { const monthName = `${field.id}Month`; const yearName = `${field.id}Year`; const month = form.watch(monthName); const year = form.watch(yearName); useEffect(() => { const mm = month ? String(month).padStart(2, "0") : ""; const yyyy = year ? String(year) : ""; const next = mm && /^\d{4}$/.test(yyyy) ? `${mm}/${yyyy}` : ""; form.setValue(field.id, next, { shouldValidate: form.formState.isSubmitted }); }, [month, year]); const error = form.errors?.[field.id]; const isInvalid = !!error; return (
{field.label} {field.required ? ( <> {" "} {" "}
required
) : null}
Month {Array.from({ length: 12 }).map((_, i) => { const v = String(i + 1).padStart(2, "0"); return ( ); })} Year {isInvalid && (
{error?.message}
)}
); }; const parseCustomFieldOptions = (options: string) => { return options .split("\n") .map((row) => row.trim()) .filter(Boolean) .map((row) => row .split(":") .map((c) => c.trim()) .filter(Boolean) ) .map((row) => { if (row.length === 0) { return null; } if (row.length === 1) { return { value: row[0], label: row[0] }; } return { value: row[0], label: row.slice(1).join(":") }; }) .filter((r) => r !== null); };