From 64aa90d159bfae5601ee9d58323f21799af5deb6 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Tue, 11 Jun 2024 11:34:01 -0400
Subject: [PATCH 01/29] add custom fetcher for limited access

---
 src/components/EditionCard/ReadOnlineLink.tsx |  6 +++-
 src/components/ReaderLayout/ReaderLayout.tsx  | 18 ++++++++----
 src/types/DataModel.ts                        |  1 +
 src/util/EditionCardUtils.tsx                 | 29 +++++++++++++++++++
 4 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/src/components/EditionCard/ReadOnlineLink.tsx b/src/components/EditionCard/ReadOnlineLink.tsx
index 95e84f95..b3c7c1d1 100644
--- a/src/components/EditionCard/ReadOnlineLink.tsx
+++ b/src/components/EditionCard/ReadOnlineLink.tsx
@@ -9,13 +9,17 @@ const ReadOnlineLink: React.FC<{
   readOnlineLink: ItemLink;
   isLoggedIn: boolean;
   title: string;
+  loginCookie?: any;
 }> = ({ readOnlineLink, isLoggedIn, title }) => {
   let linkText = "Read Online";
   let linkUrl: any = {
     pathname: `/read/${readOnlineLink.link_id}`,
   };
 
-  if (readOnlineLink.flags.nypl_login && !isLoggedIn) {
+  if (
+    (readOnlineLink.flags.nypl_login || readOnlineLink.flags.limit_access) &&
+    !isLoggedIn
+  ) {
     linkText = "Log in to read online";
     linkUrl = LOGIN_LINK_BASE + encodeURIComponent(window.location.href);
   }
diff --git a/src/components/ReaderLayout/ReaderLayout.tsx b/src/components/ReaderLayout/ReaderLayout.tsx
index cca41102..c19f9d4e 100644
--- a/src/components/ReaderLayout/ReaderLayout.tsx
+++ b/src/components/ReaderLayout/ReaderLayout.tsx
@@ -10,6 +10,13 @@ import { MAX_TITLE_LENGTH } from "~/src/constants/editioncard";
 import dynamic from "next/dynamic";
 import { MediaTypes } from "~/src/constants/mediaTypes";
 import ReaderLogoSvg from "../Svgs/ReaderLogoSvg";
+import Link from "../Link/Link";
+import { addTocToManifest } from "@nypl/web-reader";
+import Loading from "../Loading/Loading";
+import { trackCtaClick } from "~/src/lib/adobe/Analytics";
+import { useCookies } from "react-cookie";
+import { NYPL_SESSION_ID } from "~/src/constants/auth";
+
 const WebReader = dynamic(() => import("@nypl/web-reader"), { ssr: false });
 // This is how we can import a css file as a url. It's complex, but necessary
 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -22,11 +29,6 @@ import readiumDefault from "!file-loader!extract-loader!css-loader!@nypl/web-rea
 // @ts-ignore
 import readiumAfter from "!file-loader!extract-loader!css-loader!@nypl/web-reader/dist/injectable-html-styles/ReadiumCSS-after.css";
 
-import Link from "../Link/Link";
-import { addTocToManifest } from "@nypl/web-reader";
-import Loading from "../Loading/Loading";
-import { trackCtaClick } from "~/src/lib/adobe/Analytics";
-
 const origin =
   typeof window !== "undefined" && window.location?.origin
     ? window.location.origin
@@ -64,9 +66,14 @@ const ReaderLayout: React.FC<{
   const edition = link.work.editions[0];
   const [manifestUrl, setManifestUrl] = useState(url);
   const [isLoading, setIsLoading] = useState(true);
+  const [cookies] = useCookies([NYPL_SESSION_ID]);
+  const getContent = EditionCardUtils.createGetContent(
+    cookies[NYPL_SESSION_ID]
+  );
 
   const isEmbed = MediaTypes.embed.includes(link.media_type);
   const isRead = MediaTypes.read.includes(link.media_type);
+  const isLimitedAccess = link.flags.nypl_login;
 
   const pdfWorkerSrc = `${origin}/pdf-worker/pdf.worker.min.js`;
 
@@ -180,6 +187,7 @@ const ReaderLayout: React.FC<{
           pdfWorkerSrc={pdfWorkerSrc}
           headerLeft={<BackButton />}
           injectablesFixed={injectables}
+          getContent={isLimitedAccess ? getContent : null}
         />
       )}
     </>
diff --git a/src/types/DataModel.ts b/src/types/DataModel.ts
index 3b434cee..6f9dc005 100644
--- a/src/types/DataModel.ts
+++ b/src/types/DataModel.ts
@@ -65,6 +65,7 @@ export type LinkFlags = {
   edd?: boolean;
   embed?: boolean;
   nypl_login?: boolean;
+  limit_access?: boolean;
 };
 
 export type ItemLink = {
diff --git a/src/util/EditionCardUtils.tsx b/src/util/EditionCardUtils.tsx
index e59b2334..0252905c 100644
--- a/src/util/EditionCardUtils.tsx
+++ b/src/util/EditionCardUtils.tsx
@@ -16,6 +16,8 @@ import {
   PLACEHOLDER_COVER_LINK,
 } from "../constants/editioncard";
 import { MediaTypes } from "../constants/mediaTypes";
+import { LOGIN_LINK_BASE } from "../constants/links";
+import { FulfillResult } from "../types/FulfillQuery";
 
 // EditionCard holds all the methods needed to build an Edition Card
 export default class EditionCardUtils {
@@ -220,4 +222,31 @@ export default class EditionCardUtils {
         : undefined;
     return universityPress !== undefined;
   }
+
+  static createGetContent(nyplIdentityCookie) {
+    return async (linkUrl: string) => {
+      const url = new URL(linkUrl);
+      const res = await fetch(url.toString(), {
+        method: "GET",
+        headers: {
+          Authorization: `Bearer ${nyplIdentityCookie.access_token}`,
+        },
+      });
+      if (res.ok) {
+        return res.url;
+      } else {
+        // redirect to the NYPL login page if access token is invalid
+        if (res.status === 401) {
+          return `${LOGIN_LINK_BASE}${encodeURIComponent(
+            window.location.href
+          )}`;
+        }
+        if (res.status === 404) {
+          const fulfillResult: FulfillResult = await res.json();
+          throw new Error(fulfillResult.data);
+        }
+      }
+      return undefined;
+    };
+  }
 }

From fca7d8a94dc50e5a722ad5d3e56ec651a8563ad7 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 13 Jun 2024 10:32:48 -0400
Subject: [PATCH 02/29] update flag name

---
 src/components/EditionCard/ReadOnlineLink.tsx | 5 +----
 src/components/ReaderLayout/ReaderLayout.tsx  | 2 +-
 src/types/DataModel.ts                        | 2 +-
 3 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/src/components/EditionCard/ReadOnlineLink.tsx b/src/components/EditionCard/ReadOnlineLink.tsx
index b3c7c1d1..3445a4df 100644
--- a/src/components/EditionCard/ReadOnlineLink.tsx
+++ b/src/components/EditionCard/ReadOnlineLink.tsx
@@ -16,10 +16,7 @@ const ReadOnlineLink: React.FC<{
     pathname: `/read/${readOnlineLink.link_id}`,
   };
 
-  if (
-    (readOnlineLink.flags.nypl_login || readOnlineLink.flags.limit_access) &&
-    !isLoggedIn
-  ) {
+  if (readOnlineLink.flags.nypl_login && !isLoggedIn) {
     linkText = "Log in to read online";
     linkUrl = LOGIN_LINK_BASE + encodeURIComponent(window.location.href);
   }
diff --git a/src/components/ReaderLayout/ReaderLayout.tsx b/src/components/ReaderLayout/ReaderLayout.tsx
index c19f9d4e..e81cb334 100644
--- a/src/components/ReaderLayout/ReaderLayout.tsx
+++ b/src/components/ReaderLayout/ReaderLayout.tsx
@@ -73,7 +73,7 @@ const ReaderLayout: React.FC<{
 
   const isEmbed = MediaTypes.embed.includes(link.media_type);
   const isRead = MediaTypes.read.includes(link.media_type);
-  const isLimitedAccess = link.flags.nypl_login;
+  const isLimitedAccess = link.flags.fulfill_limited_access;
 
   const pdfWorkerSrc = `${origin}/pdf-worker/pdf.worker.min.js`;
 
diff --git a/src/types/DataModel.ts b/src/types/DataModel.ts
index 6f9dc005..a8e6de32 100644
--- a/src/types/DataModel.ts
+++ b/src/types/DataModel.ts
@@ -65,7 +65,7 @@ export type LinkFlags = {
   edd?: boolean;
   embed?: boolean;
   nypl_login?: boolean;
-  limit_access?: boolean;
+  fulfill_limited_access?: boolean;
 };
 
 export type ItemLink = {

From 9e3f55393e23ab37d5c4db435ad0bf9acc121f4b Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 13 Jun 2024 10:58:59 -0400
Subject: [PATCH 03/29] fix search bar issue

---
 CHANGELOG.md                                  | 4 ++++
 src/components/SearchHeader/SearchHeader.scss | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b1df02b..262b6783 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # CHANGE LOG
 
+## [Pre-release]
+- Fix cut off text on search bar dropdown
+
+
 ## [0.18.1]
 
 - SFR-1917: Fixed failing Playwright tests
diff --git a/src/components/SearchHeader/SearchHeader.scss b/src/components/SearchHeader/SearchHeader.scss
index 4043e106..ad16de7c 100644
--- a/src/components/SearchHeader/SearchHeader.scss
+++ b/src/components/SearchHeader/SearchHeader.scss
@@ -2,4 +2,8 @@
   @media (max-width: 38em) {
     margin-bottom: var(--nypl-space-xs);
   }
+}
+
+#searchbar-select-search-bar {
+  margin-right: 0;
 }
\ No newline at end of file

From 3bbb8c7552fa78ae612643674cac6355ea25c9b0 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Fri, 21 Jun 2024 15:50:58 -0400
Subject: [PATCH 04/29] update broken link on about page

---
 CHANGELOG.md                                           | 2 +-
 src/components/About/About.tsx                         | 2 +-
 src/components/About/__snapshots__/About.test.tsx.snap | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 262b6783..e6f71e11 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,7 @@
 
 ## [Pre-release]
 - Fix cut off text on search bar dropdown
-
+- Fix broken link on the About page
 
 ## [0.18.1]
 
diff --git a/src/components/About/About.tsx b/src/components/About/About.tsx
index e996043b..f31904ee 100644
--- a/src/components/About/About.tsx
+++ b/src/components/About/About.tsx
@@ -43,7 +43,7 @@ const About: React.FC = () => {
         In addition to collecting these digital editions, we group all the
         editions of the same title together as a single “work.” For instance
         there are many editions of{" "}
-        <Link to="/work/e34d73df-f32b-49e1-8fdf-151db2a7806a">
+        <Link to="/work/8771d353-b75f-4f30-a424-e3b9516601f0">
           Mary Wollstonecraft’s A Vindication of the Rights of Woman
         </Link>
         , many of them available digitally. We group them all together under a
diff --git a/src/components/About/__snapshots__/About.test.tsx.snap b/src/components/About/__snapshots__/About.test.tsx.snap
index d6cfbbcf..1c010c5e 100644
--- a/src/components/About/__snapshots__/About.test.tsx.snap
+++ b/src/components/About/__snapshots__/About.test.tsx.snap
@@ -161,7 +161,7 @@ exports[`renders about unchanged 1`] = `
          
         <a
           class="chakra-link css-qsaw8"
-          href="/work/e34d73df-f32b-49e1-8fdf-151db2a7806a"
+          href="/work/8771d353-b75f-4f30-a424-e3b9516601f0"
         >
           Mary Wollstonecraft’s A Vindication of the Rights of Woman
         </a>

From 909494b69627dd0969cc00f1fbb80984b0fb459d Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Mon, 24 Jun 2024 14:21:46 -0400
Subject: [PATCH 05/29] update getContent function

---
 package-lock.json                            | 36 ++++++++---------
 package.json                                 |  2 +-
 src/components/ReaderLayout/ReaderLayout.tsx |  8 +---
 src/util/EditionCardUtils.tsx                | 41 +++++++-------------
 4 files changed, 34 insertions(+), 53 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index ab6011f7..169c929c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,7 +22,7 @@
         "next": "^13.5.6",
         "next-transpile-modules": "^7.0.0",
         "react": "^18.2.0",
-        "react-cookie": "^4.1.1",
+        "react-cookie": "7.1.4",
         "redux": "4.0.1",
         "sass": "^1.62.1",
         "typescript": "^4.1.3"
@@ -7723,9 +7723,9 @@
       }
     },
     "node_modules/@types/cookie": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
-      "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
+      "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
     },
     "node_modules/@types/eslint": {
       "version": "8.56.10",
@@ -10861,9 +10861,9 @@
       "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
     },
     "node_modules/cookie": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
-      "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
+      "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
       "engines": {
         "node": ">= 0.6"
       }
@@ -20655,13 +20655,13 @@
       }
     },
     "node_modules/react-cookie": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.1.1.tgz",
-      "integrity": "sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A==",
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.1.4.tgz",
+      "integrity": "sha512-wDxxa/HYaSXSMlyWJvJ5uZTzIVtQTPf1gMksFgwAz/2/W3lCtY8r4OChCXMPE7wax0PAdMY97UkNJedGv7KnDw==",
       "dependencies": {
-        "@types/hoist-non-react-statics": "^3.0.1",
-        "hoist-non-react-statics": "^3.0.0",
-        "universal-cookie": "^4.0.0"
+        "@types/hoist-non-react-statics": "^3.3.5",
+        "hoist-non-react-statics": "^3.3.2",
+        "universal-cookie": "^7.0.0"
       },
       "peerDependencies": {
         "react": ">= 16.3.0"
@@ -23532,12 +23532,12 @@
       }
     },
     "node_modules/universal-cookie": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz",
-      "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==",
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.1.4.tgz",
+      "integrity": "sha512-Q+DVJsdykStWRMtXr2Pdj3EF98qZHUH/fXv/gwFz/unyToy1Ek1w5GsWt53Pf38tT8Gbcy5QNsj61Xe9TggP4g==",
       "dependencies": {
-        "@types/cookie": "^0.3.3",
-        "cookie": "^0.4.0"
+        "@types/cookie": "^0.6.0",
+        "cookie": "^0.6.0"
       }
     },
     "node_modules/universalify": {
diff --git a/package.json b/package.json
index 2e449daf..b2161fb9 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
     "next": "^13.5.6",
     "next-transpile-modules": "^7.0.0",
     "react": "^18.2.0",
-    "react-cookie": "^4.1.1",
+    "react-cookie": "7.1.4",
     "redux": "4.0.1",
     "sass": "^1.62.1",
     "typescript": "^4.1.3"
diff --git a/src/components/ReaderLayout/ReaderLayout.tsx b/src/components/ReaderLayout/ReaderLayout.tsx
index e81cb334..94bfd22d 100644
--- a/src/components/ReaderLayout/ReaderLayout.tsx
+++ b/src/components/ReaderLayout/ReaderLayout.tsx
@@ -14,8 +14,6 @@ import Link from "../Link/Link";
 import { addTocToManifest } from "@nypl/web-reader";
 import Loading from "../Loading/Loading";
 import { trackCtaClick } from "~/src/lib/adobe/Analytics";
-import { useCookies } from "react-cookie";
-import { NYPL_SESSION_ID } from "~/src/constants/auth";
 
 const WebReader = dynamic(() => import("@nypl/web-reader"), { ssr: false });
 // This is how we can import a css file as a url. It's complex, but necessary
@@ -66,10 +64,6 @@ const ReaderLayout: React.FC<{
   const edition = link.work.editions[0];
   const [manifestUrl, setManifestUrl] = useState(url);
   const [isLoading, setIsLoading] = useState(true);
-  const [cookies] = useCookies([NYPL_SESSION_ID]);
-  const getContent = EditionCardUtils.createGetContent(
-    cookies[NYPL_SESSION_ID]
-  );
 
   const isEmbed = MediaTypes.embed.includes(link.media_type);
   const isRead = MediaTypes.read.includes(link.media_type);
@@ -187,7 +181,7 @@ const ReaderLayout: React.FC<{
           pdfWorkerSrc={pdfWorkerSrc}
           headerLeft={<BackButton />}
           injectablesFixed={injectables}
-          getContent={isLimitedAccess ? getContent : null}
+          getContent={isLimitedAccess ? EditionCardUtils.fetchWithAuth : undefined}
         />
       )}
     </>
diff --git a/src/util/EditionCardUtils.tsx b/src/util/EditionCardUtils.tsx
index 0252905c..952353ce 100644
--- a/src/util/EditionCardUtils.tsx
+++ b/src/util/EditionCardUtils.tsx
@@ -16,8 +16,9 @@ import {
   PLACEHOLDER_COVER_LINK,
 } from "../constants/editioncard";
 import { MediaTypes } from "../constants/mediaTypes";
-import { LOGIN_LINK_BASE } from "../constants/links";
 import { FulfillResult } from "../types/FulfillQuery";
+import { useCookies } from "react-cookie";
+import { NYPL_SESSION_ID } from "../constants/auth";
 
 // EditionCard holds all the methods needed to build an Edition Card
 export default class EditionCardUtils {
@@ -223,30 +224,16 @@ export default class EditionCardUtils {
     return universityPress !== undefined;
   }
 
-  static createGetContent(nyplIdentityCookie) {
-    return async (linkUrl: string) => {
-      const url = new URL(linkUrl);
-      const res = await fetch(url.toString(), {
-        method: "GET",
-        headers: {
-          Authorization: `Bearer ${nyplIdentityCookie.access_token}`,
-        },
-      });
-      if (res.ok) {
-        return res.url;
-      } else {
-        // redirect to the NYPL login page if access token is invalid
-        if (res.status === 401) {
-          return `${LOGIN_LINK_BASE}${encodeURIComponent(
-            window.location.href
-          )}`;
-        }
-        if (res.status === 404) {
-          const fulfillResult: FulfillResult = await res.json();
-          throw new Error(fulfillResult.data);
-        }
-      }
-      return undefined;
-    };
-  }
+  static fetchWithAuth = async (linkUrl: string) => {
+    const [cookies] = useCookies([NYPL_SESSION_ID]);
+    const nyplIdentityCookie = cookies[NYPL_SESSION_ID];
+    const url = new URL(linkUrl);
+    const res = await fetch(url.toString(), {
+      method: "GET",
+      headers: {
+        Authorization: `Bearer ${nyplIdentityCookie.access_token}`,
+      },
+    });
+    return res.url;
+  };
 }

From 71b7b43d627962d9204323200da2ca91d826363a Mon Sep 17 00:00:00 2001
From: Shejanul Ayan Islam <100955969+ayan1229@users.noreply.github.com>
Date: Mon, 8 Jul 2024 16:35:32 -0400
Subject: [PATCH 06/29] SFR-2008/Automate_Licens_Page (#504)

---
 CHANGELOG.md                            |  4 +++-
 playwright/features/licensePage.feature | 13 +++++++++++++
 playwright/support/mappings.ts          | 11 +++++++++++
 3 files changed, 27 insertions(+), 1 deletion(-)
 create mode 100644 playwright/features/licensePage.feature

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e6f71e11..ad3d50c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,10 @@
 # CHANGE LOG
 
 ## [Pre-release]
+
 - Fix cut off text on search bar dropdown
 - Fix broken link on the About page
+- SFR-2008: Automate License Page Headers and Sub-Headers
 
 ## [0.18.1]
 
@@ -13,7 +15,7 @@
 - Update README to include production release information
 - Upgrade NYPL Design System to 3.1.1
 - Upgrade Web Reader to 4.3.4
-- Fix issue where 0 character is shown when there are no authors 
+- Fix issue where 0 character is shown when there are no authors
 
 ## [0.18.0]
 
diff --git a/playwright/features/licensePage.feature b/playwright/features/licensePage.feature
new file mode 100644
index 00000000..e2a786b5
--- /dev/null
+++ b/playwright/features/licensePage.feature
@@ -0,0 +1,13 @@
+Feature: License Page
+
+    Scenario: As a user I want to verify the headers and subheaders of DRB License Page
+        Given I go to the "license" page
+        Then the "license explanations header" should be displayed
+        Then the "public domain header" should be displayed
+        Then the "public domain us only header" should be displayed
+        Then the "creative commons licenses header" should be displayed
+
+    Scenario: As a user I verify the subheaders of DRB License Page are displayed correctly
+        Given I go to the "license" page
+        Then the "public domain subheader" should be displayed
+        Then the "public domain us only subheader" should be displayed
\ No newline at end of file
diff --git a/playwright/support/mappings.ts b/playwright/support/mappings.ts
index 3b6b3664..210ca343 100644
--- a/playwright/support/mappings.ts
+++ b/playwright/support/mappings.ts
@@ -204,6 +204,17 @@ export const elements = {
     "//a[@href='http://www.nypl.org/help/about-nypl/legal-notices/rules-and-regulations']",
   "about NYPL footer link": "//a[@href='http://www.nypl.org/help/about-nypl']",
   "language footer link": "//a[@href='http://www.nypl.org/language']",
+
+  /** license page locators */
+  "license explanations header": "//h1[text()='License Explanations']",
+  "public domain header": "//h2[text()='Public Domain']",
+  "creative commons licenses header":
+    "//h2[text()='Creative Commons Licenses']",
+  "public domain us only header": "//h2[text()='Public Domain (US Only)']",
+  "public domain subheader":
+    "//p[contains(text(),'Works may be in the public domain in the Unites St')]",
+  "public domain us only subheader":
+    "//p[contains(text(),'Works may be in the public domain in the Unites States')]",
 };
 
 export const inputs = {

From 194598521c50b8630e2cfc90ab0939b5df666096 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Wed, 10 Jul 2024 16:09:22 -0400
Subject: [PATCH 07/29] update webreader version and implement getContent
 function

---
 package-lock.json                             | 44 ++++++++--------
 package.json                                  |  2 +-
 src/components/EditionCard/ReadOnlineLink.tsx |  2 +-
 src/components/InstanceCard/InstanceCard.tsx  | 15 +++++-
 src/components/ReaderLayout/ReaderLayout.tsx  | 16 +++++-
 src/util/EditionCardUtils.tsx                 | 50 +++++++++++++------
 6 files changed, 87 insertions(+), 42 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 169c929c..5c2fa50f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
         "@chakra-ui/react": "2.5.4",
         "@newrelic/next": "^0.7.0",
         "@nypl/design-system-react-components": "3.1.1",
-        "@nypl/web-reader": "4.3.4",
+        "@nypl/web-reader": "4.3.5",
         "@types/node": "^16.11.6",
         "css-loader": "^7.1.1",
         "dotenv": "^16.0.3",
@@ -2332,11 +2332,11 @@
       }
     },
     "node_modules/@grpc/grpc-js": {
-      "version": "1.10.6",
-      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.6.tgz",
-      "integrity": "sha512-xP58G7wDQ4TCmN/cMUHh00DS7SRDv/+lC+xFLrTkMIN8h55X5NhZMLYbvy7dSELP15qlI6hPhNCRWVMtZMwqLA==",
+      "version": "1.10.10",
+      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.10.tgz",
+      "integrity": "sha512-HPa/K5NX6ahMoeBv15njAc/sfF4/jmiXLar9UlC2UfHFKZzsCVLc3wbe7+7qua7w9VPh2/L6EBxyAV7/E8Wftg==",
       "dependencies": {
-        "@grpc/proto-loader": "^0.7.10",
+        "@grpc/proto-loader": "^0.7.13",
         "@js-sdsl/ordered-map": "^4.4.2"
       },
       "engines": {
@@ -2344,13 +2344,13 @@
       }
     },
     "node_modules/@grpc/proto-loader": {
-      "version": "0.7.12",
-      "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.12.tgz",
-      "integrity": "sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q==",
+      "version": "0.7.13",
+      "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz",
+      "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==",
       "dependencies": {
         "lodash.camelcase": "^4.3.0",
         "long": "^5.0.0",
-        "protobufjs": "^7.2.4",
+        "protobufjs": "^7.2.5",
         "yargs": "^17.7.2"
       },
       "bin": {
@@ -5894,9 +5894,9 @@
       "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
     },
     "node_modules/@nypl/web-reader": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/@nypl/web-reader/-/web-reader-4.3.4.tgz",
-      "integrity": "sha512-nC0yL3rsGkipG7Qyv+Uv5oGkwV0Bqo/LY2im3OfRzBNn0ytltynWWtrz6PJRbOt3n4YYWsxKrqk3dj7XpEZrDA==",
+      "version": "4.3.5",
+      "resolved": "https://registry.npmjs.org/@nypl/web-reader/-/web-reader-4.3.5.tgz",
+      "integrity": "sha512-OKgqk6wWE76wVc+Gnl0jfNu2hOnAQeYLM+PQjkdus1IVtv7D3V4IjAzwUcB7B2IT2ubqJhPEcGHUqk23uUfr3w==",
       "dependencies": {
         "@chakra-ui/react": "2.8.1",
         "comlink": "4.4.1",
@@ -10212,11 +10212,11 @@
       }
     },
     "node_modules/braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
       "dependencies": {
-        "fill-range": "^7.0.1"
+        "fill-range": "^7.1.1"
       },
       "engines": {
         "node": ">=8"
@@ -13041,9 +13041,9 @@
       }
     },
     "node_modules/fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
       "dependencies": {
         "to-regex-range": "^5.0.1"
       },
@@ -24256,9 +24256,9 @@
       }
     },
     "node_modules/ws": {
-      "version": "8.16.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
-      "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
+      "version": "8.18.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+      "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
       "engines": {
         "node": ">=10.0.0"
       },
diff --git a/package.json b/package.json
index b2161fb9..32d15646 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
     "@chakra-ui/react": "2.5.4",
     "@newrelic/next": "^0.7.0",
     "@nypl/design-system-react-components": "3.1.1",
-    "@nypl/web-reader": "4.3.4",
+    "@nypl/web-reader": "4.3.5",
     "@types/node": "^16.11.6",
     "css-loader": "^7.1.1",
     "dotenv": "^16.0.3",
diff --git a/src/components/EditionCard/ReadOnlineLink.tsx b/src/components/EditionCard/ReadOnlineLink.tsx
index 3445a4df..389f9a49 100644
--- a/src/components/EditionCard/ReadOnlineLink.tsx
+++ b/src/components/EditionCard/ReadOnlineLink.tsx
@@ -16,7 +16,7 @@ const ReadOnlineLink: React.FC<{
     pathname: `/read/${readOnlineLink.link_id}`,
   };
 
-  if (readOnlineLink.flags.nypl_login && !isLoggedIn) {
+  if (readOnlineLink.flags.fulfill_limited_access && !isLoggedIn) {
     linkText = "Log in to read online";
     linkUrl = LOGIN_LINK_BASE + encodeURIComponent(window.location.href);
   }
diff --git a/src/components/InstanceCard/InstanceCard.tsx b/src/components/InstanceCard/InstanceCard.tsx
index 44f5df04..2a7a52ba 100644
--- a/src/components/InstanceCard/InstanceCard.tsx
+++ b/src/components/InstanceCard/InstanceCard.tsx
@@ -43,6 +43,7 @@ export const InstanceCard: React.FC<{
       border="1px"
       borderColor="ui.border.default"
       padding="s"
+      paddingLeft={{ base: "l", md: null }}
       paddingBottom="l"
       paddingRight="l"
     >
@@ -61,13 +62,22 @@ export const InstanceCard: React.FC<{
         isAlignedRightActions
         id={`card-${instance.instance_id}`}
         paddingTop="m"
+        flexFlow={{ md: "column nowrap", lg: "row" }}
+        justifyContent={{ md: "center", lg: "left" }}
+        sx={{
+          ".card-right": {
+            maxWidth: { base: "100%", lg: "200px" },
+            marginStart: { base: "0", lg: "m" },
+            marginTop: { base: "xs", lg: 0 },
+          },
+        }}
       >
         <CardHeading
           level="h3"
           size="heading6"
           sx={{ span: { fontSize: "18px" } }}
         >
-          <Flex alignItems="center">
+          <Flex alignItems="center" gap="xs">
             <span>
               {edition.publication_date
                 ? edition.publication_date
@@ -92,6 +102,9 @@ export const InstanceCard: React.FC<{
           display="flex"
           flexDir="column"
           whiteSpace="nowrap"
+          sx={{
+            width: { base: "100%", lg: "200px" },
+          }}
           gap="xs"
         >
           <Ctas item={previewItem} title={instance.title} />
diff --git a/src/components/ReaderLayout/ReaderLayout.tsx b/src/components/ReaderLayout/ReaderLayout.tsx
index 94bfd22d..54e03e87 100644
--- a/src/components/ReaderLayout/ReaderLayout.tsx
+++ b/src/components/ReaderLayout/ReaderLayout.tsx
@@ -26,6 +26,9 @@ import readiumDefault from "!file-loader!extract-loader!css-loader!@nypl/web-rea
 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
 // @ts-ignore
 import readiumAfter from "!file-loader!extract-loader!css-loader!@nypl/web-reader/dist/injectable-html-styles/ReadiumCSS-after.css";
+import { NYPL_SESSION_ID } from "~/src/constants/auth";
+import { useCookies } from "react-cookie";
+import { useRouter } from "next/router";
 
 const origin =
   typeof window !== "undefined" && window.location?.origin
@@ -69,6 +72,10 @@ const ReaderLayout: React.FC<{
   const isRead = MediaTypes.read.includes(link.media_type);
   const isLimitedAccess = link.flags.fulfill_limited_access;
 
+  const [cookies] = useCookies([NYPL_SESSION_ID]);
+  const nyplIdentityCookie = cookies[NYPL_SESSION_ID];
+  const router = useRouter();
+
   const pdfWorkerSrc = `${origin}/pdf-worker/pdf.worker.min.js`;
 
   /**
@@ -114,7 +121,8 @@ const ReaderLayout: React.FC<{
         if (
           manifest &&
           manifest.readingOrder &&
-          manifest.readingOrder.length === 1
+          manifest.readingOrder.length === 1 &&
+          !isLimitedAccess
         ) {
           const modifiedManifest = await addTocToManifest(
             manifest,
@@ -181,7 +189,11 @@ const ReaderLayout: React.FC<{
           pdfWorkerSrc={pdfWorkerSrc}
           headerLeft={<BackButton />}
           injectablesFixed={injectables}
-          getContent={isLimitedAccess ? EditionCardUtils.fetchWithAuth : undefined}
+          getContent={
+            isLimitedAccess
+              ? EditionCardUtils.createGetContent(nyplIdentityCookie, router)
+              : undefined
+          }
         />
       )}
     </>
diff --git a/src/util/EditionCardUtils.tsx b/src/util/EditionCardUtils.tsx
index 952353ce..b8eb92a3 100644
--- a/src/util/EditionCardUtils.tsx
+++ b/src/util/EditionCardUtils.tsx
@@ -1,4 +1,3 @@
-import React from "react";
 import {
   Agent,
   Instance,
@@ -16,9 +15,8 @@ import {
   PLACEHOLDER_COVER_LINK,
 } from "../constants/editioncard";
 import { MediaTypes } from "../constants/mediaTypes";
-import { FulfillResult } from "../types/FulfillQuery";
-import { useCookies } from "react-cookie";
-import { NYPL_SESSION_ID } from "../constants/auth";
+import { NextRouter } from "next/router";
+import { LOGIN_LINK_BASE } from "../constants/links";
 
 // EditionCard holds all the methods needed to build an Edition Card
 export default class EditionCardUtils {
@@ -224,16 +222,38 @@ export default class EditionCardUtils {
     return universityPress !== undefined;
   }
 
-  static fetchWithAuth = async (linkUrl: string) => {
-    const [cookies] = useCookies([NYPL_SESSION_ID]);
-    const nyplIdentityCookie = cookies[NYPL_SESSION_ID];
-    const url = new URL(linkUrl);
-    const res = await fetch(url.toString(), {
-      method: "GET",
-      headers: {
-        Authorization: `Bearer ${nyplIdentityCookie.access_token}`,
-      },
-    });
-    return res.url;
+  static createGetContent = (nyplIdentityCookie: any, router: NextRouter) => {
+    const fetchWithAuth = async (fulfillUrl: string, proxyUrl?: string) => {
+      const url = new URL(fulfillUrl);
+      const res = await fetch(url.toString(), {
+        method: "GET",
+        headers: {
+          Authorization: `Bearer ${nyplIdentityCookie.access_token}`,
+        },
+      });
+
+      if (res.ok) {
+        // Generate the resource URL using the proxy
+        const resourceUrl = res.url;
+        const proxiedUrl: string = proxyUrl
+          ? `${proxyUrl}${encodeURIComponent(resourceUrl)}`
+          : resourceUrl;
+        const response = await fetch(proxiedUrl, { mode: "cors" });
+        const array = new Uint8Array(await response.arrayBuffer());
+
+        if (!response.ok) {
+          throw new Error("Response not Ok for URL: " + url);
+        }
+        return array;
+      } else {
+        // redirect to the NYPL login page if access token is invalid
+        if (res.status === 401) {
+          router.push(
+            LOGIN_LINK_BASE + encodeURIComponent(window.location.href)
+          );
+        }
+      }
+    };
+    return fetchWithAuth;
   };
 }

From b5f2173f8e5fc51771652ec24c458c52865cbd47 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Wed, 10 Jul 2024 16:10:58 -0400
Subject: [PATCH 08/29] update changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad3d50c8..9f2e5661 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 - Fix cut off text on search bar dropdown
 - Fix broken link on the About page
 - SFR-2008: Automate License Page Headers and Sub-Headers
+- Implement "Read Online" for UP items
 
 ## [0.18.1]
 

From 04eb9c90484b861cbbc32c22adac81126d3eec37 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Wed, 10 Jul 2024 16:54:04 -0400
Subject: [PATCH 09/29] update tests

---
 .../EditionCard/EditionCard.test.tsx          | 20 +++++++++++++-----
 src/components/EditionCard/ReadOnlineLink.tsx |  6 +++++-
 .../InstanceCard/InstanceCard.test.tsx        | 21 ++++++++++++++-----
 3 files changed, 36 insertions(+), 11 deletions(-)

diff --git a/src/components/EditionCard/EditionCard.test.tsx b/src/components/EditionCard/EditionCard.test.tsx
index b4c436f4..f1e5f6a1 100644
--- a/src/components/EditionCard/EditionCard.test.tsx
+++ b/src/components/EditionCard/EditionCard.test.tsx
@@ -8,6 +8,7 @@ import {
   upEdition,
 } from "~/src/__tests__/fixtures/EditionCardFixture";
 import { NYPL_SESSION_ID } from "~/src/constants/auth";
+import { Cookies, CookiesProvider } from "react-cookie";
 
 jest.mock("next/router", () => require("next-router-mock"));
 
@@ -124,8 +125,13 @@ describe("Edition with EDD", () => {
 
   test("Shows EDD Request button when user is logged in", () => {
     // Set cookie before rendering the component
-    document.cookie = `${NYPL_SESSION_ID}="randomvalue"`;
-    render(<EditionCard edition={eddEdition} title={"title"}></EditionCard>);
+    const cookies = new Cookies();
+    cookies.set(NYPL_SESSION_ID, "randomvalue");
+    render(
+      <CookiesProvider cookies={cookies}>
+        <EditionCard edition={eddEdition} title={"title"} />
+      </CookiesProvider>
+    );
 
     expect(
       screen.getByRole("link", { name: "Request scan for title" })
@@ -151,7 +157,6 @@ describe("Edition with EDD", () => {
 
 describe("Edition with UP", () => {
   test("Shows Login button when user is not logged in", () => {
-    document.cookie = `${NYPL_SESSION_ID}=""`;
     render(<EditionCard edition={upEdition} title={"title"}></EditionCard>);
     expect(
       screen.getByRole("link", { name: "title Log in to read online" })
@@ -171,8 +176,13 @@ describe("Edition with UP", () => {
   });
   test("Shows Read Online and Download buttons when user is logged in", () => {
     // Set cookie before rendering the component
-    document.cookie = `${NYPL_SESSION_ID}="randomvalue"`;
-    render(<EditionCard edition={upEdition} title={"title"}></EditionCard>);
+    const cookies = new Cookies();
+    cookies.set(NYPL_SESSION_ID, "randomvalue");
+    render(
+      <CookiesProvider cookies={cookies}>
+        <EditionCard edition={upEdition} title={"title"} />
+      </CookiesProvider>
+    );
 
     expect(
       screen.getByRole("link", { name: "title Read Online" })
diff --git a/src/components/EditionCard/ReadOnlineLink.tsx b/src/components/EditionCard/ReadOnlineLink.tsx
index 389f9a49..f9c3bcf5 100644
--- a/src/components/EditionCard/ReadOnlineLink.tsx
+++ b/src/components/EditionCard/ReadOnlineLink.tsx
@@ -16,7 +16,11 @@ const ReadOnlineLink: React.FC<{
     pathname: `/read/${readOnlineLink.link_id}`,
   };
 
-  if (readOnlineLink.flags.fulfill_limited_access && !isLoggedIn) {
+  if (
+    (readOnlineLink.flags.nypl_login ||
+      readOnlineLink.flags.fulfill_limited_access) &&
+    !isLoggedIn
+  ) {
     linkText = "Log in to read online";
     linkUrl = LOGIN_LINK_BASE + encodeURIComponent(window.location.href);
   }
diff --git a/src/components/InstanceCard/InstanceCard.test.tsx b/src/components/InstanceCard/InstanceCard.test.tsx
index 06b7ed83..28d8296f 100644
--- a/src/components/InstanceCard/InstanceCard.test.tsx
+++ b/src/components/InstanceCard/InstanceCard.test.tsx
@@ -13,6 +13,7 @@ import {
   eddInstance,
   upInstance,
 } from "~/src/__tests__/fixtures/InstanceCardFixture";
+import { Cookies, CookiesProvider } from "react-cookie";
 
 jest.mock("next/router", () => require("next-router-mock"));
 
@@ -114,9 +115,15 @@ describe("Instance with EDD", () => {
 
   test("Shows EDD Request button when user is logged in", () => {
     // Set cookie before rendering the component
-    document.cookie = `${NYPL_SESSION_ID}="randomvalue"`;
+    const cookies = new Cookies();
+    cookies.set(NYPL_SESSION_ID, "randomvalue");
     render(
-      <InstanceCard edition={fullEdition} instance={eddInstance}></InstanceCard>
+      <CookiesProvider cookies={cookies}>
+        <InstanceCard
+          edition={fullEdition}
+          instance={eddInstance}
+        ></InstanceCard>
+      </CookiesProvider>
     );
 
     expect(
@@ -143,7 +150,6 @@ describe("Instance with EDD", () => {
 
 describe("Instance with UP", () => {
   test("Shows Login button when user is not logged in", () => {
-    document.cookie = `${NYPL_SESSION_ID}=""`;
     render(<InstanceCard edition={upEdition} instance={upInstance} />);
     expect(
       screen.getByRole("link", { name: "title Log in to read online" })
@@ -163,8 +169,13 @@ describe("Instance with UP", () => {
   });
   test("Shows Read Online and Download buttons when user is logged in", () => {
     // Set cookie before rendering the component
-    document.cookie = `${NYPL_SESSION_ID}="randomvalue"`;
-    render(<InstanceCard edition={upEdition} instance={upInstance} />);
+    const cookies = new Cookies();
+    cookies.set(NYPL_SESSION_ID, "randomvalue");
+    render(
+      <CookiesProvider cookies={cookies}>
+        <InstanceCard edition={upEdition} instance={upInstance} />
+      </CookiesProvider>
+    );
 
     expect(
       screen.getByRole("link", { name: "title Read Online" })

From b83c668668e8e4ec2586a19c555212b5314dd01f Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 11 Jul 2024 10:20:09 -0400
Subject: [PATCH 10/29] fix linting errors

---
 src/components/ReaderLayout/ReaderLayout.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/ReaderLayout/ReaderLayout.tsx b/src/components/ReaderLayout/ReaderLayout.tsx
index 54e03e87..fb095972 100644
--- a/src/components/ReaderLayout/ReaderLayout.tsx
+++ b/src/components/ReaderLayout/ReaderLayout.tsx
@@ -143,7 +143,7 @@ const ReaderLayout: React.FC<{
       document.getElementById("nypl-header").style.display = "none";
       document.getElementById("nypl-footer").style.display = "none";
     }
-  }, [isRead, pdfWorkerSrc, proxyUrl, url]);
+  }, [isLimitedAccess, isRead, pdfWorkerSrc, proxyUrl, url]);
 
   const BackButton = () => {
     return (

From 3d5660cb0027d44aeb67c54b66fd2656e3694389 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 11 Jul 2024 10:26:57 -0400
Subject: [PATCH 11/29] update jira link on pr template

---
 .github/PULL_REQUEST_TEMPLATE | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE
index 5cdbad83..f0f7ddac 100644
--- a/.github/PULL_REQUEST_TEMPLATE
+++ b/.github/PULL_REQUEST_TEMPLATE
@@ -1,4 +1,4 @@
-[Jira Ticket](http://jira.nypl.org/browse/SFR-XXX)
+[Jira Ticket](https://newyorkpubliclibrary.atlassian.net/browse/SFR-XXX)
 
 ### This PR does the following:
 -

From d0804327997803057be24a5108f2cdf12e6f7b7d Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 11 Jul 2024 10:28:32 -0400
Subject: [PATCH 12/29] update changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad3d50c8..173e0532 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 - Fix cut off text on search bar dropdown
 - Fix broken link on the About page
 - SFR-2008: Automate License Page Headers and Sub-Headers
+- Update PR template with new Jira link
 
 ## [0.18.1]
 

From 3ae14e1fa43e41d133c35b9373e7c0a530151f35 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 11 Jul 2024 13:13:57 -0400
Subject: [PATCH 13/29] add error handling for invalid read data

---
 CHANGELOG.md                                 |  1 +
 src/components/ReaderLayout/ReaderLayout.tsx |  5 +++
 src/pages/404.tsx                            | 34 +++++++++++++-------
 3 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad3d50c8..b6d143f6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 - Fix cut off text on search bar dropdown
 - Fix broken link on the About page
 - SFR-2008: Automate License Page Headers and Sub-Headers
+- Add error page for /read links with invalid source
 
 ## [0.18.1]
 
diff --git a/src/components/ReaderLayout/ReaderLayout.tsx b/src/components/ReaderLayout/ReaderLayout.tsx
index cca41102..102484f0 100644
--- a/src/components/ReaderLayout/ReaderLayout.tsx
+++ b/src/components/ReaderLayout/ReaderLayout.tsx
@@ -26,6 +26,7 @@ import Link from "../Link/Link";
 import { addTocToManifest } from "@nypl/web-reader";
 import Loading from "../Loading/Loading";
 import { trackCtaClick } from "~/src/lib/adobe/Analytics";
+import NotFound404 from "~/src/pages/404";
 
 const origin =
   typeof window !== "undefined" && window.location?.origin
@@ -68,6 +69,10 @@ const ReaderLayout: React.FC<{
   const isEmbed = MediaTypes.embed.includes(link.media_type);
   const isRead = MediaTypes.read.includes(link.media_type);
 
+  if (!isEmbed && !isRead) {
+    return NotFound404();
+  }
+
   const pdfWorkerSrc = `${origin}/pdf-worker/pdf.worker.min.js`;
 
   /**
diff --git a/src/pages/404.tsx b/src/pages/404.tsx
index f0b50a82..366fa2f4 100644
--- a/src/pages/404.tsx
+++ b/src/pages/404.tsx
@@ -1,21 +1,31 @@
+import { TemplateAppContainer } from "@nypl/design-system-react-components";
 import React from "react";
+import DrbBreakout from "../components/DrbBreakout/DrbBreakout";
 import Layout from "../components/Layout/Layout";
 import Link from "../components/Link/Link";
 
-const NotFound404 = () => (
-  <Layout>
-    <h1>404 Not Found</h1>
+const NotFound404 = () => {
+  const contentPrimaryElement = (
+    <>
+      <h1>404 Not Found</h1>
 
-    <p>We&apos;re sorry...</p>
+      <p>We&apos;re sorry...</p>
 
-    <p>The page you were looking for doesn&apos;t exist.</p>
+      <p>The page you were looking for doesn&apos;t exist.</p>
 
-    <p>
-      Search&nbsp;
-      <Link to="/">Digital Research Books Beta</Link>
-      {"."}
-    </p>
-  </Layout>
-);
+      <p>
+        Search&nbsp;
+        <Link to="/">Digital Research Books Beta</Link>
+        {"."}
+      </p>
+    </>
+  );
+
+  return (
+    <Layout>
+      <TemplateAppContainer breakout={<DrbBreakout />} contentPrimary={contentPrimaryElement} />
+    </Layout>
+  );
+};
 
 export default NotFound404;

From 0f5a463e366ed3ed72b14109da3d29df8263c07e Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 11 Jul 2024 13:19:47 -0400
Subject: [PATCH 14/29] fix build issue

---
 src/components/ReaderLayout/ReaderLayout.tsx | 8 ++++----
 src/pages/404.tsx                            | 5 ++++-
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/components/ReaderLayout/ReaderLayout.tsx b/src/components/ReaderLayout/ReaderLayout.tsx
index 102484f0..65699184 100644
--- a/src/components/ReaderLayout/ReaderLayout.tsx
+++ b/src/components/ReaderLayout/ReaderLayout.tsx
@@ -69,10 +69,6 @@ const ReaderLayout: React.FC<{
   const isEmbed = MediaTypes.embed.includes(link.media_type);
   const isRead = MediaTypes.read.includes(link.media_type);
 
-  if (!isEmbed && !isRead) {
-    return NotFound404();
-  }
-
   const pdfWorkerSrc = `${origin}/pdf-worker/pdf.worker.min.js`;
 
   /**
@@ -153,6 +149,10 @@ const ReaderLayout: React.FC<{
     );
   };
 
+  if (!isEmbed && !isRead) {
+    return NotFound404();
+  }
+
   return (
     <>
       {isEmbed && (
diff --git a/src/pages/404.tsx b/src/pages/404.tsx
index 366fa2f4..55af9007 100644
--- a/src/pages/404.tsx
+++ b/src/pages/404.tsx
@@ -23,7 +23,10 @@ const NotFound404 = () => {
 
   return (
     <Layout>
-      <TemplateAppContainer breakout={<DrbBreakout />} contentPrimary={contentPrimaryElement} />
+      <TemplateAppContainer
+        breakout={<DrbBreakout />}
+        contentPrimary={contentPrimaryElement}
+      />
     </Layout>
   );
 };

From 1da12b28dfc0b979bd07c0862f9c6af9f3849106 Mon Sep 17 00:00:00 2001
From: Shejanul Ayan Islam <100955969+ayan1229@users.noreply.github.com>
Date: Fri, 12 Jul 2024 13:05:40 -0400
Subject: [PATCH 15/29] SFR-2033/verify-nypl-header-links (#508)

* SFR-2033/first-commit-for-review

* SFR-2033/first-commit
---
 CHANGELOG.md                            |  1 +
 playwright/features/headerLinks.feature | 57 +++++++++++++++----------
 playwright/support/mappings.ts          |  8 ++++
 3 files changed, 44 insertions(+), 22 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad3d50c8..5566a39d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 - Fix cut off text on search bar dropdown
 - Fix broken link on the About page
 - SFR-2008: Automate License Page Headers and Sub-Headers
+- SFR-2033: Verify the external NYPL header links of DRB App
 
 ## [0.18.1]
 
diff --git a/playwright/features/headerLinks.feature b/playwright/features/headerLinks.feature
index a9101ccc..38e216e9 100644
--- a/playwright/features/headerLinks.feature
+++ b/playwright/features/headerLinks.feature
@@ -18,17 +18,17 @@ Feature: Header Links
         And the "get help header link" should be displayed
         And the "search header link" should be displayed
 
-           Examples:
-            | DRB                 |
-            | "home"              |
-            | "advanced search"   |
-            | "search results"    |
-            | "item details"      |
-            | "edition details"   |
-            | "collection"        |
-            | "read online"       |
-            | "license"           |
-            | "about"             |  
+        Examples:
+            | DRB               |
+            | "home"            |
+            | "advanced search" |
+            | "search results"  |
+            | "item details"    |
+            | "edition details" |
+            | "collection"      |
+            | "read online"     |
+            | "license"         |
+            | "about"           |
 
     Scenario: As a user I navigate to the Digital Research Books home page and verify the account and search header sub-links and elements are displayed
         Given I go to the "home" page
@@ -36,14 +36,27 @@ Feature: Header Links
         Then the <second header link> should be displayed
 
         Examples:
-            | first header link         | second header link                       |
-            | "my account header link"  | "catalog header link"                    |
-            | "my account header link"  | "research catalog header link"           |
-            | "my account header link"  | "close my account header link"           |
-            | "search header link"      | "search header label"                    |
-            | "search header link"      | "search header text field"               |
-            | "search header link"      | "search books music movies radio button" |
-            | "search header link"      | "search research catalog radio button"   |
-            | "search header link"      | "search library website radio button"    |
-            | "search header link"      | "search header button"                   |
-            | "search header link"      | "close search header link"               |
+            | first header link        | second header link                       |
+            | "my account header link" | "catalog header link"                    |
+            | "my account header link" | "research catalog header link"           |
+            | "my account header link" | "close my account header link"           |
+            | "search header link"     | "search header label"                    |
+            | "search header link"     | "search header text field"               |
+            | "search header link"     | "search books music movies radio button" |
+            | "search header link"     | "search research catalog radio button"   |
+            | "search header link"     | "search library website radio button"    |
+            | "search header link"     | "search header button"                   |
+            | "search header link"     | "close search header link"               |
+
+    Scenario: As a user when I click on Locations I should be directed to NYPL location page
+        Given I go to the "home" page
+        Then I click the <first header link>
+        And the <landing page header> should be displayed
+
+        Examples:
+            | first header link           | landing page header              |
+            | "locations header link"     | "locations page header"          |
+            | "library card header link"  | "get a library card page header" |
+            | "email updates header link" | "get email updates page header"  |
+            | "donate header link"        | "donation page button"           |
+            | "shop header link"          | "shop page footer"               |
diff --git a/playwright/support/mappings.ts b/playwright/support/mappings.ts
index 210ca343..305aee54 100644
--- a/playwright/support/mappings.ts
+++ b/playwright/support/mappings.ts
@@ -215,6 +215,14 @@ export const elements = {
     "//p[contains(text(),'Works may be in the public domain in the Unites St')]",
   "public domain us only subheader":
     "//p[contains(text(),'Works may be in the public domain in the Unites States')]",
+
+  /** external page locators */
+  "locations page header":
+    "//div[contains(text(),'Welcome to The New York Public Library. Discover o')]",
+  "get a library card page header": "//h1[@id='hero-banner']",
+  "get email updates page header": "//h1[@id='page-title']",
+  "donation page button": "//button[text()='Donate']",
+  "shop page footer": "//h2[text()='All Proceeds Support the Library']",
 };
 
 export const inputs = {

From 306490ffd0f5cdf3b55de78d66b0590d4bdb9d4a Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 18 Jul 2024 14:59:58 -0400
Subject: [PATCH 16/29] update variable name

---
 src/util/EditionCardUtils.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/util/EditionCardUtils.tsx b/src/util/EditionCardUtils.tsx
index b8eb92a3..a8e7ae70 100644
--- a/src/util/EditionCardUtils.tsx
+++ b/src/util/EditionCardUtils.tsx
@@ -239,12 +239,12 @@ export default class EditionCardUtils {
           ? `${proxyUrl}${encodeURIComponent(resourceUrl)}`
           : resourceUrl;
         const response = await fetch(proxiedUrl, { mode: "cors" });
-        const array = new Uint8Array(await response.arrayBuffer());
+        const resourceAsByteArray = new Uint8Array(await response.arrayBuffer());
 
         if (!response.ok) {
           throw new Error("Response not Ok for URL: " + url);
         }
-        return array;
+        return resourceAsByteArray;
       } else {
         // redirect to the NYPL login page if access token is invalid
         if (res.status === 401) {

From 885f3cca6ebf9c87a6d394d17d1a6af6323dd23c Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 18 Jul 2024 15:01:23 -0400
Subject: [PATCH 17/29] fix linting error

---
 src/util/EditionCardUtils.tsx | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/util/EditionCardUtils.tsx b/src/util/EditionCardUtils.tsx
index a8e7ae70..3849fc38 100644
--- a/src/util/EditionCardUtils.tsx
+++ b/src/util/EditionCardUtils.tsx
@@ -239,7 +239,9 @@ export default class EditionCardUtils {
           ? `${proxyUrl}${encodeURIComponent(resourceUrl)}`
           : resourceUrl;
         const response = await fetch(proxiedUrl, { mode: "cors" });
-        const resourceAsByteArray = new Uint8Array(await response.arrayBuffer());
+        const resourceAsByteArray = new Uint8Array(
+          await response.arrayBuffer()
+        );
 
         if (!response.ok) {
           throw new Error("Response not Ok for URL: " + url);

From 6117af54eb6d661f305398a0a826946a6881e23b Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 25 Jul 2024 11:30:50 -0400
Subject: [PATCH 18/29] update proxyUrl for limited access books

---
 src/components/ReaderLayout/ReaderLayout.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/ReaderLayout/ReaderLayout.tsx b/src/components/ReaderLayout/ReaderLayout.tsx
index 95944943..c1a41d13 100644
--- a/src/components/ReaderLayout/ReaderLayout.tsx
+++ b/src/components/ReaderLayout/ReaderLayout.tsx
@@ -190,7 +190,7 @@ const ReaderLayout: React.FC<{
       {isRead && !isLoading && (
         <WebReader
           webpubManifestUrl={manifestUrl}
-          proxyUrl={proxyUrl}
+          proxyUrl={!isLimitedAccess ? proxyUrl : undefined}
           pdfWorkerSrc={pdfWorkerSrc}
           headerLeft={<BackButton />}
           injectablesFixed={injectables}

From cda5e2ac7281068623ded98f8592a828452939b3 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Mon, 29 Jul 2024 10:55:32 -0400
Subject: [PATCH 19/29] add env work links for about page

---
 config/appConfig.ts            | 5 +++++
 src/components/About/About.tsx | 6 +++++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/config/appConfig.ts b/config/appConfig.ts
index 6d0de008..1e4562e0 100644
--- a/config/appConfig.ts
+++ b/config/appConfig.ts
@@ -34,6 +34,11 @@ const appConfig = {
   displayCitations: {
     experimentName: "DisplayCitations",
   },
+  aboutPageWork: {
+    development: "/work/5950e6df-9d99-42fe-8924-1116166a2acb",
+    qa: "/work/5950e6df-9d99-42fe-8924-1116166a2acb",
+    production: "/work/8771d353-b75f-4f30-a424-e3b9516601f0"
+  }
 };
 
 export default appConfig;
diff --git a/src/components/About/About.tsx b/src/components/About/About.tsx
index f31904ee..61cd3fcc 100644
--- a/src/components/About/About.tsx
+++ b/src/components/About/About.tsx
@@ -5,8 +5,12 @@ import {
 } from "@nypl/design-system-react-components";
 import Link from "../Link/Link";
 import DrbBreakout from "../DrbBreakout/DrbBreakout";
+import appConfig from "~/config/appConfig";
 
 const About: React.FC = () => {
+  const apiEnv = process.env["APP_ENV"];
+  const workUrl = appConfig.aboutPageWork[apiEnv];
+
   const breakoutElement = (
     <DrbBreakout breadcrumbsData={[{ url: "/about", text: "About" }]} />
   );
@@ -43,7 +47,7 @@ const About: React.FC = () => {
         In addition to collecting these digital editions, we group all the
         editions of the same title together as a single “work.” For instance
         there are many editions of{" "}
-        <Link to="/work/8771d353-b75f-4f30-a424-e3b9516601f0">
+        <Link to={workUrl}>
           Mary Wollstonecraft’s A Vindication of the Rights of Woman
         </Link>
         , many of them available digitally. We group them all together under a

From 42095fcc8992baae3ee7bd2999a02c2df2f60ae0 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Mon, 29 Jul 2024 11:06:27 -0400
Subject: [PATCH 20/29] update test

---
 src/components/About/About.tsx                         | 2 +-
 src/components/About/__snapshots__/About.test.tsx.snap | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/components/About/About.tsx b/src/components/About/About.tsx
index 61cd3fcc..28596fc2 100644
--- a/src/components/About/About.tsx
+++ b/src/components/About/About.tsx
@@ -9,7 +9,7 @@ import appConfig from "~/config/appConfig";
 
 const About: React.FC = () => {
   const apiEnv = process.env["APP_ENV"];
-  const workUrl = appConfig.aboutPageWork[apiEnv];
+  const workUrl = appConfig.aboutPageWork[apiEnv] || "/work/5950e6df-9d99-42fe-8924-1116166a2acb";
 
   const breakoutElement = (
     <DrbBreakout breadcrumbsData={[{ url: "/about", text: "About" }]} />
diff --git a/src/components/About/__snapshots__/About.test.tsx.snap b/src/components/About/__snapshots__/About.test.tsx.snap
index 1c010c5e..a3429adc 100644
--- a/src/components/About/__snapshots__/About.test.tsx.snap
+++ b/src/components/About/__snapshots__/About.test.tsx.snap
@@ -161,7 +161,7 @@ exports[`renders about unchanged 1`] = `
          
         <a
           class="chakra-link css-qsaw8"
-          href="/work/8771d353-b75f-4f30-a424-e3b9516601f0"
+          href="/work/5950e6df-9d99-42fe-8924-1116166a2acb"
         >
           Mary Wollstonecraft’s A Vindication of the Rights of Woman
         </a>

From e5076c3a81803d20016067163ba1c362b6f7fdc3 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Mon, 29 Jul 2024 11:14:11 -0400
Subject: [PATCH 21/29] fix lint issue

---
 src/components/About/About.tsx | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/components/About/About.tsx b/src/components/About/About.tsx
index 28596fc2..ce8d51b8 100644
--- a/src/components/About/About.tsx
+++ b/src/components/About/About.tsx
@@ -9,7 +9,9 @@ import appConfig from "~/config/appConfig";
 
 const About: React.FC = () => {
   const apiEnv = process.env["APP_ENV"];
-  const workUrl = appConfig.aboutPageWork[apiEnv] || "/work/5950e6df-9d99-42fe-8924-1116166a2acb";
+  const workUrl =
+    appConfig.aboutPageWork[apiEnv] ||
+    "/work/5950e6df-9d99-42fe-8924-1116166a2acb";
 
   const breakoutElement = (
     <DrbBreakout breadcrumbsData={[{ url: "/about", text: "About" }]} />

From bcc2953819d8b9db9a2ea7ca294d75a0cae7100f Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Mon, 29 Jul 2024 11:22:10 -0400
Subject: [PATCH 22/29] fix another lint issue

---
 config/appConfig.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config/appConfig.ts b/config/appConfig.ts
index 1e4562e0..6a760ef6 100644
--- a/config/appConfig.ts
+++ b/config/appConfig.ts
@@ -37,7 +37,7 @@ const appConfig = {
   aboutPageWork: {
     development: "/work/5950e6df-9d99-42fe-8924-1116166a2acb",
     qa: "/work/5950e6df-9d99-42fe-8924-1116166a2acb",
-    production: "/work/8771d353-b75f-4f30-a424-e3b9516601f0"
+    production: "/work/8771d353-b75f-4f30-a424-e3b9516601f0",
   }
 };
 

From 99855c36a8fb1529a6871be3ae8156ce52103fe0 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Mon, 29 Jul 2024 12:27:27 -0400
Subject: [PATCH 23/29] fix lint issue

---
 config/appConfig.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config/appConfig.ts b/config/appConfig.ts
index 6a760ef6..4ba0a74a 100644
--- a/config/appConfig.ts
+++ b/config/appConfig.ts
@@ -38,7 +38,7 @@ const appConfig = {
     development: "/work/5950e6df-9d99-42fe-8924-1116166a2acb",
     qa: "/work/5950e6df-9d99-42fe-8924-1116166a2acb",
     production: "/work/8771d353-b75f-4f30-a424-e3b9516601f0",
-  }
+  },
 };
 
 export default appConfig;

From 8b15b26796e6b89e12ae7db2dc6b7aeab4643933 Mon Sep 17 00:00:00 2001
From: Kyle Villegas <86266231+kylevillegas93@users.noreply.github.com>
Date: Tue, 30 Jul 2024 15:40:34 -0400
Subject: [PATCH 24/29] SFR-2032: Update local API url (#511)

---
 CHANGELOG.md        | 2 ++
 config/appConfig.ts | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 39b601f5..77ff3ced 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@
 - Update PR template with new Jira link
 - Add error page for /read links with invalid source
 - Implement "Read Online" for UP items
+- SFR-2032: Update local API url
+
 
 ## [0.18.1]
 
diff --git a/config/appConfig.ts b/config/appConfig.ts
index 4ba0a74a..7dfdeb75 100644
--- a/config/appConfig.ts
+++ b/config/appConfig.ts
@@ -7,7 +7,7 @@ const appConfig = {
   baseUrl: "",
   api: {
     url: {
-      local: "localhost:5000",
+      local: "http://localhost:5050",
       development: "https://drb-api-qa.nypl.org",
       qa: "https://drb-api-qa.nypl.org",
       production: "http://drb-api-qa.nypl.org",

From 36970ae68e6165268a84dcc184ab455c17765dac Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Wed, 31 Jul 2024 15:44:21 -0400
Subject: [PATCH 25/29] update local dev section of readme

---
 .env.sample       | 12 +++++++++--
 CHANGELOG.md      |  2 +-
 README.md         | 55 ++++++++++++++++++++++++++++++++++-------------
 package-lock.json | 18 ----------------
 package.json      |  1 -
 5 files changed, 51 insertions(+), 37 deletions(-)

diff --git a/.env.sample b/.env.sample
index bcf8445f..e4c5c117 100644
--- a/.env.sample
+++ b/.env.sample
@@ -9,11 +9,19 @@ API_URL=http://drb-api-qa.nypl.org
 # https://github.com/NYPL-Simplified/web-reader#cors-proxy
 NEXT_PUBLIC_PROXY_URL=http://localhost:3001/?requestUrl=
 
+
+# OPTIONAL LOCAL VARIABLES
+
 # READER VERSION 
 NEXT_PUBLIC_READER_VERSION="v2"
 
 # Airtable API Key 
 # The airtable key is only needed in development if the developer needs to send data via the feedback form. 
-
 NEXT_PUBLIC_AIRTABLE_API_KEY=[insert key here]
-NEXT_PUBLIC_ADOBE_ANALYTICS=[insert AA url here]
\ No newline at end of file
+
+# ADOBE ANALYTICS
+NEXT_PUBLIC_ADOBE_ANALYTICS=[insert AA url here]
+
+# NEW RELIC
+NEW_RELIC_APP_NAME=[insert name here]
+NEW_RELIC_LICENSE_KEY=[insert key here]
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 77ff3ced..60b11040 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,7 +10,7 @@
 - Add error page for /read links with invalid source
 - Implement "Read Online" for UP items
 - SFR-2032: Update local API url
-
+- Update README with instructions to run app locally
 
 ## [0.18.1]
 
diff --git a/README.md b/README.md
index 3597b99a..3da38f8d 100644
--- a/README.md
+++ b/README.md
@@ -1,50 +1,75 @@
-## NYPL ResearchNow
-
-### ResearchNow Search & Retrieval Application
+# Digital Research Books Frontend
 
 [![GitHub version](https://badge.fury.io/gh/NYPL%2Fsfr-bookfinder-front-end.svg)](https://badge.fury.io/gh/NYPL%2Fsfr-bookfinder-front-end)
 
-Digital Research Books' front end application based on NYPL's React Design System.
+Digital Research Books' front end application based on NYPL's Reservoir Design System.
 
 Provides a "Welcome page" entry point with heading, search box, and tagline. Connects to an ElasticSearch index via an API endpoint (https://digital-research-books-api.nypl.org).
 Simple searches can be entered in the form and an index response is displayed back to the user.
 
-### Requirements
+## Requirements
 
 This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
 
-### Contributing: Local Development
+## Contributing: Local Development
+
+### Getting Started
+
+#### Prerequisites
 
-Clone the repo and run `npm install`.
+- Node.js (v18 or later)
+- To view pdfs locally through the webreader, you will need to set up a local proxy. If you used environment variables from `.env.sample` you should be able to pull the [web-reader](https://github.com/NYPL-Simplified/web-reader) repo, install it, and run `npm run cors-proxy`. See the web-reader repo for more [instructions](https://github.com/NYPL-Simplified/web-reader#cors-proxy)
 
-Create a `.env` file and add the `APP_ENV` and `API_URL`. See `.env.sample` for an example.
+1. Install the required packages
 
-Run `npm run dev` to start the local server at `localhost:3000`
+```
+npm install
+```
+
+2. Create a `.env` file and add required environment variables. See `.env.sample` for an example.
+
+### Running the app locally with npm at `localhost:3000`
+
+```
+npm run dev
+```
 
-To view pdfs locally through the webreader, you will need to set up a local proxy.  If you used environment variables from `.env.sample` you should be able to pull the [web-reader](https://github.com/NYPL-Simplified/web-reader) repo, install it, and run `npm run cors-proxy`.  See the web-reader repo for more [instructions](https://github.com/NYPL-Simplified/web-reader#cors-proxy)
+### Local Hosting
 
 In order to successfully login under a local deployment, you will need to update your machine's `etc/hosts` file. This hosts file maps local host names to ip addresses.
 
-Add this to your `etc/hosts` file:
+This is necessary because NYPL's authentication system works by reading cookies and parsing the patron's encrypted credentials. These cookies only work on .nypl.org domains, so we need to map our local deployment to a .nypl.org domain.
+
+Add this line to your `etc/hosts` file:
 
 ```
 	127.0.0.1       local.nypl.org
 ```
 
+### Running the app locally with Docker
+
+1. Download and install Docker.
+2. `cd` into `sfr-bookfinder-front-end` repo
+3. To build and run the application, run:
+
+```
+docker-compose up
+```
+
+4. Check that app is being served at http://localhost:3000
+
 ### Deploying to Production
 
-1. Create a new branch off `development` with the name `NO-REF_prepare-<VERSION>`, i.e. `NO-REF_prepare-1.2.3`. Replace any reference to "Pre-release" and the previous production version in the package.json and package-lock.json files with the current version number. Push the branch and create a Pull Request with the name `NOREF prepare <VERSION> release`, i.e. `NOREF prepare 1.2.3 release`. Merge these changes once the PR is approved. 
+1. Create a new branch off `development` with the name `NO-REF_prepare-<VERSION>`, i.e. `NO-REF_prepare-1.2.3`. Replace any reference to "Pre-release" and the previous production version in the package.json and package-lock.json files with the current version number. Push the branch and create a Pull Request with the name `NOREF prepare <VERSION> release`, i.e. `NOREF prepare 1.2.3 release`. Merge these changes once the PR is approved.
 2. Create a Pull Request to `production` from `development` with the name `Release <VERSION> to production`, i.e. `Release 1.2.3 to production`. The description of the PR should be the new version's changelog to be used in the tag and release step of the .yaml file.
 3. Merge the PR after approval to trigger the [`build-production.yaml`](./.github/workflows/build-production.yaml) GitHub Action, which pushes a Docker image to ECR, updates the `production` ECS deployment, and creates a new release version on GitHub.
 4. Add the link to the PR to the release ticket in Jira and tag the appropriate member of QA, the project manager, and the product manager. Move the ticket to "In QA" and assign it to the appropriate member of QA. QA should be done on https://drb-qa.nypl.org/.
 
-
 ### Dependencies
 
 - NextJS
-- Redux
-- NYPL Header and Footer npm modules
 - NYPL Design System
+- NYPL Web Reader
 
 ### Usage
 
diff --git a/package-lock.json b/package-lock.json
index 5c2fa50f..5a4b3253 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,7 +23,6 @@
         "next-transpile-modules": "^7.0.0",
         "react": "^18.2.0",
         "react-cookie": "7.1.4",
-        "redux": "4.0.1",
         "sass": "^1.62.1",
         "typescript": "^4.1.3"
       },
@@ -20913,15 +20912,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/redux": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz",
-      "integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==",
-      "dependencies": {
-        "loose-envify": "^1.4.0",
-        "symbol-observable": "^1.2.0"
-      }
-    },
     "node_modules/reflect-metadata": {
       "version": "0.1.13",
       "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
@@ -22647,14 +22637,6 @@
         "react": "^16.11.0 || ^17.0.0 || ^18.0.0"
       }
     },
-    "node_modules/symbol-observable": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
-      "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/symbol-tree": {
       "version": "3.2.4",
       "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
diff --git a/package.json b/package.json
index 32d15646..7535cd5e 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,6 @@
     "next-transpile-modules": "^7.0.0",
     "react": "^18.2.0",
     "react-cookie": "7.1.4",
-    "redux": "4.0.1",
     "sass": "^1.62.1",
     "typescript": "^4.1.3"
   },

From f48976d91c9c9fee951fced2c6635db999ae6aac Mon Sep 17 00:00:00 2001
From: Shejanul Ayan Islam <100955969+ayan1229@users.noreply.github.com>
Date: Wed, 31 Jul 2024 16:27:39 -0400
Subject: [PATCH 26/29] SFR-2076/pw_test_fix (#512)

---
 CHANGELOG.md                   | 2 +-
 playwright/support/mappings.ts | 3 +--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 77ff3ced..ab7ab3ad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,7 +10,7 @@
 - Add error page for /read links with invalid source
 - Implement "Read Online" for UP items
 - SFR-2032: Update local API url
-
+- SFR-2076: Fix DRB PW Regression Tests
 
 ## [0.18.1]
 
diff --git a/playwright/support/mappings.ts b/playwright/support/mappings.ts
index 305aee54..7862c647 100644
--- a/playwright/support/mappings.ts
+++ b/playwright/support/mappings.ts
@@ -189,8 +189,7 @@ export const elements = {
 
   /** footer locators */
   "accessibility footer link": "//a[@href='http://www.nypl.org/accessibility']",
-  "press footer link":
-    "//a[@href='http://www.nypl.org/help/about-nypl/media-center']",
+  "press footer link": "//a[@href='http://www.nypl.org/press']",
   "careers footer link": "//a[@href='http://www.nypl.org/careers']",
   "space rental footer link": "//a[@href='http://www.nypl.org/spacerental']",
   "privacy policy footer link":

From 8a39ae3e96dcf604bf4cfe4f04de8b41c5bbb020 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 1 Aug 2024 15:05:43 -0400
Subject: [PATCH 27/29] address comments and update heading levels

---
 .env.sample |  2 +-
 README.md   | 26 +++++++++++++-------------
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/.env.sample b/.env.sample
index e4c5c117..b94074f2 100644
--- a/.env.sample
+++ b/.env.sample
@@ -24,4 +24,4 @@ NEXT_PUBLIC_ADOBE_ANALYTICS=[insert AA url here]
 
 # NEW RELIC
 NEW_RELIC_APP_NAME=[insert name here]
-NEW_RELIC_LICENSE_KEY=[insert key here]
\ No newline at end of file
+NEW_RELIC_LICENSE_KEY=[insert key here]
diff --git a/README.md b/README.md
index 3da38f8d..6e10fefc 100644
--- a/README.md
+++ b/README.md
@@ -7,17 +7,17 @@ Digital Research Books' front end application based on NYPL's Reservoir Design S
 Provides a "Welcome page" entry point with heading, search box, and tagline. Connects to an ElasticSearch index via an API endpoint (https://digital-research-books-api.nypl.org).
 Simple searches can be entered in the form and an index response is displayed back to the user.
 
-## Requirements
+### Requirements
 
 This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
 
-## Contributing: Local Development
+### Contributing: Local Development
 
-### Getting Started
+#### Getting Started
 
-#### Prerequisites
+##### Prerequisites
 
-- Node.js (v18 or later)
+- Install Node.js v18 or later
 - To view pdfs locally through the webreader, you will need to set up a local proxy. If you used environment variables from `.env.sample` you should be able to pull the [web-reader](https://github.com/NYPL-Simplified/web-reader) repo, install it, and run `npm run cors-proxy`. See the web-reader repo for more [instructions](https://github.com/NYPL-Simplified/web-reader#cors-proxy)
 
 1. Install the required packages
@@ -28,13 +28,13 @@ npm install
 
 2. Create a `.env` file and add required environment variables. See `.env.sample` for an example.
 
-### Running the app locally with npm at `localhost:3000`
+#### Running the app locally with npm at `localhost:3000`
 
 ```
 npm run dev
 ```
 
-### Local Hosting
+#### Local Hosting
 
 In order to successfully login under a local deployment, you will need to update your machine's `etc/hosts` file. This hosts file maps local host names to ip addresses.
 
@@ -46,7 +46,7 @@ Add this line to your `etc/hosts` file:
 	127.0.0.1       local.nypl.org
 ```
 
-### Running the app locally with Docker
+#### Running the app locally with Docker
 
 1. Download and install Docker.
 2. `cd` into `sfr-bookfinder-front-end` repo
@@ -73,7 +73,7 @@ docker-compose up
 
 ### Usage
 
-### Searchbar
+#### Searchbar
 
 Currently takes in a query string to pass along to the ResearchNow Search API which submits a keyword search to the Elasticsearch instance, and renders the returned output. Sends the `query` parameter specified in the rendered search form on the main page.
 
@@ -88,23 +88,23 @@ Term combinations
 
 These types of combinations can be used with any available field selection.
 
-### Filtering
+#### Filtering
 
 Search Results can be filtered by year range, language and available format.
 
-### Advanced Search
+#### Advanced Search
 
 Advanced Search works like the Simple Search, but allows searching for multiple fields and for pre-filtering. Terms use the AND boolean operator
 Term combinations
 
 - Example: Subject:"English Literature" AND Keyword:"Witches"
 
-### Works and Editions
+#### Works and Editions
 
 - Each source record is represented as an Item (something that can actually be read online), which are grouped into Editions (e.g. the 1917 edition of X), which are in turn grouped into Works, (e.g. Moby Dick, or, The Whale). Through this a user can search for and find a single Work record and see all editions of that Work and all of its options for reading online.
 - The information and code for this normalization is found in the [drb-etl-pipeline repo](https://github.com/NYPL/drb-etl-pipeline)
 
-### Accessing books
+#### Accessing books
 
 - Books can be read three ways:
   - Embedded page: DRB embeds a read-online page from a different source. DRB commonly does this for Hathitrust books

From 35f57dbf4fe60f4a1c52176dd6f1035a1e06e472 Mon Sep 17 00:00:00 2001
From: Jackie Quach <jackiequach14@gmail.com>
Date: Thu, 1 Aug 2024 15:25:09 -0400
Subject: [PATCH 28/29] update autotag version

---
 .github/workflows/build-production.yaml | 4 ++--
 CHANGELOG.md                            | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/build-production.yaml b/.github/workflows/build-production.yaml
index adb41340..f6e1a522 100644
--- a/.github/workflows/build-production.yaml
+++ b/.github/workflows/build-production.yaml
@@ -18,8 +18,8 @@ jobs:
         # the tag will match the package.json version (eg. v1.0.0)
       - name: Tag
         id: autotagger
-        uses: butlerlogic/action-autotag@stable
-        env: 
+        uses: butlerlogic/action-autotag@1.1.2
+        env:
           GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
         with:
           strategy: package
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47c2af5a..6a28411d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@
 - SFR-2032: Update local API url
 - SFR-2076: Fix DRB PW Regression Tests
 - Update README with instructions to run app locally
+- Update `butlerlogic/action-autotag` version to be pinned since the stable version is using an unsupported Node version
 
 ## [0.18.1]
 

From 7877174aa88db2658ca197585da64612dad56c70 Mon Sep 17 00:00:00 2001
From: Kyle Villegas <86266231+kylevillegas93@users.noreply.github.com>
Date: Tue, 6 Aug 2024 09:38:42 -0400
Subject: [PATCH 29/29] NOREF prepare 0.18.2 release (#515)

---
 CHANGELOG.md      | 2 +-
 package-lock.json | 2 +-
 package.json      | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6a28411d..a1991a5f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
 # CHANGE LOG
 
-## [Pre-release]
+## [0.18.2]
 
 - Fix cut off text on search bar dropdown
 - Fix broken link on the About page
diff --git a/package-lock.json b/package-lock.json
index 5a4b3253..d080adaf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "sfr-bookfinder-front-end",
-  "version": "0.18.1",
+  "version": "0.18.2",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
diff --git a/package.json b/package.json
index 7535cd5e..00d36bfc 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "sfr-bookfinder-front-end",
-  "version": "0.18.1",
+  "version": "0.18.2",
   "private": true,
   "scripts": {
     "dev": "next dev",