From 1882b7048d0fa71e5cd6c175d66f25795e032cf4 Mon Sep 17 00:00:00 2001 From: Simon Backx Date: Thu, 5 Oct 2023 11:37:02 +0200 Subject: [PATCH] Added click tracking for recommendations helper (#18496) fixes https://github.com/TryGhost/Product/issues/4001 --- apps/portal/src/App.js | 30 +++++++++++++++++++ apps/portal/src/actions.js | 12 ++++++++ .../frontend/helpers/tpl/recommendations.hbs | 2 +- .../frontend/helpers/recommendations.test.js | 20 ++++++------- 4 files changed, 53 insertions(+), 11 deletions(-) diff --git a/apps/portal/src/App.js b/apps/portal/src/App.js index 2ecc2ae675..669b94eee9 100644 --- a/apps/portal/src/App.js +++ b/apps/portal/src/App.js @@ -195,6 +195,7 @@ export default class App extends React.Component { }); } } + this.setupRecommendationButtons(); } catch (e) { /* eslint-disable no-console */ console.error(`[Portal] Failed to initialize:`, e); @@ -888,6 +889,35 @@ export default class App extends React.Component { }; } + getRecommendationButtons() { + const customTriggerSelector = '[data-recommendation]'; + return document.querySelectorAll(customTriggerSelector) || []; + } + + /** Setup click tracking for recommendation buttons */ + setupRecommendationButtons() { + // Handler for custom buttons + const clickHandler = (event) => { + // Send beacons for recommendation clicks + const recommendationId = event.currentTarget.dataset.recommendation; + + if (recommendationId) { + this.dispatchAction('trackRecommendationClicked', { + recommendationId + // eslint-disable-next-line no-console + }).catch(console.error); + } else { + // eslint-disable-next-line no-console + console.warn('[Portal] Invalid usage of data-recommendation attribute'); + } + }; + + const elements = this.getRecommendationButtons(); + for (const element of elements) { + element.addEventListener('click', clickHandler, {passive: true}); + } + } + render() { if (this.state.initStatus === 'success') { return ( diff --git a/apps/portal/src/actions.js b/apps/portal/src/actions.js index df96cd95e8..68663094c1 100644 --- a/apps/portal/src/actions.js +++ b/apps/portal/src/actions.js @@ -502,6 +502,18 @@ async function oneClickSubscribe({data: {siteUrl}, state}) { } function trackRecommendationClicked({data: {recommendationId}, api}) { + try { + const existing = localStorage.getItem('ghost-recommendations-clicked'); + const clicked = existing ? JSON.parse(existing) : []; + if (clicked.includes(recommendationId)) { + // Already tracked + return; + } + clicked.push(recommendationId); + localStorage.setItem('ghost-recommendations-clicked', JSON.stringify(clicked)); + } catch (e) { + // Ignore localstorage errors (browser not supported or in private mode) + } api.recommendations.trackClicked({ recommendationId }); diff --git a/ghost/core/core/frontend/helpers/tpl/recommendations.hbs b/ghost/core/core/frontend/helpers/tpl/recommendations.hbs index b748cdf9d9..4f262c38d6 100644 --- a/ghost/core/core/frontend/helpers/tpl/recommendations.hbs +++ b/ghost/core/core/frontend/helpers/tpl/recommendations.hbs @@ -2,7 +2,7 @@