@aglyn/plugins-marketing 1.0.0-beta.221 → 1.0.0-beta.223

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +4 -1
  2. package/package.json +15 -15
  3. package/src/lib/components/campaign-conversions-card.js +53 -64
  4. package/src/lib/components/campaign-conversions-card.js.map +1 -1
  5. package/src/lib/components/campaign-detail-card.js +18 -3
  6. package/src/lib/components/campaign-detail-card.js.map +1 -1
  7. package/src/lib/components/campaign-edit-drawer.d.ts +6 -0
  8. package/src/lib/components/campaign-edit-drawer.js +13 -2
  9. package/src/lib/components/campaign-edit-drawer.js.map +1 -1
  10. package/src/lib/components/campaign-queries.d.ts +5 -0
  11. package/src/lib/components/campaign-queries.js +8 -1
  12. package/src/lib/components/campaign-queries.js.map +1 -1
  13. package/src/lib/components/campaign-reach-sections.d.ts +28 -0
  14. package/src/lib/components/campaign-reach-sections.js +96 -5
  15. package/src/lib/components/campaign-reach-sections.js.map +1 -1
  16. package/src/lib/components/campaign-report-card.js +5 -4
  17. package/src/lib/components/campaign-report-card.js.map +1 -1
  18. package/src/lib/components/campaigns-card.js +1 -1
  19. package/src/lib/components/campaigns-card.js.map +1 -1
  20. package/src/lib/components/conversion-attribution.component.d.ts +18 -0
  21. package/src/lib/components/conversion-attribution.component.js +177 -39
  22. package/src/lib/components/conversion-attribution.component.js.map +1 -1
  23. package/src/lib/components/email-compose-card.js +1 -1
  24. package/src/lib/components/email-compose-card.js.map +1 -1
  25. package/src/lib/components/email-detail.js +5 -4
  26. package/src/lib/components/email-detail.js.map +1 -1
  27. package/src/lib/components/email-template-report-card.d.ts +53 -0
  28. package/src/lib/components/email-template-report-card.js +469 -0
  29. package/src/lib/components/email-template-report-card.js.map +1 -0
  30. package/src/lib/components/emails-list-card.js +2 -2
  31. package/src/lib/components/emails-list-card.js.map +1 -1
  32. package/src/lib/components/host-experiments-card.component.js +3 -2
  33. package/src/lib/components/host-experiments-card.component.js.map +1 -1
  34. package/src/lib/components/record-attribution-widget.d.ts +5 -0
  35. package/src/lib/components/record-attribution-widget.js +6 -3
  36. package/src/lib/components/record-attribution-widget.js.map +1 -1
  37. package/src/lib/components/site-runtime.d.ts +5 -0
  38. package/src/lib/components/site-runtime.js +104 -3
  39. package/src/lib/components/site-runtime.js.map +1 -1
  40. package/src/lib/declarations.server.d.ts +9 -0
  41. package/src/lib/declarations.server.js +78 -0
  42. package/src/lib/declarations.server.js.map +1 -1
  43. package/src/lib/model/campaign-container.d.ts +531 -0
  44. package/src/lib/model/campaign-container.js +532 -0
  45. package/src/lib/model/campaign-container.js.map +1 -0
  46. package/src/lib/model/campaign-conversions.d.ts +55 -4
  47. package/src/lib/model/campaign-conversions.js +60 -4
  48. package/src/lib/model/campaign-conversions.js.map +1 -1
  49. package/src/lib/model/campaign-report.d.ts +158 -0
  50. package/src/lib/model/campaign-report.js +268 -0
  51. package/src/lib/model/campaign-report.js.map +1 -0
  52. package/src/lib/model/campaign-revenue.d.ts +3 -3
  53. package/src/lib/model/campaign-revenue.js +3 -3
  54. package/src/lib/model/campaign-revenue.js.map +1 -1
  55. package/src/lib/model/campaign-send-time.js +2 -2
  56. package/src/lib/model/campaign-send-time.js.map +1 -1
  57. package/src/lib/model/campaign-visits.d.ts +74 -0
  58. package/src/lib/model/campaign-visits.js +65 -0
  59. package/src/lib/model/campaign-visits.js.map +1 -0
  60. package/src/lib/model/compile-client-automations.d.ts +2 -2
  61. package/src/lib/model/compile-client-automations.js +3 -3
  62. package/src/lib/model/compile-client-automations.js.map +1 -1
  63. package/src/lib/model/email-record.d.ts +175 -0
  64. package/src/lib/model/email-record.js +206 -0
  65. package/src/lib/model/email-record.js.map +1 -0
  66. package/src/lib/model/site-contract.d.ts +2 -2
  67. package/src/lib/model/site-contract.js.map +1 -1
  68. package/src/lib/model/template-report.d.ts +158 -0
  69. package/src/lib/model/template-report.js +249 -0
  70. package/src/lib/model/template-report.js.map +1 -0
  71. package/src/lib/plugin.js +16 -0
  72. package/src/lib/plugin.js.map +1 -1
  73. package/src/lib/preview-props.js.map +1 -1
  74. package/src/lib/server/campaign-attribution-store.d.ts +55 -0
  75. package/src/lib/server/campaign-attribution-store.js +98 -0
  76. package/src/lib/server/campaign-attribution-store.js.map +1 -0
  77. package/src/lib/server/campaign-conversion-attribution.d.ts +314 -0
  78. package/src/lib/server/campaign-conversion-attribution.js +549 -0
  79. package/src/lib/server/campaign-conversion-attribution.js.map +1 -0
  80. package/src/lib/server/campaign-manage.js +1 -1
  81. package/src/lib/server/campaign-manage.js.map +1 -1
  82. package/src/lib/server/campaign-org-refs.js +1 -1
  83. package/src/lib/server/campaign-org-refs.js.map +1 -1
  84. package/src/lib/server/campaign-recipients.js +7 -7
  85. package/src/lib/server/campaign-recipients.js.map +1 -1
  86. package/src/lib/server/campaign-send.js +25 -19
  87. package/src/lib/server/campaign-send.js.map +1 -1
  88. package/src/lib/server/campaign-touch-targets.d.ts +61 -0
  89. package/src/lib/server/campaign-touch-targets.js +162 -0
  90. package/src/lib/server/campaign-touch-targets.js.map +1 -0
  91. package/src/lib/server/campaign-visit-beacon.d.ts +44 -0
  92. package/src/lib/server/campaign-visit-beacon.js +125 -0
  93. package/src/lib/server/campaign-visit-beacon.js.map +1 -0
  94. package/src/lib/server/conversion-creditor.d.ts +18 -0
  95. package/src/lib/server/conversion-creditor.js +238 -0
  96. package/src/lib/server/conversion-creditor.js.map +1 -0
  97. package/src/lib/server/email-campaign-touch.d.ts +81 -0
  98. package/src/lib/server/email-campaign-touch.js +227 -0
  99. package/src/lib/server/email-campaign-touch.js.map +1 -0
  100. package/src/lib/server/email-events.js +32 -31
  101. package/src/lib/server/email-events.js.map +1 -1
  102. package/src/lib/server/email-revenue-attribution.d.ts +186 -0
  103. package/src/lib/server/email-revenue-attribution.js +322 -0
  104. package/src/lib/server/email-revenue-attribution.js.map +1 -0
  105. package/src/lib/server/email-send-record-index.d.ts +34 -0
  106. package/src/lib/server/email-send-record-index.js +90 -0
  107. package/src/lib/server/email-send-record-index.js.map +1 -0
  108. package/src/lib/server/get-client-automations.js.map +1 -1
  109. package/src/lib/server/marketing-figures.js +3 -3
  110. package/src/lib/server/marketing-figures.js.map +1 -1
  111. package/src/lib/server/send-tally.d.ts +32 -0
  112. package/src/lib/server/send-tally.js +54 -0
  113. package/src/lib/server/send-tally.js.map +1 -0
  114. package/src/lib/server/site-page-enricher.js +18 -2
  115. package/src/lib/server/site-page-enricher.js.map +1 -1
  116. package/src/lib/testing/stand-in-person-records.d.ts +35 -0
  117. package/src/lib/testing/stand-in-person-records.js +67 -0
  118. package/src/lib/testing/stand-in-person-records.js.map +1 -0
  119. package/src/lib/testing/stand-in-record-counts.d.ts +26 -0
  120. package/src/lib/testing/stand-in-record-counts.js +70 -0
  121. package/src/lib/testing/stand-in-record-counts.js.map +1 -0
  122. package/src/lib/transfer/campaigns-package.d.ts +72 -0
  123. package/src/lib/transfer/campaigns-package.js +145 -0
  124. package/src/lib/transfer/campaigns-package.js.map +1 -0
  125. package/src/lib/transfer/campaigns-package.server.d.ts +33 -0
  126. package/src/lib/transfer/campaigns-package.server.js +305 -0
  127. package/src/lib/transfer/campaigns-package.server.js.map +1 -0
