import { useState } from "react"; import { compact, uniqueId } from "lodash-es"; import { UseFormMethods } from "react-hook-form"; import { get as getEnv } from '../env'; /** * Represents an Ideal Postcodes result, which has * the individual properties set, or a getAddress.io * result, which just has id and address set. */ class Address { id: string; address: string; post_town!: string; postcode!: string; line_1!: string; line_2!: string; country!: string; county!: string; /** * The ID will be overridden by the getAddress.io ID * if present. This makes it possible to tell if the * address came from ideal-postcodes or getAddress.io. */ constructor() { this.id = uniqueId("idealpostcodes"); this.address = ''; } toString() { if (this.address) { return this.address } return compact([ this.line_1, this.line_2, this.postcode, this.county, this.country ]).join(", "); } } export const useAddressLookup = (form: UseFormMethods) => { const [options, setOptions] = useState(); const [address, setAddressValue] = useState
(); const [loading, setLoading] = useState(false); const [message, setMessage] = useState(null); const [messageType, setMessageType] = useState<'error' | 'info' | null>(null); const setPostcode = async (postcode: string) => { const endpoint = "join/v1/postcode"; const baseUrl = (getEnv('WP_REST_API') as string).replace(/\/$/, ''); // trim trailing slash // Clear previous messages setMessage(null); setMessageType(null); try { const res = await fetch(`${baseUrl}/${endpoint}?postcode=${encodeURIComponent(postcode)}`, { method: "GET", headers: { "content-type": "application/json", accept: "application/json" } }); if (!res.ok) { throw Error(await res.text()); } const response = await res.json(); // Handle bad postcode (validation failed) if (response.status === 'bad_postcode') { setMessage(response.message); setMessageType('error'); setOptions([]); setAddressValue(undefined); return; } // Handle successful lookup if (response.status === 'ok') { setAddressValue(undefined); setOptions(response.data.map((addr: any) => Object.assign(new Address(), addr))); // Handle enrichment message (positive info like local branch) if (response.message) { setMessage(response.message); setMessageType('info'); } return; } throw new Error('Postcode address lookup failed: ' + response.status); } catch (error: any) { console.error(error.message) setOptions([]); } }; const fetchAddress = async (id: string) => { const endpoint = "join/v1/address"; const baseUrl = (getEnv('WP_REST_API') as string).replace(/\/$/, ''); // trim trailing slash try { const res = await fetch(`${baseUrl}/${endpoint}?id=${encodeURIComponent(id)}`, { method: "GET", headers: { "content-type": "application/json", accept: "application/json" } }); if (!res.ok) { throw Error(await res.text()); } const response = await res.json(); if (response.status !== 'ok') { throw new Error('Postcode address lookup failed: ' + response.status) } return response.data } catch (error: any) { console.error(error.message) } return {} } const setAddress = async (id: string) => { const hit = options?.find((x) => x.id === id); if (!hit) { return; } setAddressValue(hit); const setFormValue = (name: string, value: string) => { // Hacky workaround for react-hook-form not being fully reactive const el = document.querySelector(`input[name="${name}`) ?? document.querySelector(`select[name="${name}"`); if (!el) { return; } el.value = value; form.setValue(name, value, { shouldDirty: true, shouldValidate: true }); }; let address: Address | null = null; // Check which provider is configured to determine if we need a second API call: // - Ideal Postcodes returns complete structured data immediately // - getAddress.io returns only id/address in autocomplete and requires a second call const provider = getEnv('POSTCODE_ADDRESS_PROVIDER') as string; const isGetAddressIo = provider === 'get_address_io'; if (isGetAddressIo) { // getAddress.io: need to fetch full address details setLoading(true); address = await fetchAddress(id); } else { // Ideal Postcodes (or any other provider): address data is already complete address = hit; } setTimeout(() => { if (address !== null) { setFormValue("addressLine1", address.line_1); setFormValue("addressLine2", address.line_2); setFormValue("addressCity", address.post_town); setFormValue("addressCounty", address.county); setFormValue("addressPostcode", address.postcode); setFormValue("addressCountry", "GB"); } setLoading(false) }); }; return { setPostcode, setAddress, address, options, loading, message, messageType }; };