Files
web/apps/scandic-web/components/Forms/Edit/Profile/index.tsx
Chuma Mcphoy (We Ahead) 494bfe10f3 Merged in feat/profile-consent-feature-branch (pull request #2900)
feat(LOY-268): Feature branch for profiling consent work

* feat: Add feature branch for profile and consent work

* Merged in feat/LOY-268-profile-consent-banner-comp (pull request #2908)

Feat/LOY-358 profile consent banner component

* feat: Add feature branch for profile and consent work

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component


Approved-by: Chuma Mcphoy (We Ahead)

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component

* Merged in feat/profile-consent-contentstack (pull request #2921)

Feat(LOY-389): Profile consent in Contentstack

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component

* chore(LOY-348): add profiling consent as CS entry

* chore(LOY-348): add banner as dynamic content

Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-347-Profile-consent-modal-phase-1 (pull request #2901)

Feat(LOY-347): Profiling Consent Modal (phase 1)

* feat(LOY-347): Profile Consent Modal base functionality

* feat(LOY-347): Add Icon

* feat(LOY-347): Add Benefit Cards to Profile consent modal

* feat(LOY-347): Add accordion to profile consent modal

* fix(LOY-347): scroll behaviour

* Fix(LOY-347): fade in/out animations of profile consent modal

* fix(LOY-347): White Ellipsis Around Icons

* feat(LOY-347): Implement ability to open modal from banner

* fix(LOY-347): minor fixes

* fix(LOY-347): replace old spa icon

* fix(LOY-347): re-add env vars

* fix(LOY-347): File renaming and cleanup

* chore(LOY-347): Update readme

* fix(LOY-347): use correct space var

* fix(LOY-347): Add TODO comment for adding link to accordion

Approved-by: Matilda Landström

* Merged in fix/LOY-386-profiling-consent-modal-contentstack (pull request #2930)

Fix(LOY-386): profiling consent modal contentstack

* feat(LOY-347): Profile Consent Modal base functionality

* feat(LOY-347): Add Icon

* feat(LOY-347): Add Benefit Cards to Profile consent modal

* feat(LOY-347): Add accordion to profile consent modal

* fix(LOY-347): scroll behaviour

* Fix(LOY-347): fade in/out animations of profile consent modal

* fix(LOY-347): White Ellipsis Around Icons

* feat(LOY-347): Implement ability to open modal from banner

* fix(LOY-347): minor fixes

* fix(LOY-347): replace old spa icon

* fix(LOY-347): re-add env vars

* fix(LOY-347): File renaming and cleanup

* fix(LOY-386): Use contentstack content for profile consent modal

* fix(LOY-386): beneift cards schema transform

* chore(LOY-386): remove usememo

* fix(LOY-386): fix modalcontent check

* fix(LOY-386): remove uneeded vars


Approved-by: Matilda Landström

* Merged in feat/LOY-412-profiling-consent-in-signup (pull request #2976)

Feat(LOY-412): profiling consent in signup

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component

* Merged in feat/profile-consent-contentstack (pull request #2921)

Feat(LOY-389): Profile consent in Contentstack

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component

* chore(LOY-348): add profiling consent as CS entry

* chore(LOY-348): add banner as dynamic content

Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-347-Profile-consent-modal-phase-1 (pull request #2901)

Feat(LOY-347): Profiling Consent Modal (phase 1)

* feat(LOY-347): Profile Consent Modal base functionality

* feat(LOY-347): Add Icon

* feat(LOY-347): Add Benefit Cards to Profile consent modal

* feat(LOY-347): Add accordion to profile consent modal

* fix(LOY-347): scroll behaviour

* Fix(LOY-347): fade in/out animations of profile consent modal

* fix(LOY-347): White Ellipsis Around Ico…
* Merged in fix/lokalise-ids (pull request #3013)

fix: add ids to translations in Profiling Consent

* fix: add ids to translations


Approved-by: Erik Tiekstra
Approved-by: Chuma Mcphoy (We Ahead)

* Merged in LOY-436-my-pages-profiling-consent (pull request #3011)

LOY-436: Profiling Consent on My Profile, no api

Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-418-profiling-consent-ui-text-update (pull request #3080)

Feat/LOY-418: Profiling consent ui and text update

* chore(LOY-418): update /consent buttons

* chore(LOY-418): update legal texts


Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-268-profiling-consent-api (pull request #3088)

Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-413-Signup-API-Profiling-Consent (pull request #3105)

Feat/LOY-413 Signup API Profiling Consent

* feat(LOY-413): signup profiling consent

* chore(LOY-413): remove todo

* fix(LOY-413): only pass in profilingConsent if true

* fix(LOY-413): proper spread of profilingConsent in signup input


Approved-by: Christel Westerberg

* Merged in fix/LOY-413-use-v2-for-signup-call (pull request #3112)

fix(LOY-413): use v2 endpoint for profile POST in signup

* fix(LOY-413): use v2 endpoint for profile POST in signup


Approved-by: Erik Tiekstra

* Merged in feat/LOY-268-profiling-consent-improvements (pull request #3094)

Feat/LOY-268: Profiling consent improvements

* Merged in feat/profile-consent-contentstack (pull request #2921)

Feat(LOY-389): Profile consent in Contentstack

* feat(LOY-268): create banner

* feat(LOY-268): Create personalization banner component

* chore(LOY-348): add profiling consent as CS entry

* chore(LOY-348): add banner as dynamic content

Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-347-Profile-consent-modal-phase-1 (pull request #2901)

Feat(LOY-347): Profiling Consent Modal (phase 1)

* feat(LOY-347): Profile Consent Modal base functionality

* feat(LOY-347): Add Icon

* feat(LOY-347): Add Benefit Cards to Profile consent modal

* feat(LOY-347): Add accordion to profile consent modal

* fix(LOY-347): scroll behaviour

* Fix(LOY-347): fade in/out animations of profile consent modal

* fix(LOY-347): White Ellipsis Around Icons

* feat(LOY-347): Implement ability to open modal from banner

* fix(LOY-347): min…
* Merged in fix/update-graphql (pull request #3130)

fix: update graphql

* fix: update graphql


Approved-by: Chuma Mcphoy (We Ahead)

* Merged in feat/LOY-414-prof-consent-tracking (pull request #3127)

Feat/LOY-414 profile consent tracking + credit card ui update

* chore(LOY-414): create track link function

* chore(LOY-414): add cta tracking

* chore(LOY-414): add profileConsent to userInfo datalayer

* chore(LOY-414): update credit card ui

* chore(LOY-414): update tracking specs

* chore(LOY-414): add pageView tracking to modal

Approved-by: Chuma Mcphoy (We Ahead)

* fix: remove old flag

* Merged in fix/LOY-268-prof-consent-button-fix (pull request #3162)

fix(LOY-268): add button as link

* fix(LOY-268): add button as link


Approved-by: Chuma Mcphoy (We Ahead)


Approved-by: Matilda Landström
2025-11-14 13:28:56 +00:00

200 lines
6.5 KiB
TypeScript

"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useParams, useRouter } from "next/navigation"
import { useEffect, useState } from "react"
import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"
import { logout } from "@scandic-hotels/common/constants/routes/handleAuth"
import { profile } from "@scandic-hotels/common/constants/routes/myPages"
import { usePhoneNumberParsing } from "@scandic-hotels/common/hooks/usePhoneNumberParsing"
import { logger } from "@scandic-hotels/common/logger"
import {
formatPhoneNumber,
getDefaultCountryFromLang,
} from "@scandic-hotels/common/utils/phone"
import { Button } from "@scandic-hotels/design-system/Button"
import { toast } from "@scandic-hotels/design-system/Toast"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { trpc } from "@scandic-hotels/trpc/client"
import { langToApiLang } from "@scandic-hotels/trpc/constants/apiLang"
import { editProfile } from "@/actions/editProfile"
import Dialog from "@/components/Dialog"
import ChangeNameDisclaimer from "@/components/MyPages/Profile/ChangeNameDisclaimer"
import FormContent from "./FormContent"
import { type EditProfileSchema, editProfileSchema } from "./schema"
import styles from "./form.module.css"
import type { Lang } from "@scandic-hotels/common/constants/language"
import {
type EditFormProps,
Status,
} from "@/types/components/myPages/myProfile/edit"
const formId = "edit-profile"
export default function Form({ user }: EditFormProps) {
const intl = useIntl()
const router = useRouter()
const params = useParams()
const lang = params.lang as Lang
const utils = trpc.useUtils()
/**
* RHF isValid defaults to false and never
* changes when JS is disabled, therefore
* we need to keep it insync ourselves
*/
const [isValid, setIsValid] = useState(true)
const { phoneNumber, phoneNumberCC } = usePhoneNumberParsing(user.phoneNumber)
const methods = useForm<EditProfileSchema>({
defaultValues: {
address: {
city: user.address?.city ?? "",
countryCode: user.address?.countryCode ?? "",
streetAddress: user.address?.streetAddress ?? "",
zipCode: user.address?.zipCode ?? "",
},
dateOfBirth: user.dateOfBirth,
email: user.email,
language: user.language ?? langToApiLang[lang],
phoneNumber: phoneNumber,
phoneNumberCC: phoneNumber
? phoneNumberCC
: getDefaultCountryFromLang(lang),
password: "",
newPassword: "",
retypeNewPassword: "",
},
mode: "all",
criteriaMode: "all",
resolver: zodResolver(editProfileSchema),
reValidateMode: "onChange",
})
const trigger = methods.trigger
async function handleSubmit(data: EditProfileSchema) {
const isPasswordChanged = !!data.newPassword
const phoneNumber = formatPhoneNumber(data.phoneNumber, data.phoneNumberCC)
const response = await editProfile({ ...data, phoneNumber })
switch (response.status) {
case Status.error:
if (response.issues?.length) {
response.issues.forEach((issue) => {
logger.error("Profile edit error:", issue)
})
}
toast.error(response.message)
break
case Status.success:
toast.success(response.message)
utils.user.get.invalidate()
if (isPasswordChanged) {
// Kept logout out of Next router forcing browser to navigate on logout url
window.location.href = logout[lang]
} else {
const myStayReturnRoute = sessionStorage.getItem("myStayReturnRoute")
if (myStayReturnRoute) {
const returnRoute = JSON.parse(myStayReturnRoute)
sessionStorage.removeItem("myStayReturnRoute")
router.push(returnRoute.path)
} else {
router.push(profile[lang])
}
router.refresh() // Can be removed on NextJs 15
}
break
}
}
useEffect(() => {
setIsValid(methods.formState.isValid)
}, [setIsValid, methods.formState.isValid])
useEffect(() => {
trigger()
}, [trigger])
return (
<>
<section className={styles.container}>
<div className={styles.title}>
<Typography variant="Title/sm">
<span>
<h1 className={styles.welcome}>
{intl.formatMessage({
id: "common.welcome",
defaultMessage: "Welcome",
})}
</h1>
<h2 data-hj-suppress className={styles.name}>
{user.name}
</h2>
</span>
</Typography>
</div>
<div className={styles.btnContainer}>
<Dialog
bodyText={intl.formatMessage({
id: "editProfile.discardChangesDescription",
defaultMessage: "Any changes you've made will be lost.",
})}
cancelButtonText={intl.formatMessage({
id: "editProfile.goBackToEdit",
defaultMessage: "Go back to edit",
})}
proceedHref={profile[lang]}
proceedText={intl.formatMessage({
id: "editProfile.yesDiscardChanges",
defaultMessage: "Yes, discard changes",
})}
titleText={intl.formatMessage({
id: "editProfile.discardChangesTitle",
defaultMessage: "Discard unsaved changes?",
})}
trigger={
<Button variant="Secondary" size="Small" color="Primary">
{intl.formatMessage({
id: "editProfile.discardChanges",
defaultMessage: "Discard changes",
})}
</Button>
}
/>
<Button
isDisabled={!isValid || methods.formState.isSubmitting}
form={formId}
variant="Primary"
size="Small"
color="Primary"
type="submit"
>
{intl.formatMessage({
id: "common.save",
defaultMessage: "Save",
})}
</Button>
</div>
<form
/* @ts-expect-error Ignoring since ts doesn't recognize that tRPC parses FormData before reaching the route */
action={editProfile}
className={styles.form}
id={formId}
onSubmit={methods.handleSubmit(handleSubmit)}
>
<FormProvider {...methods}>
<FormContent errors={methods.formState.errors} />
</FormProvider>
</form>
</section>
<ChangeNameDisclaimer />
</>
)
}