diff --git a/.env.local.example b/.env.local.example index 6d842230e..16e56afa3 100644 --- a/.env.local.example +++ b/.env.local.example @@ -19,6 +19,8 @@ DESIGN_SYSTEM_ACCESS_TOKEN="" NEXTAUTH_REDIRECT_PROXY_URL="http://localhost:3000/api/web/auth" NEXTAUTH_SECRET="" REVALIDATE_SECRET="" +SALESFORCE_PREFRENCE_BASE_URL="https://mc31njyvc80x-2b9wg-24jxcj5yq.pub.sfmc-content.com/disfelgm4fv" + SEAMLESS_LOGIN_DA="http://www.example.dk/updatelogin" SEAMLESS_LOGIN_DE="http://www.example.de/updatelogin" SEAMLESS_LOGIN_EN="http://www.example.com/updatelogin" diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@communication/page.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@communication/page.tsx index fb15f24cc..1cc5c648f 100644 --- a/app/[lang]/(live)/(protected)/my-pages/profile/@communication/page.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/profile/@communication/page.tsx @@ -1,4 +1,5 @@ import { ArrowRightIcon } from "@/components/Icons" +import ManagePreferencesButton from "@/components/Profile/ManagePreferencesButton" import Link from "@/components/TempDesignSystem/Link" import Body from "@/components/TempDesignSystem/Text/Body" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" @@ -27,12 +28,7 @@ export default async function CommunicationSlot({ })} - - - - {formatMessage({ id: "Manage preferences" })} - - + ) } diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx index a37b0047b..4dbfc1ab4 100644 --- a/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx @@ -1,4 +1,3 @@ -import { env } from "@/env/server" import { serverClient } from "@/lib/trpc/server" import AddCreditCardButton from "@/components/Profile/AddCreditCardButton" @@ -17,8 +16,6 @@ export default async function CreditCardSlot({ params }: PageArgs) { const { formatMessage } = await getIntl() const creditCards = await serverClient().user.creditCards() - const { lang } = params - return (
diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/layout.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/layout.tsx index cdf46a35d..73496fe4e 100644 --- a/app/[lang]/(live)/(protected)/my-pages/profile/layout.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/profile/layout.tsx @@ -15,8 +15,7 @@ export default function ProfileLayout({ {profile} {creditCards} - {/* TODO: Implement communication preferences flow. Hidden until decided on where to send user. */} - {/* {communication} */} + {communication}
) diff --git a/components/Profile/ManagePreferencesButton/index.tsx b/components/Profile/ManagePreferencesButton/index.tsx new file mode 100644 index 000000000..8741888d0 --- /dev/null +++ b/components/Profile/ManagePreferencesButton/index.tsx @@ -0,0 +1,51 @@ +"use client" + +import { useIntl } from "react-intl" + +import { trpc } from "@/lib/trpc/client" + +import ArrowRight from "@/components/Icons/ArrowRight" +import Button from "@/components/TempDesignSystem/Button" +import { toast } from "@/components/TempDesignSystem/Toasts" + +import styles from "./managePreferencesButton.module.css" + +export default function ManagePreferencesButton() { + const intl = useIntl() + const generatePreferencesLink = trpc.user.generatePreferencesLink.useMutation( + { + onSuccess: (preferencesLink) => { + if (preferencesLink) { + window.open(preferencesLink, "_blank") + } else { + toast.error( + intl.formatMessage({ + id: "It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.", + }) + ) + } + }, + onError: (e) => { + toast.error( + intl.formatMessage({ + id: "An error occurred trying to manage your preferences, please try again later.", + }) + ) + }, + } + ) + + return ( + + ) +} diff --git a/components/Profile/ManagePreferencesButton/managePreferencesButton.module.css b/components/Profile/ManagePreferencesButton/managePreferencesButton.module.css new file mode 100644 index 000000000..f786a669f --- /dev/null +++ b/components/Profile/ManagePreferencesButton/managePreferencesButton.module.css @@ -0,0 +1,3 @@ +.managePreferencesButton { + justify-self: flex-start; +} diff --git a/env/server.ts b/env/server.ts index 751143e62..623c02e3a 100644 --- a/env/server.ts +++ b/env/server.ts @@ -47,6 +47,7 @@ export const env = createEnv({ .default("false"), PUBLIC_URL: z.string().optional(), REVALIDATE_SECRET: z.string(), + SALESFORCE_PREFRENCE_BASE_URL: z.string(), SEAMLESS_LOGIN_DA: z.string(), SEAMLESS_LOGIN_DE: z.string(), SEAMLESS_LOGIN_EN: z.string(), @@ -104,6 +105,7 @@ export const env = createEnv({ PRINT_QUERY: process.env.PRINT_QUERY, PUBLIC_URL: process.env.PUBLIC_URL, REVALIDATE_SECRET: process.env.REVALIDATE_SECRET, + SALESFORCE_PREFRENCE_BASE_URL: process.env.SALESFORCE_PREFRENCE_BASE_URL, SEAMLESS_LOGIN_DA: process.env.SEAMLESS_LOGIN_DA, SEAMLESS_LOGIN_DE: process.env.SEAMLESS_LOGIN_DE, SEAMLESS_LOGIN_EN: process.env.SEAMLESS_LOGIN_EN, diff --git a/i18n/dictionaries/da.json b/i18n/dictionaries/da.json index 03dc00da5..77e15f396 100644 --- a/i18n/dictionaries/da.json +++ b/i18n/dictionaries/da.json @@ -10,6 +10,7 @@ "Amenities": "Faciliteter", "Amusement park": "Forlystelsespark", "An error occurred when adding a credit card, please try again later.": "Der opstod en fejl under tilføjelse af et kreditkort. Prøv venligst igen senere.", + "An error occurred trying to manage your preferences, please try again later.": "Der opstod en fejl under forsøget på at administrere dine præferencer. Prøv venligst igen senere.", "An error occurred when trying to update profile.": "Der opstod en fejl under forsøg på at opdatere profilen.", "Any changes you've made will be lost.": "Alle ændringer, du har foretaget, går tabt.", "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på, at du vil fjerne kortet, der slutter me {lastFourDigits} fra din medlemsprofil?", @@ -99,6 +100,7 @@ "How do you want to sleep?": "Hvordan vil du sove?", "How it works": "Hvordan det virker", "Image gallery": "Billedgalleri", + "It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Det er ikke muligt at administrere dine kommunikationspræferencer lige nu, prøv venligst igen senere eller kontakt support, hvis problemet fortsætter.", "Join Scandic Friends": "Tilmeld dig Scandic Friends", "Join at no cost": "Tilmeld dig uden omkostninger", "Language": "Sprog", diff --git a/i18n/dictionaries/de.json b/i18n/dictionaries/de.json index 5f79f7d9d..99dbc5dc0 100644 --- a/i18n/dictionaries/de.json +++ b/i18n/dictionaries/de.json @@ -10,6 +10,7 @@ "Amenities": "Annehmlichkeiten", "Amusement park": "Vergnügungspark", "An error occurred when adding a credit card, please try again later.": "Beim Hinzufügen einer Kreditkarte ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.", + "An error occurred trying to manage your preferences, please try again later.": "Beim Versuch, Ihre Einstellungen zu verwalten, ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.", "An error occurred when trying to update profile.": "Beim Versuch, das Profil zu aktualisieren, ist ein Fehler aufgetreten.", "Any changes you've made will be lost.": "Alle Änderungen, die Sie vorgenommen haben, gehen verloren.", "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Möchten Sie die Karte mit der Endung {lastFourDigits} wirklich aus Ihrem Mitgliedsprofil entfernen?", @@ -104,6 +105,7 @@ "How do you want to sleep?": "Wie möchtest du schlafen?", "How it works": "Wie es funktioniert", "Image gallery": "Bildergalerie", + "It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Es ist derzeit nicht möglich, Ihre Kommunikationseinstellungen zu verwalten. Bitte versuchen Sie es später erneut oder wenden Sie sich an den Support, wenn das Problem weiterhin besteht.", "Join Scandic Friends": "Treten Sie Scandic Friends bei", "km to city center": "km bis zum Stadtzentrum", "Language": "Sprache", diff --git a/i18n/dictionaries/en.json b/i18n/dictionaries/en.json index fe95c44b6..64ff97c27 100644 --- a/i18n/dictionaries/en.json +++ b/i18n/dictionaries/en.json @@ -10,6 +10,7 @@ "Amenities": "Amenities", "Amusement park": "Amusement park", "An error occurred when adding a credit card, please try again later.": "An error occurred when adding a credit card, please try again later.", + "An error occurred trying to manage your preferences, please try again later.": "An error occurred trying to manage your preferences, please try again later.", "An error occurred when trying to update profile.": "An error occurred when trying to update profile.", "Any changes you've made will be lost.": "Any changes you've made will be lost.", "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?", @@ -103,6 +104,7 @@ "How do you want to sleep?": "How do you want to sleep?", "How it works": "How it works", "Image gallery": "Image gallery", + "It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.", "Join Scandic Friends": "Join Scandic Friends", "Join at no cost": "Join at no cost", "Language": "Language", diff --git a/i18n/dictionaries/fi.json b/i18n/dictionaries/fi.json index 5485d1871..870acb18e 100644 --- a/i18n/dictionaries/fi.json +++ b/i18n/dictionaries/fi.json @@ -10,6 +10,7 @@ "Amenities": "Mukavuudet", "Amusement park": "Huvipuisto", "An error occurred when adding a credit card, please try again later.": "Luottokorttia lisättäessä tapahtui virhe. Yritä myöhemmin uudelleen.", + "An error occurred trying to manage your preferences, please try again later.": "Asetusten hallinnassa tapahtui virhe. Yritä myöhemmin uudelleen.", "An error occurred when trying to update profile.": "Profiilia päivitettäessä tapahtui virhe.", "Any changes you've made will be lost.": "Kaikki tekemäsi muutokset menetetään.", "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Haluatko varmasti poistaa kortin, joka päättyy numeroon {lastFourDigits} jäsenprofiilistasi?", @@ -99,6 +100,7 @@ "How do you want to sleep?": "Kuinka haluat nukkua?", "How it works": "Kuinka se toimii", "Image gallery": "Kuvagalleria", + "It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Viestintäasetuksiasi ei voi hallita juuri nyt. Yritä myöhemmin uudelleen tai ota yhteyttä tukeen, jos ongelma jatkuu.", "Join Scandic Friends": "Liity jäseneksi", "Join at no cost": "Liity maksutta", "Language": "Kieli", diff --git a/i18n/dictionaries/no.json b/i18n/dictionaries/no.json index 395143f40..65fa174b5 100644 --- a/i18n/dictionaries/no.json +++ b/i18n/dictionaries/no.json @@ -10,6 +10,7 @@ "Amenities": "Fasiliteter", "Amusement park": "Tivoli", "An error occurred when adding a credit card, please try again later.": "Det oppstod en feil ved å legge til et kredittkort. Prøv igjen senere.", + "An error occurred trying to manage your preferences, please try again later.": "Det oppstod en feil under forsøket på å administrere innstillingene dine. Prøv igjen senere.", "An error occurred when trying to update profile.": "Det oppstod en feil under forsøk på å oppdatere profilen.", "Any changes you've made will be lost.": "Eventuelle endringer du har gjort, går tapt.", "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på at du vil fjerne kortet som slutter på {lastFourDigits} fra medlemsprofilen din?", @@ -99,6 +100,7 @@ "How do you want to sleep?": "Hvordan vil du sove?", "How it works": "Hvordan det fungerer", "Image gallery": "Bildegalleri", + "It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Det er ikke mulig å administrere kommunikasjonspreferansene dine akkurat nå, prøv igjen senere eller kontakt support hvis problemet vedvarer.", "Join Scandic Friends": "Bli med i Scandic Friends", "Join at no cost": "Bli med uten kostnad", "Language": "Språk", diff --git a/i18n/dictionaries/sv.json b/i18n/dictionaries/sv.json index 21bf5397e..56274dfad 100644 --- a/i18n/dictionaries/sv.json +++ b/i18n/dictionaries/sv.json @@ -10,6 +10,7 @@ "Amenities": "Bekvämligheter", "Amusement park": "Nöjespark", "An error occurred when adding a credit card, please try again later.": "Ett fel uppstod när ett kreditkort lades till, försök igen senare.", + "An error occurred trying to manage your preferences, please try again later.": "Ett fel uppstod när du försökte hantera dina inställningar, försök igen senare.", "An error occurred when trying to update profile.": "Ett fel uppstod när du försökte uppdatera profilen.", "Any changes you've made will be lost.": "Alla ändringar du har gjort kommer att gå förlorade.", "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Är du säker på att du vill ta bort kortet som slutar med {lastFourDigits} från din medlemsprofil?", @@ -99,6 +100,8 @@ "How do you want to sleep?": "Hur vill du sova?", "How it works": "Hur det fungerar", "Image gallery": "Bildgalleri", + "It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Det gick inte att hantera dina kommunikationsinställningar just nu, försök igen senare eller kontakta supporten om problemet kvarstår.", + "Join Scandic Friends": "Gå med i Scandic Friends", "Join at no cost": "Gå med utan kostnad", "Language": "Språk", diff --git a/lib/api/endpoints.ts b/lib/api/endpoints.ts index 213e40b37..b31f32b5f 100644 --- a/lib/api/endpoints.ts +++ b/lib/api/endpoints.ts @@ -21,6 +21,9 @@ export namespace endpoints { upcomingStays = "booking/v1/Stays/future", rewards = `${profile}/reward`, tierRewards = `${profile}/TierRewards`, + intiateSaveCard = `${creditCards}/initiateSaveCard`, + deleteCreditCard = `${profile}/creditCards`, + subscriberId = `${profile}/SubscriberId`, } } diff --git a/next-env.d.ts b/next-env.d.ts index 4f11a03dc..40c3d6809 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/server/routers/user/mutation.ts b/server/routers/user/mutation.ts index 38bb458e3..7e99c142f 100644 --- a/server/routers/user/mutation.ts +++ b/server/routers/user/mutation.ts @@ -1,5 +1,11 @@ +import { metrics } from "@opentelemetry/api" + +import { env } from "@/env/server" import * as api from "@/lib/api" -import { initiateSaveCardSchema } from "@/server/routers/user/output" +import { + initiateSaveCardSchema, + subscriberIdSchema, +} from "@/server/routers/user/output" import { protectedProcedure, router } from "@/server/trpc" import { @@ -8,6 +14,17 @@ import { saveCreditCardInput, } from "./input" +const meter = metrics.getMeter("trpc.user") +const generatePreferencesLinkCounter = meter.createCounter( + "trpc.user.generatePreferencesLink" +) +const generatePreferencesLinkSuccessCounter = meter.createCounter( + "trpc.user.generatePreferencesLink-success" +) +const generatePreferencesLinkFailCounter = meter.createCounter( + "trpc.user.generatePreferencesLink-fail" +) + export const userMutationRouter = router({ creditCard: router({ add: protectedProcedure.input(addCreditCardInput).mutation(async function ({ @@ -128,4 +145,62 @@ export const userMutationRouter = router({ return true }), }), + generatePreferencesLink: protectedProcedure.mutation(async function ({ + ctx, + }) { + generatePreferencesLinkCounter.add(1) + const apiResponse = await api.get(api.endpoints.v1.subscriberId, { + headers: { + Authorization: `Bearer ${ctx.session.token.access_token}`, + }, + }) + + if (!apiResponse.ok) { + const text = await apiResponse.text() + generatePreferencesLinkFailCounter.add(1, { + error_type: "http_error", + error: JSON.stringify({ + status: apiResponse.status, + statusText: apiResponse.statusText, + text, + }), + }) + console.error( + "api.user.subscriberId error ", + JSON.stringify({ + error: { + status: apiResponse.status, + statusText: apiResponse.statusText, + text, + }, + }) + ) + return null + } + + const data = await apiResponse.json() + + const validatedData = subscriberIdSchema.safeParse(data) + + if (!validatedData.success) { + generatePreferencesLinkSuccessCounter.add(1, { + error_type: "validation_error", + error: JSON.stringify(validatedData.error), + }) + console.error( + "api.user.generatePreferencesLink validation error", + JSON.stringify({ + error: validatedData.error, + }) + ) + console.error(validatedData.error.format()) + + return null + } + const preferencesLink = new URL(env.SALESFORCE_PREFRENCE_BASE_URL) + preferencesLink.searchParams.set("subKey", validatedData.data.subscriberId) + + generatePreferencesLinkSuccessCounter.add(1) + return preferencesLink.toString() + }), }) diff --git a/server/routers/user/output.ts b/server/routers/user/output.ts index 8ee482057..9c5e955ab 100644 --- a/server/routers/user/output.ts +++ b/server/routers/user/output.ts @@ -234,3 +234,7 @@ export const initiateSaveCardSchema = z.object({ type: z.string(), }), }) + +export const subscriberIdSchema = z.object({ + subscriberId: z.string(), +})