---
.../app/get-started/page.tsx | 8 +-
.../__tests__/marketingTracking.guard.test.ts | 14 ++-
.../components/ai/ClosingCta.tsx | 4 +-
.../components/landing/FinalCta.tsx | 16 ++--
.../components/landing/Hero.tsx | 8 +-
.../lib/__tests__/entryAttribution.test.ts | 93 +++++++++++++++++++
.../lib/appInsightsRuntime.ts | 9 ++
.../lib/entryAttribution.ts | 73 +++++++++++++++
8 files changed, 210 insertions(+), 15 deletions(-)
create mode 100644 src/Web/autopilot-monitor-web/lib/__tests__/entryAttribution.test.ts
create mode 100644 src/Web/autopilot-monitor-web/lib/entryAttribution.ts
diff --git a/src/Web/autopilot-monitor-web/app/get-started/page.tsx b/src/Web/autopilot-monitor-web/app/get-started/page.tsx
index ece81b08..990028f6 100644
--- a/src/Web/autopilot-monitor-web/app/get-started/page.tsx
+++ b/src/Web/autopilot-monitor-web/app/get-started/page.tsx
@@ -1,4 +1,5 @@
import type { Metadata } from "next";
+import Link from "next/link";
import { LandingNavbar } from "../../components/landing/LandingNavbar";
import { SignupConsentCta } from "../../components/landing/SignupConsentCta";
import { SiteFooter } from "../../components/SiteFooter";
@@ -102,13 +103,14 @@ export default function GetStartedPage() {
When you need more later: the{" "}
-
Pro plan
- {" "}
+ {" "}
adds SLAs, support commitments, MSP delegation, higher operating limits and more —
same service, nothing to migrate.
diff --git a/src/Web/autopilot-monitor-web/components/__tests__/marketingTracking.guard.test.ts b/src/Web/autopilot-monitor-web/components/__tests__/marketingTracking.guard.test.ts
index 9973d500..00ce6d24 100644
--- a/src/Web/autopilot-monitor-web/components/__tests__/marketingTracking.guard.test.ts
+++ b/src/Web/autopilot-monitor-web/components/__tests__/marketingTracking.guard.test.ts
@@ -1,5 +1,5 @@
import { describe, it, expect, vi } from "vitest";
-import { readFileSync } from "node:fs";
+import { readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { createElement, type ComponentType } from "react";
import { renderToStaticMarkup } from "react-dom/server";
@@ -73,6 +73,18 @@ describe("marketing page tracking", () => {
expect(html).toMatch(/]*\sid="push"[^>]*\sdata-track-section="push"/);
});
+ // A plain loads a new document: the click's entry channel (lib/entryAttribution.ts) would
+ // turn into the own host. The static markup cannot tell a from an , so the source is read.
+ it("links to get-started and plans are client-side Links, so the page load and its entry channel carry over", () => {
+ const root = join(__dirname, "..", "..");
+ const plain = ["components", "app"].flatMap(dir =>
+ readdirSync(join(root, dir), { encoding: "utf-8", recursive: true })
+ .filter(file => file.endsWith(".tsx") && !file.includes("__tests__"))
+ .filter(file => / ]*\shref="\/(?:get-started|plans)\b/.test(readFileSync(join(root, dir, file), "utf-8")))
+ );
+ expect(plain).toEqual([]);
+ });
+
it("the AI page carries every question text in its static HTML", () => {
const html = render(AiPage);
const escape = (text: string) => text.replace(/&/g, "&").replace(/'/g, "'");
diff --git a/src/Web/autopilot-monitor-web/components/ai/ClosingCta.tsx b/src/Web/autopilot-monitor-web/components/ai/ClosingCta.tsx
index da68b0b9..e670f474 100644
--- a/src/Web/autopilot-monitor-web/components/ai/ClosingCta.tsx
+++ b/src/Web/autopilot-monitor-web/components/ai/ClosingCta.tsx
@@ -20,9 +20,9 @@ export function ClosingCta() {
Free, open source, deployed in minutes.
{"Need SLAs, support, or MSP delegation? "}
-
+
There's a Pro plan
-
+
.