@@ -27,6 +27,11 @@ export interface RecordAttributionWidgetProps {
27
27
  hostId?: string | null;
28
28
  recordKind?: string;
29
29
  recordId?: string;
30
+ /**
31
+ * The campaigns the host's record is filed under, when the host knows them
32
+ * (AGL-3461) — the Inbox hands a submission's form and page campaigns.
33
+ */
34
+ filedUnder?: readonly string[];
30
35
  }
31
36
  export declare function RecordAttributionWidget(props: RecordAttributionWidgetProps): import("react").JSX.Element;
32
37
  export declare namespace RecordAttributionWidget {
@@ -14,18 +14,21 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */ 'use client';
17
+ import { _ as _extends } from "@swc/helpers/_/_extends";
17
18
  import { jsx as _jsx } from "react/jsx-runtime";
18
19
  import { CAMPAIGN_CONVERSION_KINDS } from "../model/campaign-conversions.js";
19
20
  import ConversionAttribution from "./conversion-attribution.component.js";
20
21
  const isConversionKind = (kind)=>CAMPAIGN_CONVERSION_KINDS.includes(kind);
21
22
  export function RecordAttributionWidget(props) {
22
- const { hostId, recordKind, recordId } = props;
23
+ const { hostId, recordKind, recordId, filedUnder } = props;
23
24
  if (!hostId || !recordId || !isConversionKind(recordKind)) return null;
24
- return /*#__PURE__*/ _jsx(ConversionAttribution, {
25
+ return /*#__PURE__*/ _jsx(ConversionAttribution, _extends({
25
26
  hostId: hostId,
26
27
  kind: recordKind,
27
28
  refId: recordId
28
- });
29
+ }, Array.isArray(filedUnder) && filedUnder.length ? {
30
+ filedUnder
31
+ } : {}));
29
32
  }
30
33
  RecordAttributionWidget.displayName = 'RecordAttributionWidget';
31
34
  export default RecordAttributionWidget;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/record-attribution-widget.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport {\n CAMPAIGN_CONVERSION_KINDS,\n type CampaignConversionKind,\n} from '../model/campaign-conversions'\nimport ConversionAttribution from './conversion-attribution.component'\n\n/**\n * The widget this plugin draws in another plugin's record-attribution zone.\n *\n * The CRM and the Inbox each host a zone on a record and hand it\n * `{ hostId, recordKind, recordId }` in their own words. Those words are the\n * identify moments this plugin credits a conversion at, so the widget reads\n * the kind as one — and draws nothing for a kind it never credits, rather\n * than reading a document that cannot exist.\n */\nexport interface RecordAttributionWidgetProps {\n hostId?: string | null\n recordKind?: string\n recordId?: string\n}\n\nconst isConversionKind = (kind: unknown): kind is CampaignConversionKind =>\n (CAMPAIGN_CONVERSION_KINDS as readonly unknown[]).includes(kind)\n\nexport function RecordAttributionWidget(props: RecordAttributionWidgetProps) {\n const { hostId, recordKind, recordId } = props\n if (!hostId || !recordId || !isConversionKind(recordKind)) return null\n return <ConversionAttribution hostId={hostId} kind={recordKind} refId={recordId} />\n}\nRecordAttributionWidget.displayName = 'RecordAttributionWidget'\n\nexport default RecordAttributionWidget\n"],"names":["CAMPAIGN_CONVERSION_KINDS","ConversionAttribution","isConversionKind","kind","includes","RecordAttributionWidget","props","hostId","recordKind","recordId","refId","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;AAEA,SACEA,yBAAyB,QAEpB,mCAA+B;AACtC,OAAOC,2BAA2B,wCAAoC;AAiBtE,MAAMC,mBAAmB,CAACC,OACxB,AAACH,0BAAiDI,QAAQ,CAACD;AAE7D,OAAO,SAASE,wBAAwBC,KAAmC;IACzE,MAAM,EAAEC,MAAM,EAAEC,UAAU,EAAEC,QAAQ,EAAE,GAAGH;IACzC,IAAI,CAACC,UAAU,CAACE,YAAY,CAACP,iBAAiBM,aAAa,OAAO;IAClE,qBAAO,KAACP;QAAsBM,QAAQA;QAAQJ,MAAMK;QAAYE,OAAOD;;AACzE;AACAJ,wBAAwBM,WAAW,GAAG;AAEtC,eAAeN,wBAAuB"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/record-attribution-widget.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport {\n CAMPAIGN_CONVERSION_KINDS,\n type CampaignConversionKind,\n} from '../model/campaign-conversions'\nimport ConversionAttribution from './conversion-attribution.component'\n\n/**\n * The widget this plugin draws in another plugin's record-attribution zone.\n *\n * The CRM and the Inbox each host a zone on a record and hand it\n * `{ hostId, recordKind, recordId }` in their own words. Those words are the\n * identify moments this plugin credits a conversion at, so the widget reads\n * the kind as one — and draws nothing for a kind it never credits, rather\n * than reading a document that cannot exist.\n */\nexport interface RecordAttributionWidgetProps {\n hostId?: string | null\n recordKind?: string\n recordId?: string\n /**\n * The campaigns the host's record is filed under, when the host knows them\n * (AGL-3461) — the Inbox hands a submission's form and page campaigns.\n */\n filedUnder?: readonly string[]\n}\n\nconst isConversionKind = (kind: unknown): kind is CampaignConversionKind =>\n (CAMPAIGN_CONVERSION_KINDS as readonly unknown[]).includes(kind)\n\nexport function RecordAttributionWidget(props: RecordAttributionWidgetProps) {\n const { hostId, recordKind, recordId, filedUnder } = props\n if (!hostId || !recordId || !isConversionKind(recordKind)) return null\n return (\n <ConversionAttribution\n hostId={hostId}\n kind={recordKind}\n refId={recordId}\n {...(Array.isArray(filedUnder) && filedUnder.length ? { filedUnder } : {})}\n />\n )\n}\nRecordAttributionWidget.displayName = 'RecordAttributionWidget'\n\nexport default RecordAttributionWidget\n"],"names":["CAMPAIGN_CONVERSION_KINDS","ConversionAttribution","isConversionKind","kind","includes","RecordAttributionWidget","props","hostId","recordKind","recordId","filedUnder","refId","Array","isArray","length","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;AAEA,SACEA,yBAAyB,QAEpB,mCAA+B;AACtC,OAAOC,2BAA2B,wCAAoC;AAsBtE,MAAMC,mBAAmB,CAACC,OACxB,AAACH,0BAAiDI,QAAQ,CAACD;AAE7D,OAAO,SAASE,wBAAwBC,KAAmC;IACzE,MAAM,EAAEC,MAAM,EAAEC,UAAU,EAAEC,QAAQ,EAAEC,UAAU,EAAE,GAAGJ;IACrD,IAAI,CAACC,UAAU,CAACE,YAAY,CAACP,iBAAiBM,aAAa,OAAO;IAClE,qBACE,KAACP;QACCM,QAAQA;QACRJ,MAAMK;QACNG,OAAOF;OACFG,MAAMC,OAAO,CAACH,eAAeA,WAAWI,MAAM,GAAG;QAAEJ;IAAW,IAAI,CAAC;AAG9E;AACAL,wBAAwBU,WAAW,GAAG;AAEtC,eAAeV,wBAAuB"}
