Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

My Account 2.0 #407

Merged
merged 92 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
88f7bc2
Multiple emails, semi working with js
7emansell Oct 3, 2024
ebdc5a2
Cleaning up form and form validation
7emansell Oct 4, 2024
1f1565e
Unit tests for email form
7emansell Oct 4, 2024
06e1343
With no js form validation
7emansell Oct 7, 2024
54e668b
Clean up components
7emansell Oct 8, 2024
36dd68b
Add the logout modal back in
7emansell Oct 8, 2024
441110f
Cleaning up again for readability
7emansell Oct 8, 2024
3ec3663
Styling adjustment
7emansell Oct 8, 2024
c86f605
Removing no js
7emansell Oct 10, 2024
5cf94f9
Missed one
7emansell Oct 10, 2024
dea4307
Updating loading and pulling out components
7emansell Oct 15, 2024
4fe76ad
Import fixes
7emansell Oct 15, 2024
a1105fe
Typo
7emansell Oct 15, 2024
7577670
New version
7emansell Oct 15, 2024
08fda6d
Adding failure banner
7emansell Oct 15, 2024
4641d64
Fixing test
7emansell Oct 15, 2024
83bbf82
Splitting out form label component
7emansell Oct 15, 2024
828da62
Edwin updates
7emansell Oct 16, 2024
6622f50
Wrapping in form component
7emansell Oct 16, 2024
0b3c0bf
Uniqueness....
7emansell Oct 16, 2024
09acc0c
Updating label
7emansell Oct 16, 2024
b27bcbb
Correcting tests
7emansell Oct 16, 2024
56b793f
Merge pull request #358 from NYPL/SCC-4253/individual-edit-email
7emansell Oct 21, 2024
f7c9231
Pulling out form component and adding phone logic
7emansell Oct 22, 2024
0d4f6aa
Phone form test
7emansell Oct 22, 2024
b99c329
Fixing nested p tag
7emansell Oct 22, 2024
2271fa7
Clean up
7emansell Oct 29, 2024
116ef9b
inline checks moved into formUtils
7emansell Oct 31, 2024
dbf4598
Recommenting for clarity
7emansell Oct 31, 2024
ac68ff8
Home library and notification form component, plus tests
7emansell Nov 1, 2024
420f93f
Styling and readme corrections
7emansell Nov 1, 2024
d099abd
Renaming form components
7emansell Nov 4, 2024
3a94d2f
Password form, lacking styling
7emansell Nov 4, 2024
f754bc8
With settingsState as a chunk
7emansell Nov 5, 2024
81499c4
Styling fixes
7emansell Nov 5, 2024
4f91b7e
Test fixed
7emansell Nov 5, 2024
74d2fdc
Edit button and add button extracted
7emansell Nov 5, 2024
fbdfdc1
Merge branch 'SCC-4254/individual-edit-phone' into SCC-4337/individua…
7emansell Nov 5, 2024
9aca068
Merge pull request #373 from NYPL/SCC-4254/individual-edit-phone
7emansell Nov 5, 2024
9eb3fa5
Adding notification preference conditions
7emansell Nov 5, 2024
4dd9ee5
Final phone number decisions
7emansell Nov 5, 2024
e58f2bd
Midway through rewriting tests
7emansell Nov 6, 2024
d875400
More refinining tests
7emansell Nov 6, 2024
8217a7a
Reorganizing helpers
7emansell Nov 6, 2024
56a402d
Actually fixing readme
7emansell Nov 6, 2024
7d29c39
Removing personal #
7emansell Nov 7, 2024
3022c3e
Refactoring settingState and password form field
7emansell Nov 7, 2024
99abee7
Updating banner styling
7emansell Nov 7, 2024
d988d52
Rewriting tests
7emansell Nov 7, 2024
ba18616
Updating status to take error message, handling none as notif pref pl…
7emansell Nov 8, 2024
5063b22
Sending focus to banner on submission
7emansell Nov 8, 2024
2f78ef9
Splitting status and statusMessage state
7emansell Nov 13, 2024
3ce348f
correcting typo
7emansell Nov 13, 2024
111217c
More typos
7emansell Nov 13, 2024
5f35ebf
Edwin updates (rearranging variables, typing state, style fixes)
7emansell Nov 14, 2024
34ff7dc
More styling and renaming
7emansell Nov 14, 2024
7d19d0d
banner content out of block
7emansell Nov 14, 2024
edadac1
Merge pull request #392 from NYPL/SCC-4337/individual-edit-other-fields
7emansell Nov 14, 2024
766b558
Start of username
7emansell Nov 15, 2024
7e67c6e
Pulling out status banner
7emansell Nov 19, 2024
47219eb
Start of username validation api route
7emansell Nov 19, 2024
cd54635
Removing discovery links from config and updating nyplApiClient
7emansell Nov 19, 2024
f116cf8
Username form and cleanup in other files
7emansell Nov 19, 2024
a6f7684
Tests (need more once deletion is confirmed)
7emansell Nov 20, 2024
e52407e
Clean up
7emansell Nov 20, 2024
673d296
One more test
7emansell Nov 20, 2024
bc995e2
Typooo
7emansell Nov 21, 2024
f1a87f8
Moving UsernameForm into list
7emansell Nov 22, 2024
ee979fc
Typo/import clean up
7emansell Nov 22, 2024
28e698d
Simplifying ternaries
7emansell Nov 22, 2024
303cf48
Removing ternaries and fixing alignment
7emansell Nov 25, 2024
d240835
Removing DISCOVERY_API_NAME
7emansell Nov 25, 2024
8ec5985
Removing more ternary
7emansell Nov 25, 2024
4a5e036
Styling updates
7emansell Nov 25, 2024
d78b943
Tweaking error handling on helper
7emansell Nov 25, 2024
013d510
Changing deletion strategy
7emansell Nov 25, 2024
b17b5a1
Adding deletion test and defining tempInput null meaning
7emansell Nov 25, 2024
6570b9f
Commenting
7emansell Nov 25, 2024
0bcf57d
More clean up
7emansell Nov 25, 2024
bc4f37e
Variable name updates
7emansell Nov 25, 2024
63aa14e
More styling tweaks
7emansell Nov 25, 2024
f1e65a6
styling
7emansell Nov 25, 2024
2863e93
Starting to fix helper error text
7emansell Nov 26, 2024
5e720be
using ui.link.primary where possible
7emansell Nov 26, 2024
3b317c3
more changes
7emansell Nov 26, 2024
e7bf689
Tests corrected
7emansell Nov 26, 2024
865b452
Merge pull request #397 from NYPL/SCC-4236/edit-username
7emansell Nov 26, 2024
48cd2ee
Removing old settings and updating changelog
7emansell Nov 26, 2024
e8d4eec
Correcting changelog
7emansell Nov 26, 2024
e51bd0b
Merge branch 'my-account-2.0'
7emansell Nov 26, 2024
b846751
Vera fixes
7emansell Nov 26, 2024
245d6e3
Adding tests
7emansell Nov 27, 2024
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
7 changes: 7 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Updated

