From 8450b9b5a8cc419706b45bc07cf11dd5f2f73e3f Mon Sep 17 00:00:00 2001 From: Ronen Mars Date: Wed, 3 Sep 2025 10:54:34 +0300 Subject: [PATCH 1/6] fix: hubspot submission --- src/hooks/useHubspotSubmission.ts | 47 +++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/hooks/useHubspotSubmission.ts b/src/hooks/useHubspotSubmission.ts index c8e82c0d7f..0b0129a2bf 100644 --- a/src/hooks/useHubspotSubmission.ts +++ b/src/hooks/useHubspotSubmission.ts @@ -6,17 +6,25 @@ import { LoggerService } from "@services/logger.service"; export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { return async (user: { email?: string; name?: string }) => { - if (!isProduction || !hubSpotPortalId || !hubSpotFormId) return; + // Guard: require production + IDs + email + if (!isProduction || !hubSpotPortalId || !hubSpotFormId) { + LoggerService.debug(namespaces.ui.loginPage, "HubSpot submission skipped: non-production or missing IDs"); + return; + } + if (!user?.email) { + LoggerService.debug(namespaces.ui.loginPage, "HubSpot submission skipped: missing user.email"); + return; + } const hsUrl = `https://api.hsforms.com/submissions/v3/integration/submit/${hubSpotPortalId}/${hubSpotFormId}`; const hsContext = { - hutk: Cookies.get("hubspotutk"), - pageUri: window.location.href, - pageName: document.title, + hutk: Cookies.get("hubspotutk") ?? "", + pageUri: String(window.location.href || ""), + pageName: String(document.title || ""), }; const hsData = [ - { objectTypeId: "0-1", name: "email", value: user?.email }, - { objectTypeId: "0-1", name: "firstname", value: user?.name }, + { name: "email", value: user.email }, + { name: "firstname", value: user.name ?? "" }, ]; const submissionData = { submittedAt: Date.now(), @@ -24,13 +32,34 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { context: hsContext, }; try { - await fetch(hsUrl, { + const controller = new AbortController(); + const timeoutId = window.setTimeout(() => controller.abort(), 8000); + + const res = await fetch(hsUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(submissionData), + signal: controller.signal, }); - } catch (error) { - LoggerService.error(namespaces.ui.loginPage, t("errors.loginFailedExtended", { error }), true); + window.clearTimeout(timeoutId); + + if (!res.ok) { + const text = await res.text().catch(() => ""); + LoggerService.error( + namespaces.ui.loginPage, + `HubSpot submission failed: ${res.status} ${res.statusText} ${text ? "- " + text : ""}`, + true + ); + return; + } + + LoggerService.debug(namespaces.ui.loginPage, "HubSpot submission succeeded"); + } catch (error: any) { + LoggerService.error( + namespaces.ui.loginPage, + t("errors.loginFailedExtended", { error: String(error?.message ?? error) }), + true + ); } }; } From cc7d91f069a37aee546e8b2079bb9be17b13d9b4 Mon Sep 17 00:00:00 2001 From: Ronen Mars Date: Thu, 25 Sep 2025 19:16:34 +0300 Subject: [PATCH 2/6] fix(UI-1800): hubspot submission --- src/autokitteh | 2 +- src/hooks/useHubspotSubmission.ts | 118 ++++++++++++++++++++++++++---- 2 files changed, 104 insertions(+), 16 deletions(-) diff --git a/src/autokitteh b/src/autokitteh index e7c5a9cb51..9826cb8e71 160000 --- a/src/autokitteh +++ b/src/autokitteh @@ -1 +1 @@ -Subproject commit e7c5a9cb5194906422690cf46da04200573c2b9a +Subproject commit 9826cb8e711429f43080023e1dcbdaba9cd30ce9 diff --git a/src/hooks/useHubspotSubmission.ts b/src/hooks/useHubspotSubmission.ts index 0b0129a2bf..a0483c6c51 100644 --- a/src/hooks/useHubspotSubmission.ts +++ b/src/hooks/useHubspotSubmission.ts @@ -1,3 +1,4 @@ +import * as Sentry from "@sentry/react"; import Cookies from "js-cookie"; import { isProduction, hubSpotFormId, hubSpotPortalId, namespaces } from "@constants"; @@ -6,13 +7,45 @@ import { LoggerService } from "@services/logger.service"; export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { return async (user: { email?: string; name?: string }) => { - // Guard: require production + IDs + email if (!isProduction || !hubSpotPortalId || !hubSpotFormId) { - LoggerService.debug(namespaces.ui.loginPage, "HubSpot submission skipped: non-production or missing IDs"); + if (isProduction) { + const message = "HubSpot submission skipped: missing IDs"; + LoggerService.error(namespaces.ui.loginPage, message, true); + Sentry.captureMessage(message, { + level: "error", + tags: { component: "hubspot-submission" }, + extra: { + isProduction, + hasHubSpotPortalId: !!hubSpotPortalId, + hasHubSpotFormId: !!hubSpotFormId, + }, + }); + } return; } if (!user?.email) { - LoggerService.debug(namespaces.ui.loginPage, "HubSpot submission skipped: missing user.email"); + const message = "HubSpot submission skipped: missing user.email"; + LoggerService.error(namespaces.ui.loginPage, message, true); + Sentry.captureMessage(message, { + level: "error", + tags: { component: "hubspot-submission" }, + extra: { + hasUser: !!user, + hasUserName: !!user?.name, + }, + }); + return; + } + + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(user.email)) { + const errorMessage = "HubSpot submission skipped: invalid email format"; + LoggerService.error(namespaces.ui.loginPage, errorMessage, true); + Sentry.captureMessage(errorMessage, { + level: "error", + tags: { component: "hubspot-submission" }, + extra: { userEmail: user.email }, + }); return; } @@ -31,9 +64,12 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { fields: hsData, context: hsContext, }; + try { const controller = new AbortController(); - const timeoutId = window.setTimeout(() => controller.abort(), 8000); + const timeoutId = window.setTimeout(() => { + controller.abort(); + }, 8000); const res = await fetch(hsUrl, { method: "POST", @@ -41,25 +77,77 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { body: JSON.stringify(submissionData), signal: controller.signal, }); + window.clearTimeout(timeoutId); if (!res.ok) { const text = await res.text().catch(() => ""); - LoggerService.error( - namespaces.ui.loginPage, - `HubSpot submission failed: ${res.status} ${res.statusText} ${text ? "- " + text : ""}`, - true - ); + const errorMessage = `HubSpot submission failed: ${res.status} ${res.statusText}${text ? " - " + text : ""}`; + + LoggerService.error(namespaces.ui.loginPage, errorMessage, true); + + Sentry.captureException(new Error(errorMessage), { + tags: { + component: "hubspot-submission", + http_status: res.status, + http_status_text: res.statusText, + }, + extra: { + userEmail: user.email, + userName: user.name, + hubSpotUrl: hsUrl, + responseText: text, + submissionData: submissionData, + }, + level: "error", + }); return; } - LoggerService.debug(namespaces.ui.loginPage, "HubSpot submission succeeded"); + const successMessage = "HubSpot submission succeeded"; + LoggerService.debug(namespaces.ui.loginPage, successMessage); + Sentry.captureMessage(successMessage, { + level: "info", + tags: { component: "hubspot-submission" }, + extra: { + userEmail: user.email, + userName: user.name, + responseStatus: res.status, + }, + }); } catch (error: any) { - LoggerService.error( - namespaces.ui.loginPage, - t("errors.loginFailedExtended", { error: String(error?.message ?? error) }), - true - ); + let errorMessage: string; + let errorType = "UnknownError"; + + if (error.name === "AbortError") { + errorMessage = "HubSpot submission timed out after 8 seconds"; + errorType = "TimeoutError"; + } else if (error.name === "TypeError" && error.message.includes("fetch")) { + errorMessage = "HubSpot submission failed: network error"; + errorType = "NetworkError"; + } else { + errorMessage = t("errors.loginFailedExtended", { error: String(error?.message ?? error) }); + errorType = error.name || "UnknownError"; + } + + LoggerService.error(namespaces.ui.loginPage, errorMessage, true); + + Sentry.captureException(error, { + tags: { + component: "hubspot-submission", + error_type: errorType, + }, + extra: { + userEmail: user.email, + userName: user.name, + hubSpotUrl: hsUrl, + submissionData: submissionData, + errorMessage: error.message, + errorStack: error.stack, + timeout: errorType === "TimeoutError", + }, + level: "error", + }); } }; } From 145d2d8d5fa8a966df5d6ac6a981f31d0f28c540 Mon Sep 17 00:00:00 2001 From: Ronen Mars Date: Thu, 25 Sep 2025 19:32:38 +0300 Subject: [PATCH 3/6] fix(UI-1800): hubspot submission --- src/hooks/useHubspotSubmission.ts | 49 ++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/hooks/useHubspotSubmission.ts b/src/hooks/useHubspotSubmission.ts index a0483c6c51..28bb6991b2 100644 --- a/src/hooks/useHubspotSubmission.ts +++ b/src/hooks/useHubspotSubmission.ts @@ -50,14 +50,55 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { } const hsUrl = `https://api.hsforms.com/submissions/v3/integration/submit/${hubSpotPortalId}/${hubSpotFormId}`; + + const hubspotUtk = Cookies.get("hubspotutk"); + const pageUri = window.location.href; + const pageName = document.title; + + if (!hubspotUtk || hubspotUtk.trim() === "" || !pageUri || !pageName) { + const missingValues = []; + if (!hubspotUtk || hubspotUtk.trim() === "") missingValues.push("hubspotutk"); + if (!pageUri) missingValues.push("pageUri"); + if (!pageName) missingValues.push("pageName"); + + const message = `HubSpot submission skipped: missing required values: ${missingValues.join(", ")}`; + LoggerService.error(namespaces.ui.loginPage, message, true); + Sentry.captureMessage(message, { + level: "error", + tags: { component: "hubspot-submission" }, + extra: { + missingValues, + hasHubspotUtk: !!hubspotUtk, + hasPageUri: !!pageUri, + hasPageName: !!pageName, + userEmail: user.email, + userName: user.name, + }, + }); + return; + } + + if (!user.name || user.name.trim() === "") { + const message = "HubSpot submission: user name is empty"; + LoggerService.error(namespaces.ui.loginPage, message, true); + Sentry.captureMessage(message, { + level: "warning", + tags: { component: "hubspot-submission" }, + extra: { + userEmail: user.email, + userName: user.name, + }, + }); + } + const hsContext = { - hutk: Cookies.get("hubspotutk") ?? "", - pageUri: String(window.location.href || ""), - pageName: String(document.title || ""), + hutk: hubspotUtk, + pageUri: pageUri, + pageName: pageName, }; const hsData = [ { name: "email", value: user.email }, - { name: "firstname", value: user.name ?? "" }, + { name: "firstname", value: user.name }, ]; const submissionData = { submittedAt: Date.now(), From ceba0bae41a0fcfb88e55e8b05ff0882c5ea9148 Mon Sep 17 00:00:00 2001 From: Ronen Mars Date: Thu, 25 Sep 2025 19:33:30 +0300 Subject: [PATCH 4/6] fix(UI-1800): hubspot submission --- src/autokitteh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autokitteh b/src/autokitteh index 9826cb8e71..e7c5a9cb51 160000 --- a/src/autokitteh +++ b/src/autokitteh @@ -1 +1 @@ -Subproject commit 9826cb8e711429f43080023e1dcbdaba9cd30ce9 +Subproject commit e7c5a9cb5194906422690cf46da04200573c2b9a From d66a50abb7eeccf5c1ac217647e4b07c840dc229 Mon Sep 17 00:00:00 2001 From: Ronen Mars Date: Sun, 28 Sep 2025 18:42:36 +0300 Subject: [PATCH 5/6] fix(UI-1800): hubspot - optimize the code --- src/hooks/useHubspotSubmission.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/hooks/useHubspotSubmission.ts b/src/hooks/useHubspotSubmission.ts index 28bb6991b2..0c4fa8246f 100644 --- a/src/hooks/useHubspotSubmission.ts +++ b/src/hooks/useHubspotSubmission.ts @@ -7,20 +7,20 @@ import { LoggerService } from "@services/logger.service"; export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { return async (user: { email?: string; name?: string }) => { - if (!isProduction || !hubSpotPortalId || !hubSpotFormId) { - if (isProduction) { - const message = "HubSpot submission skipped: missing IDs"; - LoggerService.error(namespaces.ui.loginPage, message, true); - Sentry.captureMessage(message, { - level: "error", - tags: { component: "hubspot-submission" }, - extra: { - isProduction, - hasHubSpotPortalId: !!hubSpotPortalId, - hasHubSpotFormId: !!hubSpotFormId, - }, - }); - } + if (!isProduction) return; + if (!hubSpotPortalId || !hubSpotFormId) { + const message = "HubSpot submission skipped: missing formId or portalId"; + LoggerService.error(namespaces.ui.loginPage, message, true); + Sentry.captureMessage(message, { + level: "error", + tags: { component: "hubspot-submission" }, + extra: { + isProduction, + hasHubSpotPortalId: !!hubSpotPortalId, + hasHubSpotFormId: !!hubSpotFormId, + }, + }); + return; } if (!user?.email) { From 3b1f88762e33bcf6edd31e2e807f65a530a54a4f Mon Sep 17 00:00:00 2001 From: Ronen Mars Date: Mon, 29 Sep 2025 12:58:39 +0300 Subject: [PATCH 6/6] fix(UI-1800): hubspot - get more info if hubspot submit or track fails --- src/hooks/useHubspot.ts | 153 ++++++++++++++++++++++++++---- src/hooks/useHubspotSubmission.ts | 18 ++-- 2 files changed, 143 insertions(+), 28 deletions(-) diff --git a/src/hooks/useHubspot.ts b/src/hooks/useHubspot.ts index 992914d14c..8c9825e4b8 100644 --- a/src/hooks/useHubspot.ts +++ b/src/hooks/useHubspot.ts @@ -1,45 +1,160 @@ // inspired by: https://github.com/kelvinmaues/react-hubspot-tracking-code-hook +import * as Sentry from "@sentry/react"; + import { PropsUseSetTrackEvent, UseTrackingCode } from "@src/types/hooks"; export const useHubspot = (): UseTrackingCode => { const _hsq = typeof window !== "undefined" && window._hsq ? window._hsq : []; const setContentType = (contentType: string): void => { - _hsq.push(["setContentType", contentType]); + try { + if (!contentType || contentType.trim() === "") { + Sentry.captureMessage("HubSpot setContentType failed: empty contentType", { + level: "warning", + tags: { component: "hubspot-tracking" }, + extra: { contentType, hasHsq: !!_hsq }, + }); + return; + } + _hsq.push(["setContentType", contentType]); + } catch (error) { + Sentry.captureException(error, { + tags: { component: "hubspot-tracking" }, + extra: { contentType, hasHsq: !!_hsq }, + level: "error", + }); + } }; const setTrackPageView = () => { - _hsq.push(["trackPageView"]); + try { + _hsq.push(["trackPageView"]); + } catch (error) { + Sentry.captureException(error, { + tags: { component: "hubspot-tracking" }, + extra: { hasHsq: !!_hsq }, + level: "error", + }); + } }; const setPathPageView = (path: string): void => { - _hsq.push(["setPath", path]); - setTrackPageView(); + try { + if (!path || path.trim() === "") { + Sentry.captureMessage("HubSpot setPathPageView failed: empty path", { + level: "warning", + tags: { component: "hubspot-tracking" }, + extra: { path, hasHsq: !!_hsq }, + }); + return; + } + _hsq.push(["setPath", path]); + setTrackPageView(); + } catch (error) { + Sentry.captureException(error, { + tags: { component: "hubspot-tracking" }, + extra: { path, hasHsq: !!_hsq }, + level: "error", + }); + } }; const setIdentity = (email: string, customPropertities?: object) => { - _hsq.push([ - "identify", - { - email, - ...customPropertities, - }, - ]); + try { + if (!email || email.trim() === "") { + Sentry.captureMessage("HubSpot setIdentity failed: empty email", { + level: "warning", + tags: { component: "hubspot-tracking" }, + extra: { + email, + hasCustomProperties: !!customPropertities, + hasHsq: !!_hsq, + }, + }); + return; + } + + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + Sentry.captureMessage("HubSpot setIdentity failed: invalid email format", { + level: "warning", + tags: { component: "hubspot-tracking" }, + extra: { + email, + hasCustomProperties: !!customPropertities, + hasHsq: !!_hsq, + }, + }); + return; + } + + _hsq.push([ + "identify", + { + email, + ...customPropertities, + }, + ]); + } catch (error) { + Sentry.captureException(error, { + tags: { component: "hubspot-tracking" }, + extra: { + email, + hasCustomProperties: !!customPropertities, + customPropertities, + hasHsq: !!_hsq, + }, + level: "error", + }); + } }; const setTrackEvent = ({ eventId, value }: PropsUseSetTrackEvent) => { - _hsq.push([ - "trackEvent", - { - id: eventId, - value, - }, - ]); + try { + if (!eventId || eventId.trim() === "") { + Sentry.captureMessage("HubSpot setTrackEvent failed: empty eventId", { + level: "warning", + tags: { component: "hubspot-tracking" }, + extra: { + eventId, + value, + hasHsq: !!_hsq, + }, + }); + return; + } + + _hsq.push([ + "trackEvent", + { + id: eventId, + value, + }, + ]); + } catch (error) { + Sentry.captureException(error, { + tags: { component: "hubspot-tracking" }, + extra: { + eventId, + value, + hasHsq: !!_hsq, + }, + level: "error", + }); + } }; const revokeCookieConsent = () => { - _hsq.push(["revokeCookieConsent"]); + try { + _hsq.push(["revokeCookieConsent"]); + } catch (error) { + Sentry.captureException(error, { + tags: { component: "hubspot-tracking" }, + extra: { hasHsq: !!_hsq }, + level: "error", + }); + } }; return { diff --git a/src/hooks/useHubspotSubmission.ts b/src/hooks/useHubspotSubmission.ts index 0c4fa8246f..14207a8c1f 100644 --- a/src/hooks/useHubspotSubmission.ts +++ b/src/hooks/useHubspotSubmission.ts @@ -13,7 +13,7 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { LoggerService.error(namespaces.ui.loginPage, message, true); Sentry.captureMessage(message, { level: "error", - tags: { component: "hubspot-submission" }, + tags: { component: "hubspot-submission-on-login" }, extra: { isProduction, hasHubSpotPortalId: !!hubSpotPortalId, @@ -28,7 +28,7 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { LoggerService.error(namespaces.ui.loginPage, message, true); Sentry.captureMessage(message, { level: "error", - tags: { component: "hubspot-submission" }, + tags: { component: "hubspot-submission-on-login" }, extra: { hasUser: !!user, hasUserName: !!user?.name, @@ -43,7 +43,7 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { LoggerService.error(namespaces.ui.loginPage, errorMessage, true); Sentry.captureMessage(errorMessage, { level: "error", - tags: { component: "hubspot-submission" }, + tags: { component: "hubspot-submission-on-login" }, extra: { userEmail: user.email }, }); return; @@ -64,8 +64,8 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { const message = `HubSpot submission skipped: missing required values: ${missingValues.join(", ")}`; LoggerService.error(namespaces.ui.loginPage, message, true); Sentry.captureMessage(message, { - level: "error", - tags: { component: "hubspot-submission" }, + level: "warning", + tags: { component: "hubspot-submission-on-login" }, extra: { missingValues, hasHubspotUtk: !!hubspotUtk, @@ -83,7 +83,7 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { LoggerService.error(namespaces.ui.loginPage, message, true); Sentry.captureMessage(message, { level: "warning", - tags: { component: "hubspot-submission" }, + tags: { component: "hubspot-submission-on-login" }, extra: { userEmail: user.email, userName: user.name, @@ -129,7 +129,7 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { Sentry.captureException(new Error(errorMessage), { tags: { - component: "hubspot-submission", + component: "hubspot-submission-on-login", http_status: res.status, http_status_text: res.statusText, }, @@ -149,7 +149,7 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { LoggerService.debug(namespaces.ui.loginPage, successMessage); Sentry.captureMessage(successMessage, { level: "info", - tags: { component: "hubspot-submission" }, + tags: { component: "hubspot-submission-on-login" }, extra: { userEmail: user.email, userName: user.name, @@ -175,7 +175,7 @@ export function useHubspotSubmission({ t }: HubspotSubmissionArgs) { Sentry.captureException(error, { tags: { - component: "hubspot-submission", + component: "hubspot-submission-on-login", error_type: errorType, }, extra: {