Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
Expand Up @@ -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

# ==============================================================================
Expand Down
25 changes: 25 additions & 0 deletions src/app/_components/analytics/DigitalDataScript.tsx
Original file line number Diff line number Diff line change
@@ -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(/</g, "\\u003c")
.replace(/\u2028/g, "\\u2028")
.replace(/\u2029/g, "\\u2029");
};

export const DigitalDataScript = () => {
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 <script id="digital-data-script" dangerouslySetInnerHTML={{ __html: digitalDataScript }} />;
};
127 changes: 127 additions & 0 deletions src/app/_components/analytics/digitalDataConfig.ts
Original file line number Diff line number Diff line change
@@ -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<string, PageCategory> = {
"/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 },
},
};
};
7 changes: 7 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -29,6 +30,12 @@ export default function RootLayout({

{/* NHS app js - https://nhsconnect.github.io/nhsapp-developer-documentation/js-v2-api-specification/ */}
<Script src={"https://www.nhsapp.service.nhs.uk/js/v2/nhsapp.js"} strategy="beforeInteractive" />
<DigitalDataScript />
<Script
id="adobe-launch-script"
src="https://assets.adobedtm.com/f8560165ec6a/de7c895bef21/launch-284cbaabc37d-development.min.js"
strategy="afterInteractive"
/>
</head>

<body suppressHydrationWarning>
Expand Down
1 change: 1 addition & 0 deletions src/types/nhsapp-js.d.ts
Original file line number Diff line number Diff line change
@@ -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: {
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
Loading