import { ReactiveController, ReactiveControllerHost } from "lit"; import { Address, parseAttomAutocompleteResults, parseAutocompleteResults, } from "./autocomplete.parser"; import type { AttomResponse } from "./autocomplete.interface"; export interface AutocompleteOptions { url?: string; states?: string; limit?: string; apikey?: string; v2?: boolean; country?: string; apiURL?: string; environment?: "production" | "staging"; preferredStates?: string; } export interface ValidateKeyResponse { domain: string; params: { key: string }; states_allowed: string[]; status: string; url: string; } export default class AutocompleteController implements ReactiveController { private host: ReactiveControllerHost; isLoading: boolean; addresses: Address[]; tenant?: ValidateKeyResponse; constructor(host: ReactiveControllerHost) { host.addController(this); this.host = host; this.isLoading = false; this.addresses = []; } hostConnected() { // Host connected } hostDisconnected() { // Host disconnected } adressesList() {} async validateTenant(apiKey: string, environment: "production" | "staging") { const requestURL = environment === "staging" ? `https://stage.buyermls.com/widget/verify-json?key=${apiKey}` : `https://api.buyermls.com/widget/verify-json?key=${apiKey}`; const response = await fetch(requestURL); const json = await response.json(); this.tenant = json; this.host.requestUpdate(); return json as ValidateKeyResponse; } async decryptAddress(signedAddress: string, options: { apiKey: string }) { if (!signedAddress) return; const response = await fetch(`https://api.percyx.com/v1/address/encrypter/verify/${signedAddress}`, { headers: { "x-api-key": options.apiKey, }, }); const json = await response.json() as { data: { validator: "attom", address: string, city: string, state: string, zipcode: string, lat: string, lng: string, source: string, address1: string, address2: string, streetAddress: string, iat: number } }; return json; } async fetchAddressAutocomplete( inputValue: string, options: AutocompleteOptions ) { if (inputValue.length <= 0) { this.addresses = []; this.host.requestUpdate(); return; } let url = options.apiURL ? `${options.apiURL}?query=${encodeURIComponent(inputValue)}` : `https://api.percyx.com/v1/address/autocomplete-all-v2?query=${encodeURIComponent(inputValue)}`; if ( options.states !== undefined && typeof options.states === "string" && options.states.length >= 2 ) { url += `&state_filters=${options.states || this.tenant?.states_allowed}`; } if ( options.preferredStates !== undefined && typeof options.preferredStates === "string" && options.preferredStates.length >= 2 ) { url += `&preferred_states=${options.preferredStates}`; } if (options.v2) { url += `&v2=true`; } url += `&country=${options?.country || "us"}`; url += `&limit=${options.limit || 30}`; try { this.host.requestUpdate(); this.isLoading = true; const response = await fetch(url, { headers: { "x-api-key": options.apikey ?? "", }, }); const json = await response.json(); const melissaAddresses = { Version: json.Version, ResultCode: json.ResultCode, ErrorString: json.ErrorString, Results: json.Results?.melissa ?? [], }; const addresses = parseAutocompleteResults(melissaAddresses); const attomHits = json.Results?.attom as AttomResponse; const parsedAttomHits = attomHits?.map((hit) => { return { id: hit._source.id, address: hit._source.address, city: hit._source.city, state: hit._source.state, zipcode: hit._source.zipcode, geo_point: hit._source.geo_point, signedAddress: hit.signedAddress, }; }); const attomAddresses = parseAttomAutocompleteResults( parsedAttomHits, inputValue ); this.addresses = [...addresses, ...attomAddresses]; } catch (error) { this.isLoading = false; console.error(error); } finally { this.host.requestUpdate(); this.isLoading = false; } } }