- Updated phone, email, notification preference and home library to be individually editable in Account Settings (SCC-4337, SCC-4254, SCC-4253)
- Updated username to be editable in My Account header (SCC-4236)

## [1.3.6] 2024-11-6

## Added
Expand Down
16 changes: 0 additions & 16 deletions __test__/pages/account/account.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,22 +231,6 @@ describe("MyAccount page", () => {
const result = await getServerSideProps({ req: req, res: mockRes })
expect(result.props.tabsPath).toBe("overdues")
})
it("can handle no username", () => {
render(
<MyAccount
isAuthenticated={true}
accountData={{
checkouts: processedCheckouts,
holds: processedHolds,
patron: { ...processedPatron, username: undefined },
fines: processedFines,
pickupLocations: filteredPickupLocations,
}}
/>
)
const username = screen.queryByText("Username")
expect(username).toBeNull()
})
it("renders notification banner if user has fines", () => {
render(
<MyAccount
Expand Down
3 changes: 2 additions & 1 deletion accountREADME.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ Route parameter is the patron ID. Request body can include any fields on the pat

exampleBody: {
emails: ['[email protected]'],
phones: [6466600432]
phones: [12345678],
homeLibraryCode: 'sn'
},

```
Expand Down
9 changes: 6 additions & 3 deletions pages/account/[[...index]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export default function MyAccount({
assistance.
</Text>
)

useEffect(() => {
resetCountdown()
// to avoid a reference error on document in the modal, wait to render it
Expand Down Expand Up @@ -111,16 +112,17 @@ export async function getServerSideProps({ req, res }) {
},
}
}
// Parsing path from url to pass to ProfileTabs.

// Parsing path from URL
const tabsPathRegex = /\/account\/(.+)/
const match = req.url.match(tabsPathRegex)
const tabsPath = match ? match[1] : null
const id = patronTokenResponse.decodedPatron.sub

try {
const { checkouts, holds, patron, fines, pickupLocations } =
await getPatronData(id)
/* Redirecting invalid paths (including /overdues if user has none) and
// cleaning extra parts off valid paths. */
// Redirecting invalid paths and cleaning extra parts off valid paths.
if (tabsPath) {
const allowedPaths = ["items", "requests", "overdues", "settings"]
if (
Expand All @@ -147,6 +149,7 @@ export async function getServerSideProps({ req, res }) {
}
}
}

return {
props: {
accountData: { checkouts, holds, patron, fines, pickupLocations },
Expand Down
47 changes: 47 additions & 0 deletions pages/api/account/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import sierraClient from "../../../src/server/sierraClient"
import type { HTTPResponse } from "../../../src/types/appTypes"
import nyplApiClient from "../../../src/server/nyplApiClient"

/**
* PUT request to Sierra to update patron PIN, first validating with previous PIN.
Expand Down Expand Up @@ -27,6 +28,52 @@ export async function updatePin(
}
}

/**
* PUT request to Sierra to update patron username, first validating that it's available.
* Returns status and message about request.
*/
export async function updateUsername(
patronId: string,
newUsername: string
): Promise<HTTPResponse> {
try {
// If the new username is an empty string, skips validation and directly updates in Sierra.
const client = await sierraClient()
if (newUsername === "") {
const client = await sierraClient()
await client.put(`patrons/${patronId}`, {
varFields: [{ fieldTag: "u", content: newUsername }],
})
return { status: 200, message: "Username removed successfully" }
} else {
const platformClient = await nyplApiClient({ version: "v0.3" })
const response = await platformClient.post("/validations/username", {
username: newUsername,
})

if (response?.type === "available-username") {
await client.put(`patrons/${patronId}`, {
varFields: [{ fieldTag: "u", content: newUsername }],
})
return { status: 200, message: `Username updated to ${newUsername}` }
} else if (response?.type === "unavailable-username") {
// Username taken but not an error, returns a message.
return { status: 200, message: "Username taken" }
} else {
throw new Error("Username update failed")
}
}
} catch (error) {
return {
status: error?.status || 500,
message:
error?.message ||
error.response?.data?.description ||
"An error occurred",
}
}
}

/**
* PUT request to Sierra to update patron settings. Returns status and message about request.
*/
Expand Down
1 change: 1 addition & 0 deletions pages/api/account/settings/[id].ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default async function handler(
if (req.method == "GET") {
responseMessage = "Please make a PUT request to this endpoint."
}

if (req.method == "PUT") {
/** We get the patron id from the request: */
const patronId = req.query.id as string
Expand Down
40 changes: 40 additions & 0 deletions pages/api/account/username/[id].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { NextApiResponse, NextApiRequest } from "next"
import initializePatronTokenAuth from "../../../../src/server/auth"
import { updateUsername } from "../helpers"

/**
* API route handler for /api/account/username/{patronId}
*/
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
let responseMessage = "Request error"
let responseStatus = 400
const patronTokenResponse = await initializePatronTokenAuth(req.cookies)
const cookiePatronId = patronTokenResponse.decodedPatron?.sub
if (!cookiePatronId) {
responseStatus = 403
responseMessage = "No authenticated patron"
return res.status(responseStatus).json(responseMessage)
7emansell marked this conversation as resolved.
Show resolved Hide resolved
}
if (req.method == "GET") {
responseMessage = "Please make a PUT request to this endpoint."
}
if (req.method == "PUT") {
/** We get the patron id from the request: */
const patronId = req.query.id as string
const { username } = req.body
/** We check that the patron cookie matches the patron id in the request,
* i.e.,the logged in user is updating their own username. */
if (patronId == cookiePatronId) {
const response = await updateUsername(patronId, username)
responseStatus = response.status
responseMessage = response.message
} else {
responseStatus = 403
responseMessage = "Authenticated patron does not match request"
}
}
res.status(responseStatus).json(responseMessage)
}
2 changes: 1 addition & 1 deletion src/components/MyAccount/IconListElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface IconListElementPropType {

// This component is designed to centralize common styling patterns for a
// description type List with icons
const IconListElement = ({
export const IconListElement = ({
icon,
term,
description,
Expand Down
62 changes: 48 additions & 14 deletions src/components/MyAccount/ProfileHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Banner,
Box,
List,
useNYPLBreakpoints,
Expand All @@ -10,17 +11,37 @@ import styles from "../../../styles/components/MyAccount.module.scss"
import type { Patron } from "../../types/myAccountTypes"
import type { IconListElementPropType } from "./IconListElement"
import { buildListElementsWithIcons } from "./IconListElement"
import UsernameForm from "./Settings/UsernameForm"
import { useEffect, useRef, useState } from "react"
import type { StatusType } from "./Settings/StatusBanner"
import { StatusBanner } from "./Settings/StatusBanner"

const ProfileHeader = ({ patron }: { patron: Patron }) => {
const { isLargerThanMobile } = useNYPLBreakpoints()
const [usernameStatus, setUsernameStatus] = useState<StatusType>("")
const [usernameStatusMessage, setUsernameStatusMessage] = useState<string>("")
const usernameBannerRef = useRef<HTMLDivElement>(null)

useEffect(() => {
if (usernameStatus !== "" && usernameBannerRef.current) {
usernameBannerRef.current.focus()
}
}, [usernameStatus])

const usernameState = {
setUsernameStatus,
setUsernameStatusMessage,
}

const profileData = (
[
{ icon: "actionIdentityFilled", term: "Name", description: patron.name },
{
icon: "actionIdentity",
term: "Username",
description: patron.username,
description: (
<UsernameForm patron={patron} usernameState={usernameState} />
),
},
{
icon: "actionPayment",
Expand Down Expand Up @@ -53,19 +74,32 @@ const ProfileHeader = ({ patron }: { patron: Patron }) => {
.map(buildListElementsWithIcons)

return (
<List
className={styles.myAccountList}
id="my-account-profile-header"
title="My Account"
type="dl"
sx={{
border: "none",
h2: { border: "none", paddingTop: 0 },
marginBottom: "xxl",
}}
>
{profileData}
</List>
<>
{usernameStatus !== "" && (
<div
ref={usernameBannerRef}
tabIndex={-1}
style={{ marginBottom: "32px" }}
>
<StatusBanner
status={usernameStatus}
statusMessage={usernameStatusMessage}
/>
</div>
)}
<List
className={styles.myAccountList}
id="my-account-profile-header"
title="My Account"
type="dl"
sx={{
border: "none",
h2: { border: "none", paddingTop: 0 },
}}
>
{profileData}
</List>
</>
)
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/MyAccount/ProfileTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Tabs, Text } from "@nypl/design-system-react-components"
import { useRouter } from "next/router"

import AccountSettingsTab from "./Settings/AccountSettingsTab"
import CheckoutsTab from "./CheckoutsTab/CheckoutsTab"
import RequestsTab from "./RequestsTab/RequestsTab"
import FeesTab from "./FeesTab/FeesTab"
import { PatronDataContext } from "../../context/PatronDataContext"
import { useContext } from "react"
import NewAccountSettingsTab from "./Settings/NewAccountSettingsTab"

interface ProfileTabsPropsType {
activePath: string
Expand Down Expand Up @@ -49,7 +49,7 @@ const ProfileTabs = ({ activePath }: ProfileTabsPropsType) => {
: []),
{
label: "Account settings",
content: <AccountSettingsTab />,
content: <NewAccountSettingsTab />,
urlPath: "settings",
},
]
Expand Down
51 changes: 0 additions & 51 deletions src/components/MyAccount/Settings/AccountSettingsButtons.tsx

This file was deleted.

Loading