diff --git a/packages/spacecat-shared-rum-api-client/src/functions/form-vitals.js b/packages/spacecat-shared-rum-api-client/src/functions/form-vitals.js index d57b0d620..99d0ce3b2 100644 --- a/packages/spacecat-shared-rum-api-client/src/functions/form-vitals.js +++ b/packages/spacecat-shared-rum-api-client/src/functions/form-vitals.js @@ -10,10 +10,12 @@ * governing permissions and limitations under the License. */ -import { DataChunks, facets } from '@adobe/rum-distiller'; +import { DataChunks, facetFns, facets } from '@adobe/rum-distiller'; import trafficAcquisition from './traffic-acquisition.js'; import { DELIMITER, generateKey, loadBundles } from '../utils.js'; +const { checkpointSource } = facetFns; +const { checkpoint: checkpointFacet, url: urlFacet } = facets; const METRICS = ['formview', 'formengagement', 'formsubmit']; const CHECKPOINTS = ['viewblock', 'click', 'fill', 'formsubmit', 'navigate', 'viewmedia', 'experiment']; const KEYWORDS_TO_FILTER = ['search']; @@ -102,8 +104,8 @@ function populateFormsInternalNavigation(bundles, formVitals) { const formVital = findByUrl(formVitals, bundle.url); if (formInternalNav && formVital - && !formVital.forminternalnavigation - .some((e) => e.url === formInternalNav.source)) { + && !formVital.forminternalnavigation + .some((e) => e.url === formInternalNav.source)) { const fv = findByUrl(formVitals, formInternalNav.source); formVital.forminternalnavigation.push({ url: formInternalNav.source, @@ -147,6 +149,39 @@ function findFormCTAForInternalNavigation(bundles, formVitals) { }); } +function findFormCTAWithinPage(bundles, formVitals) { + return formVitals.map((item) => { + const { url, formsource } = item; + const dataChunks = new DataChunks(); + dataChunks.load([{ rumBundles: bundles }]); + dataChunks.addFacet('viewblock.source', checkpointSource('viewblock')); + dataChunks.addFacet('checkpoint', checkpointFacet); + dataChunks.addFacet('url', urlFacet); + dataChunks.filter = { checkpoint: ['click', 'viewblock'], url: [url], 'viewblock.source': [formsource] }; + const sortedBundles = dataChunks.filtered.sort((a, b) => a.timeDelta - b.timeDelta); + const sources = sortedBundles.map((a) => { + const { events } = a; + const viewblock = events.find(({ checkpoint, source }) => checkpoint === 'viewblock' && source === formsource); + const viewblockTime = viewblock.timeDelta; + + const clicks = events.filter((e) => e.checkpoint === 'click') + // clicks within 1 second of viewblock + .filter((e) => Math.abs(e.timeDelta - viewblockTime) < 1000) + // ignore form clicks + .filter((e) => e.source && !e.source.match(/\bform\b/i)); + + return clicks.map((_) => _.source); + }).filter((_) => _.length > 0).flat(); + if (sources.length > 0) { + return { + ...item, + formCTAWithinPage: [...new Set(sources)], + }; + } + return item; + }); +} + function containsFormVitals(row) { return METRICS.some((metric) => Object.keys(row[metric]).length > 0); } @@ -196,7 +231,6 @@ function getParentPageVitalsGroupedByIFrame(bundles, dataChunks, iframeParentMap populateFormsInternalNavigation(bundles, parentWebVitals); findFormCTAForInternalNavigation(bundles, Object.values(parentWebVitals)); - const iframeParentVitalsMap = {}; for (const vitals of Object.values(parentWebVitals)) { iframeParentVitalsMap[vitals.iframeSrc] = vitals; @@ -300,8 +334,9 @@ function handler(bundles) { (formVital) => containsFormVitals(formVital) && !isUnderExperiment(formVital, experimentUrls), ); findFormCTAForInternalNavigation(bundles, filteredFormVitals); + const formVitalsWithCTA = findFormCTAWithinPage(bundles, filteredFormVitals); - const updatedFormVitals = filteredFormVitals.map((formVital) => { + const updatedFormVitals = formVitalsWithCTA.map((formVital) => { const formVitalCopy = { ...formVital }; const parentFormVital = iframeParentVitalsMap[formVital.url]; if (parentFormVital) { @@ -315,7 +350,6 @@ function handler(bundles) { url, pageview: { ...pageview }, forminternalnavigation, - iframeSrc, }); }