diff --git a/CHANGELOG b/CHANGELOG index 190688108..9dcb62be6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added patron ineligibility error messaging to HoldRequestErrorBanner component (SCC-3762) - Added eligibility checks to EDD and on-site request hold pages and API routes (SCC-3762) - Added HoldContactButton component and update error copy per VQA requests (SCC-4427) +- Added email address field prepopulation on EDD Request form (SCC-4407) ### Updated diff --git a/__test__/pages/hold/eddRequestPage.test.tsx b/__test__/pages/hold/eddRequestPage.test.tsx index 160e4c251..37ae9b719 100644 --- a/__test__/pages/hold/eddRequestPage.test.tsx +++ b/__test__/pages/hold/eddRequestPage.test.tsx @@ -25,6 +25,7 @@ jest.mock("../../../src/server/auth") jest.mock("../../../src/server/api/bib") jest.mock("../../../src/server/sierraClient") jest.mock("../../../src/server/api/hold") +jest.mock("../../../src/models/MyAccount") jest.mock("next/router", () => jest.requireActual("next-router-mock")) @@ -203,6 +204,22 @@ describe("EDD Request page", () => { expect(screen.getByTestId("edd-request-form")).toBeInTheDocument() }) }) + describe("EDD Request prepopulated form fields", () => { + beforeEach(() => { + render( + + ) + }) + it("prepopulates the email field with the patron's email address if present", () => { + expect(screen.getByDisplayValue("test@test.com")).toBeInTheDocument() + }) + }) describe("EDD Request form validation", () => { beforeEach(async () => { render( diff --git a/pages/hold/request/[id]/edd.tsx b/pages/hold/request/[id]/edd.tsx index 547c9dbdb..9a99cc545 100644 --- a/pages/hold/request/[id]/edd.tsx +++ b/pages/hold/request/[id]/edd.tsx @@ -7,6 +7,8 @@ import { SkeletonLoader, } from "@nypl/design-system-react-components" +import sierraClient from "../../../../src/server/sierraClient" + import Layout from "../../../../src/components/Layout/Layout" import EDDRequestForm from "../../../../src/components/HoldPages/EDDRequestForm" import HoldRequestErrorBanner from "../../../../src/components/HoldPages/HoldRequestErrorBanner" @@ -16,6 +18,8 @@ import { SITE_NAME, BASE_URL, PATHS } from "../../../../src/config/constants" import useLoading from "../../../../src/hooks/useLoading" import { fetchBib } from "../../../../src/server/api/bib" +import MyAccount from "../../../../src/models/MyAccount" + import { fetchDeliveryLocations, fetchPatronEligibility, @@ -43,6 +47,7 @@ interface EDDRequestPropsType { discoveryBibResult: DiscoveryBibResult discoveryItemResult: DiscoveryItemResult patronId: string + patronEmail?: string isAuthenticated?: boolean errorStatus?: HoldErrorStatus patronEligibilityStatus?: PatronEligibilityStatus @@ -55,12 +60,12 @@ export default function EDDRequestPage({ discoveryBibResult, discoveryItemResult, patronId, + patronEmail, isAuthenticated, errorStatus: defaultErrorStatus, patronEligibilityStatus: defaultEligibilityStatus, }: EDDRequestPropsType) { const metadataTitle = `Electronic Delivery Request | ${SITE_NAME}` - const bib = new Bib(discoveryBibResult) const item = new Item(discoveryItemResult, bib) @@ -71,10 +76,11 @@ export default function EDDRequestPage({ defaultEligibilityStatus ) - const [eddFormState, setEddFormState] = useState({ + const [eddFormState, setEddFormState] = useState({ ...initialEDDFormState, + emailAddress: patronEmail, patronId, - source: item.source, + source: item.formattedSourceForHoldRequest, }) const [formPosting, setFormPosting] = useState(false) @@ -251,6 +257,12 @@ export async function getServerSideProps({ params, req, res }) { const patronEligibilityStatus = await fetchPatronEligibility(patronId) + // fetch patron's email to pre-populate the edd form if available + const client = await sierraClient() + const patronAccount = new MyAccount(client, patronId) + const patron = await patronAccount.getPatron() + const patronEmail = patron?.emails?.[0] + const locationOrEligibilityFetchFailed = locationStatus !== 200 || ![200, 401].includes(patronEligibilityStatus?.status) @@ -260,6 +272,7 @@ export async function getServerSideProps({ params, req, res }) { discoveryBibResult, discoveryItemResult, patronId, + patronEmail, isAuthenticated, patronEligibilityStatus, errorStatus: locationOrEligibilityFetchFailed diff --git a/pages/hold/request/[id]/index.tsx b/pages/hold/request/[id]/index.tsx index e0b0577df..e6c223859 100644 --- a/pages/hold/request/[id]/index.tsx +++ b/pages/hold/request/[id]/index.tsx @@ -180,8 +180,8 @@ export default function HoldRequestPage({ handleSubmit={handleSubmit} holdId={holdId} patronId={patronId} - source={item.source} errorStatus={errorStatus} + source={item.formattedSourceForHoldRequest} /> ) : null} diff --git a/src/models/Item.ts b/src/models/Item.ts index d3c8c8de7..ae4e81a88 100644 --- a/src/models/Item.ts +++ b/src/models/Item.ts @@ -12,6 +12,7 @@ import { locationEndpointsMap, } from "../utils/itemUtils" import { appConfig } from "../config/config" +import { convertCamelToShishKabobCase } from "../utils/appUtils" /** * The Item class contains the data and getter functions @@ -81,6 +82,10 @@ export default class Item { .includes("all") } + get formattedSourceForHoldRequest(): string { + return convertCamelToShishKabobCase(this.source) + } + // Pre-processing logic for setting Item holding location getLocationFromItem(item: DiscoveryItemResult): ItemLocation { let location = defaultNYPLLocation diff --git a/src/models/modelTests/Item.test.ts b/src/models/modelTests/Item.test.ts index db080df42..d07e92901 100644 --- a/src/models/modelTests/Item.test.ts +++ b/src/models/modelTests/Item.test.ts @@ -104,6 +104,10 @@ describe("Item model", () => { "A history of spaghetti eating and cooking for: spaghetti dinner." ) }) + + it("returns the source in kebabcase for use in hold requests", () => { + expect(item.formattedSourceForHoldRequest).toBe("sierra-nypl") + }) }) describe("ReCAP checks", () => { diff --git a/src/utils/appUtils.ts b/src/utils/appUtils.ts index 67a1f8dbb..41a4c9e28 100644 --- a/src/utils/appUtils.ts +++ b/src/utils/appUtils.ts @@ -140,3 +140,19 @@ export const convertToSentenceCase = (str: string) => str.split(" ").length > 1 ? str.charAt(0).toUpperCase() + str.slice(1).toLowerCase() : str + +/** + * Converts camel case string to shish kabob case + * + * e.g. camelToShishKabobCase("RecapPul") + * => "recap-pul" + * camelToShishKabobCase("firstCharCanBeLowerCase") + * => "first-char-can-be-lower-case" + */ +export const convertCamelToShishKabobCase = (str: string) => + str + // Change capital letters into "-{lowercase letter}" + .replace(/([A-Z])/g, (capitalLetter, placeholderVar, index) => { + // If capital letter is not first character, precede with '-': + return (index > 0 ? "-" : "") + capitalLetter.toLowerCase() + })