@@ -1,4 +1,9 @@
1
1
  import type { SiteRuntimeProps } from '@aglyn/aglyn';
2
+ /** The page-props slice the server enricher writes for a page filed under a campaign. */
3
+ export interface CampaignPageData {
4
+ screenId: string;
5
+ campaignIds: string[];
6
+ }
2
7
  /**
3
8
  * The marketing plugin's site runtime (AGL-419), relocated verbatim from
4
9
  * the tenant catch-all: announcement bar + popup rendering (AGL-195/200),
@@ -19,7 +19,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
19
19
  import * as Aglyn from "@aglyn/aglyn";
20
20
  import { sendAnalyticsBeacon } from "@aglyn/aglyn/app-utils/analytics-beacon";
21
21
  import { trackAuthoredEvent, trackEvent } from "@aglyn/aglyn/app-utils/analytics-events";
22
- import { utmTouchField } from "@aglyn/aglyn/app-utils/utm-touch";
22
+ import { claimFirstCampaignTouch, notePageCampaigns, utmTouchField, whenUtmTouchConsentSettles } from "@aglyn/aglyn/app-utils/utm-touch";
23
23
  import { useEffect, useRef, useState } from "react";
24
24
  import * as MarketingModel from "../model/index.js";
25
25
  /**
@@ -886,7 +886,9 @@ function AnnouncementBar(props) {
886
886
  }),
887
887
  popup.imageUrl && !Aglyn.isRefusedAuthorImageSrc(popup.imageUrl) ? // eslint-disable-next-line @next/next/no-img-element
888
888
  /*#__PURE__*/ _jsx("img", {
889
- src: popup.imageUrl,
889
+ src: Aglyn.siteRelativeMediaSrc(popup.imageUrl, {
890
+ hostId
891
+ }),
890
892
  // AGL-1896. This was a hardcoded `alt=""` with no field behind
891
893
  // it. Still empty when the author left it empty — a decorative
892
894
  // banner beside its own headline is the case `alt=""` exists for
@@ -1015,6 +1017,100 @@ function AnnouncementBar(props) {
1015
1017
  })
1016
1018
  });
