diff --git a/.tool-versions b/.tool-versions
index d7fea77c..848faad1 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -3,6 +3,7 @@
terraform 1.14.1
pre-commit 4.5.0
vale 3.13.0
+gitleaks 8.30.1
nodejs 22.13.1
# ==============================================================================
diff --git a/src/app/_components/analytics/DigitalDataScript.tsx b/src/app/_components/analytics/DigitalDataScript.tsx
new file mode 100644
index 00000000..5f6db496
--- /dev/null
+++ b/src/app/_components/analytics/DigitalDataScript.tsx
@@ -0,0 +1,25 @@
+"use client";
+
+import { buildDigitalData } from "@src/app/_components/analytics/digitalDataConfig";
+import { usePathname } from "next/navigation";
+import { useEffect } from "react";
+
+const toSafeInlineScriptJson = (value: unknown): string => {
+ return JSON.stringify(value)
+ .replace(/ {
+ const pathname = usePathname();
+ const digitalData = buildDigitalData(pathname);
+ const digitalDataScript = `window.digitalData = ${toSafeInlineScriptJson(digitalData)};`;
+
+ // Keep window.digitalData current on every client-side navigation
+ useEffect(() => {
+ window.digitalData = buildDigitalData(pathname);
+ }, [pathname]);
+
+ return ;
+};
diff --git a/src/app/_components/analytics/digitalDataConfig.ts b/src/app/_components/analytics/digitalDataConfig.ts
new file mode 100644
index 00000000..574c9453
--- /dev/null
+++ b/src/app/_components/analytics/digitalDataConfig.ts
@@ -0,0 +1,127 @@
+type DigitalData = {
+ page: {
+ pageInfo: {
+ pageName: string;
+ };
+ category: {
+ primaryCategory: string;
+ subCategory1: string;
+ subCategory2: string;
+ subCategory3: string;
+ };
+ };
+};
+
+type PageCategory = {
+ pageName: string;
+ primaryCategory: string;
+ subCategory1: string;
+ subCategory2: string;
+ subCategory3: string;
+};
+
+export const PAGE_DIGITAL_DATA: Record = {
+ "/check-and-book-vaccinations": {
+ pageName: "nhs:vds:home",
+ primaryCategory: "vaccinations",
+ subCategory1: "hub",
+ subCategory2: "",
+ subCategory3: "",
+ },
+ "/vaccines-for-all-ages": {
+ pageName: "nhs:vds:vaccines-for-all-ages",
+ primaryCategory: "vaccinations",
+ subCategory1: "vaccines-for-all-ages",
+ subCategory2: "",
+ subCategory3: "",
+ },
+ "/vaccines-during-pregnancy": {
+ pageName: "nhs:vds:vaccines-during-pregnancy",
+ primaryCategory: "vaccinations",
+ subCategory1: "vaccines-during-pregnancy",
+ subCategory2: "",
+ subCategory3: "",
+ },
+ "/our-policies/accessibility": {
+ pageName: "nhs:vds:our-policies:accessibility",
+ primaryCategory: "policies",
+ subCategory1: "accessibility",
+ subCategory2: "",
+ subCategory3: "",
+ },
+ "/our-policies/cookies-policy": {
+ pageName: "nhs:vds:our-policies:cookies-policy",
+ primaryCategory: "policies",
+ subCategory1: "cookies-policy",
+ subCategory2: "",
+ subCategory3: "",
+ },
+ "/service-failure": {
+ pageName: "nhs:vds:service-failure",
+ primaryCategory: "error",
+ subCategory1: "service-failure",
+ subCategory2: "",
+ subCategory3: "",
+ },
+ "/sso-failure": {
+ pageName: "nhs:vds:sso-failure",
+ primaryCategory: "error",
+ subCategory1: "sso-failure",
+ subCategory2: "",
+ subCategory3: "",
+ },
+ "/session-logout": {
+ pageName: "nhs:vds:session:logout",
+ primaryCategory: "session",
+ subCategory1: "logout",
+ subCategory2: "",
+ subCategory3: "",
+ },
+ "/session-timeout": {
+ pageName: "nhs:vds:session:timeout",
+ primaryCategory: "session",
+ subCategory1: "timeout",
+ subCategory2: "",
+ subCategory3: "",
+ },
+};
+
+const DEFAULT_PAGE_CATEGORY: PageCategory = {
+ pageName: "nhs:vds:unknown",
+ primaryCategory: "unknown",
+ subCategory1: "",
+ subCategory2: "",
+ subCategory3: "",
+};
+
+const resolvePageCategory = (pathname: string): PageCategory => {
+ if (PAGE_DIGITAL_DATA[pathname]) {
+ return PAGE_DIGITAL_DATA[pathname];
+ }
+
+ // Handle dynamic routes e.g. /vaccines/flu-vaccine
+ const vaccineMatch = pathname.match(/^\/vaccines\/([^/]+)$/);
+ if (vaccineMatch) {
+ const vaccine = vaccineMatch[1];
+ return {
+ pageName: `nhs:vds:vaccinations:vaccine:${vaccine}`,
+ primaryCategory: "vaccinations",
+ subCategory1: "vaccine",
+ subCategory2: vaccine,
+ subCategory3: "",
+ };
+ }
+
+ return DEFAULT_PAGE_CATEGORY;
+};
+
+export const buildDigitalData = (pathname: string): DigitalData => {
+ const { pageName, primaryCategory, subCategory1, subCategory2, subCategory3 } = resolvePageCategory(pathname);
+
+ return {
+ page: {
+ pageInfo: { pageName },
+ category: { primaryCategory, subCategory1, subCategory2, subCategory3 },
+ },
+ };
+};
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index e18f53ab..82584eae 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,3 +1,4 @@
+import { DigitalDataScript } from "@src/app/_components/analytics/DigitalDataScript";
import { ClientUnhandledErrorLogger } from "@src/app/_components/client-unhandled-error-logger/ClientUnhandledErrorLogger";
import { ClientProviders } from "@src/app/_components/context/ClientProviders";
import { NoJsMessage } from "@src/app/_components/static/NoJsMessage";
@@ -29,6 +30,12 @@ export default function RootLayout({
{/* NHS app js - https://nhsconnect.github.io/nhsapp-developer-documentation/js-v2-api-specification/ */}
+
+
diff --git a/src/types/nhsapp-js.d.ts b/src/types/nhsapp-js.d.ts
index 4fbc9157..1b4511fe 100644
--- a/src/types/nhsapp-js.d.ts
+++ b/src/types/nhsapp-js.d.ts
@@ -1,5 +1,6 @@
interface Window {
// Reference: https://nhsconnect.github.io/nhsapp-developer-documentation/js-v2-api-specification/
+ digitalData?: unknown;
nhsapp: {
// tools functions are available within both native mobile and desktop app
tools: {
diff --git a/tsconfig.json b/tsconfig.json
index 18e8f32c..12e3b674 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -28,6 +28,6 @@
}
},
"types": ["node", "jest", "@testing-library/jest-dom"],
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts", ".next/types/**/*.ts"],
"exclude": ["node_modules", "docs", "scripts", ".next", ".open-next"]
}