1// === AJAX CALLS TO HUBSPOT API === // 2 3/** 4 * Fetches contact data from HubSpot API 5 * @param {string} email 6 * @returns {Promise<Object|null>} 7 */ 8export async function getHsptContactData(email) { 9 if (!email) return; 10 11 let response = await fetch(`${ajax_object.ajaxurl}`, { 12 method: 'POST', 13 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 14 body: new URLSearchParams({ 15 action: 'get_contact_by_email_ajax', 16 email: email, 17 nonce: ajax_object?.nonce || '' 18 }) 19 }); 20 21 // properly parse the JSON body 22 const {status, data} = await response.json(); 23 24 // if contact not found, return null 25 if (status === "error") { 26 console.error("Error fetching contact data. Status:", status); 27 return null; 28 } 29 30 // return the contact data 31 return { 32 id: data?.vid || "", 33 email, 34 cda: data["associated-company"]?.properties?.cda?.value || "", 35 owner: data.properties?.hubspot_owner_id?.value || "", 36 ip_country: data.properties?.ip_country?.value || "", 37 headquarters: data["associated-company"]?.properties?.country?.value || "" 38 }; 39} 40 41/** 42 * Sets the sentiment for a contact's message 43 * @param {string} email 44 * @param {string} sentiment 45 * @returns {Promise<void>} 46 */ 47export function setContactMessageSentiment(email, sentiment) { 48 if (!email || !sentiment) return; 49 50 // fire and forget. No need to wait 51 fetch(`${ajax_object.ajaxurl}`, { 52 method: 'POST', 53 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 54 body: new URLSearchParams({ 55 action: 'update_hubspot_contact_sentiment', 56 email: email, 57 sentiment: sentiment, 58 nonce: ajax_object?.nonce || '' 59 }) 60 }).catch(err => console.error('Failed to update sentiment:', err)); 61} 62 63 64/** 65 * Get the company associations for a contact 66 * @param {string} contactId 67 * @returns {Promise<Array>|<Array>|<Error>} Company associations 68 */ 69export const getContactCompanyAssociations = async (contactId) => { 70 if (!contactId) { 71 console.error('Missing contactId'); 72 return []; 73 } 74 if (!isAjaxDefined()) return []; 75 76 const resp = await fetch(`${ajax_object?.ajaxurl}`, { 77 method: 'POST', 78 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 79 body: new URLSearchParams({ 80 action: 'get_contact_company_associations', 81 contact_id: contactId, 82 nonce: ajax_object?.nonce || '' 83 }) 84 }); 85 86 const result = await resp.json(); 87 88 if (!resp.ok || result.status === 'error') { 89 throw new Error(result.message || 'Failed to fetch company associations'); 90 } 91 92 return Array.isArray(result.data) ? result.data : []; 93}; 94 95 96/** 97 * Search for companies based on filters 98 * @param {Object} filters { propertyName: 'domain', operator: 'EQ', value: ${domain} } 99 * @param {number} limit 100 * @param {string} after 101 * @returns {Promise<Object>} Search results 102 */ 103export const searchCompanies = async (filters, limit = 2, after = undefined) => { 104 if (!isAjaxDefined()) return; 105 106 const resp = await fetch(`${ajax_object.ajaxurl}`, { 107 method: 'POST', 108 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 109 body: new URLSearchParams({ 110 action: 'search_companies', 111 filters: JSON.stringify(filters), 112 limit, 113 after, 114 nonce: ajax_object?.nonce || '' 115 }) 116 }); 117 118 const json = await resp.json(); 119 if (!resp.ok || !json?.data) { 120 throw new Error(`Search API ${resp.status}: ${JSON.stringify(json)}`); 121 } 122 123 const { data } = json; 124 const total = typeof data.total === 'number' 125 ? data.total 126 : (Array.isArray(data.results) ? data.results.length : 0); 127 128 return { total, results: data.results || [], paging: data.paging }; 129}; 130 131 132/** 133 * Get company properties by ID 134 * @param {string} companyId 135 * @returns {Promise<Object>} Company properties 136 */ 137export const getCompanyPropsById = async (companyId) => { 138 if (!companyId) { 139 console.error('Missing companyId'); 140 return {}; 141 } 142 if (!isAjaxDefined()) return {}; 143 144 const resp = await fetch(`${ajax_object.ajaxurl}`, { 145 method: 'POST', 146 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 147 body: new URLSearchParams({ 148 action: 'get_company_props_by_id', 149 company_id: companyId, 150 nonce: ajax_object?.nonce || '' 151 }) 152 }); 153 154 const result = await resp.json(); 155 156 if (!resp.ok || result.status === 'error') { 157 throw new Error(result.message || 'Failed to fetch company properties'); 158 } 159 160 return result.data; 161}; 162 163 164/**
165 * Remove company associations for a contact 166 * @param {string} contactId 167 * @param {Array<string>} companyIds 168 * @returns {Promise<Object>} API response 169 */ 170export const removeAssociations = async (contactId, companyIds = []) => { 171 if (!contactId || !companyIds.length) return; 172 if (!isAjaxDefined()) return; 173 174 const resp = await fetch(`${ajax_object.ajaxurl}`, { 175 method: 'POST', 176 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 177 body: new URLSearchParams({ 178 action: 'remove_company_associations', 179 contact_id: contactId, 180 company_ids: JSON.stringify(companyIds), 181 nonce: ajax_object?.nonce || '' 182 }) 183 }); 184 185 const result = await resp.json(); 186 187 if (!resp.ok || result.status === 'error') { 188 throw new Error(result.message || 'Failed to remove associations'); 189 } 190 191 return result.data; // { companyId: 'deleted' | 'error message', ... } 192}; 193 194 195/** 196 * Ensure company association for a contact 197 * @param {string} contactId 198 * @param {string} companyId 199 * @returns {Promise<Object>} API response 200 */ 201export const ensureAssociation = async (contactId, companyId) => { 202 if (!contactId || !companyId) { 203 console.error('Missing contactId or companyId'); 204 return; 205 } 206 if (!isAjaxDefined()) return; 207 208 const resp = await fetch(`${ajax_object.ajaxurl}`, { 209 method: 'POST', 210 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 211 body: new URLSearchParams({ 212 action: 'ensure_company_association', 213 contact_id: contactId, 214 company_id: companyId, 215 nonce: ajax_object?.nonce || '' 216 }) 217 }); 218 219 const result = await resp.json(); 220 221 if (!resp.ok || result.status === 'error') { 222 throw new Error(result.message || 'Failed to ensure association'); 223 } 224 225 return result.data.status; // HTTP status code from HubSpot API 226}; 227 228 229/** 230 * Set primary company for a contact 231 * @param {string} contactId 232 * @param {string} companyId 233 * @returns {Promise<Object>} API response 234 */ 235export const setPrimaryCompany = async (contactId, companyId, cdaId) => { 236 if (!contactId || !companyId || !cdaId) { 237 console.error('Missing contactId or companyId or cdaId'); 238 return; 239 } 240 if (!isAjaxDefined()) return; 241 242 const resp = await fetch(`${ajax_object.ajaxurl}`, { 243 method: 'POST', 244 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 245 body: new URLSearchParams({ 246 action: 'set_primary_company', 247 contact_id: contactId, 248 company_id: companyId, 249 cda_id: cdaId, 250 nonce: ajax_object?.nonce || '' 251 }) 252 }); 253 254 const result = await resp.json(); 255 256 if (!resp.ok || result.status === 'error') { 257 throw new Error(result.message || `Failed to set primary company`); 258 } 259 260 return result.data; // { status: HTTP status code, hubspot_owner_id: 'owner id' } 261}; 262 263/** 264 * Checks if the AJAX object is defined 265 * pre: ajax_object must be globally available (localized by WordPress) 266 * post: logs error if not defined 267 * @returns {boolean} 268 */ 269const isAjaxDefined = () => { 270 if (typeof ajax_object?.ajaxurl === 'undefined') { 271 console.error('ajaxurl not defined. This must be localized by WordPress.'); 272 return false; 273 } 274 return true; 275};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.