1017
1019
  }
1020
+ /**
1021
+ * Pageviews already reported to their campaigns, keyed by host and address.
1022
+ * Module scope for the reason the pageview beacon's guard is: a page remounts
1023
+ * on its own, and a remount is not a second visit.
1024
+ */ const campaignVisitsReported = new Set();
1025
+ /**
1026
+ * How long the visit report waits for the visitor's storage consent to
1027
+ * settle before it reports the view alone. Long enough for the region check
1028
+ * a consent posture runs on load; short enough that a stalled one still
1029
+ * counts the page view.
1030
+ */ const CAMPAIGN_VISIT_CONSENT_WAIT_MS = 10000;
1031
+ /**
1032
+ * A PAGE FILED UNDER A CAMPAIGN IS A CAMPAIGN TOUCH (AGL-3461).
1033
+ *
1034
+ * On every pageview this tells the core which campaigns the page is filed
1035
+ * under — the touch every conversion door on the page then reports, live and
1036
+ * with nothing written, and the one the device remembers under the visitor's
1037
+ * analytics grant (`utm-touch.ts`). A page filed under nothing clears the
1038
+ * live touch and keeps the remembered one.
1039
+ *
1040
+ * And it reports the visit to each campaign through the site collector's
1041
+ * `campaignVisit` beacon: a view of the campaign's page, and — when this is
1042
+ * the device's first campaign touch in the window — a first visit. A link
1043
+ * carrying a `utm_campaign` label is a touch too, credited as a first visit
1044
+ * to the campaign that declares it; the server answers which one. The
1045
+ * first-touch claim waits for consent to settle, because asked before it an
1046
+ * unresolved state would read as "may not remember", and the view would be
1047
+ * counted as nobody's first.
1048
+ *
1049
+ * Renders nothing.
1050
+ */ function CampaignPageTouch(props) {
1051
+ var _ref, _ref1;
1052
+ const { hostId, campaignPage } = props;
1053
+ const screenId = (_ref = campaignPage == null ? void 0 : campaignPage.screenId) != null ? _ref : '';
1054
+ const idsKey = ((_ref1 = campaignPage == null ? void 0 : campaignPage.campaignIds) != null ? _ref1 : []).join(',');
1055
+ useEffect(()=>{
1056
+ var _get;
1057
+ if (typeof window === 'undefined') return undefined;
1058
+ const ids = idsKey ? idsKey.split(',') : [];
1059
+ notePageCampaigns(ids.length ? {
1060
+ screenId,
1061
+ containerIds: ids,
1062
+ path: window.location.pathname
1063
+ } : null);
1064
+ if (!hostId) return undefined;
1065
+ const label = ((_get = new URLSearchParams(window.location.search).get('utm_campaign')) != null ? _get : '').trim().toLowerCase().slice(0, 100);
1066
+ if (!ids.length && !label) return undefined;
1067
+ const visitKey = `${hostId}\x00${window.location.pathname}${window.location.search}`;
1068
+ if (campaignVisitsReported.has(visitKey)) return undefined;
1069
+ let done = false;
1070
+ const report = (allowed)=>{
1071
+ if (done) return;
1072
+ done = true;
1073
+ campaignVisitsReported.add(visitKey);
1074
+ const claimed = allowed ? claimFirstCampaignTouch([
1075
+ ...ids.map((id)=>`c:${id}`),
1076
+ ...label ? [
1077
+ `u:${label}`
1078
+ ] : []
1079
+ ]) : [];
1080
+ const first = claimed.filter((key)=>key.startsWith('c:')).map((key)=>key.slice(2));
1081
+ const firstLabel = Boolean(label) && claimed.includes(`u:${label}`);
1082
+ // A label arrival on a page filed under nothing is only a first visit;
1083
+ // with nothing first about it there is nothing to count.
1084
+ if (!ids.length && !firstLabel) return;
1085
+ sendAnalyticsBeacon(_extends({
1086
+ hostId,
1087
+ campaignVisit: '1'
1088
+ }, ids.length ? {
1089
+ screenId,
1090
+ campaignIds: ids
1091
+ } : {}, first.length ? {
1092
+ first
1093
+ } : {}, firstLabel ? {
1094
+ utmCampaign: label,
1095
+ firstLabel: true
1096
+ } : {}));
1097
+ };
1098
+ const withdraw = whenUtmTouchConsentSettles(report);
1099
+ const timer = window.setTimeout(()=>{
1100
+ withdraw();
1101
+ report(false);
1102
+ }, CAMPAIGN_VISIT_CONSENT_WAIT_MS);
1103
+ return ()=>{
1104
+ withdraw();
1105
+ window.clearTimeout(timer);
1106
+ };
1107
+ }, [
1108
+ hostId,
1109
+ screenId,
1110
+ idsKey
1111
+ ]);
1112
+ return null;
1113
+ }
1018
1114
  /**
1019
1115
  * The marketing plugin's site runtime (AGL-419), relocated verbatim from
1020
1116
  * the tenant catch-all: announcement bar + popup rendering (AGL-195/200),
@@ -1022,7 +1118,7 @@ function AnnouncementBar(props) {
1022
1118
  * (AGL-256/257), and automation-triggered overlays. Reads back exactly the
1023
1119
  * page-props slices its server enricher wrote (site-contract shapes).
1024
1120
  */ export function MarketingSiteRuntime({ hostId, screens, page }) {
1025
- var _page_experiments, _page_clientAutomations, _automationOverlay_popup_frequencyDays;
1121
+ var _page_experiments, _page_clientAutomations, _page_campaignPage, _automationOverlay_popup_frequencyDays;
1026
1122
  var _page_data, _automationOverlay_bar, _automationOverlay_popup;
1027
1123
  const [automationOverlay, setAutomationOverlay] = useState(null);
1028
1124
  const announcementBar = page['announcementBar'];
@@ -1030,8 +1126,13 @@ function AnnouncementBar(props) {
1030
1126
  const experiments = (_page_experiments = page['experiments']) != null ? _page_experiments : [];
1031
1127
  const clientAutomations = (_page_clientAutomations = page['clientAutomations']) != null ? _page_clientAutomations : [];
1032
1128
  const automationOverlays = page['automationOverlays'];
1129
+ const campaignPage = (_page_campaignPage = page['campaignPage']) != null ? _page_campaignPage : null;
1033
1130
  return /*#__PURE__*/ _jsxs(_Fragment, {
1034
1131
  children: [
1132
+ /*#__PURE__*/ _jsx(CampaignPageTouch, {
1133
+ hostId: hostId,
1134
+ campaignPage: campaignPage
1135
+ }),
1035
1136
  announcementBar ? /*#__PURE__*/ _jsx(AnnouncementBar, {
1036
1137
  bar: announcementBar,
1037
1138
  hostId: hostId