diff --git a/.changeset/many-beds-invent.md b/.changeset/many-beds-invent.md new file mode 100644 index 0000000000..87dfd58db8 --- /dev/null +++ b/.changeset/many-beds-invent.md @@ -0,0 +1,5 @@ +--- +"@bigcommerce/catalyst-makeswift": minor +--- + +This release includes all changes included in the `canary` 1.4.0 release (see the 1.4.0 changelog for more details: https://github.com/bigcommerce/catalyst/blob/44c682ef988030d7500275f3e4e4503a3a1af63c/core/CHANGELOG.md#140) diff --git a/.github/workflows/changesets-release.yml b/.github/workflows/changesets-release.yml index 251741f70a..38d63d889c 100644 --- a/.github/workflows/changesets-release.yml +++ b/.github/workflows/changesets-release.yml @@ -8,6 +8,12 @@ on: concurrency: ${{ github.workflow }}-${{ github.ref }} +permissions: + id-token: write + contents: write + packages: write + pull-requests: write + jobs: changesets-release: name: Changesets Release @@ -42,4 +48,3 @@ jobs: commit: "Version Packages (`${{ github.ref_name }}`)" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.nvmrc b/.nvmrc index 2bd5a0a98a..a45fd52cc5 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22 +24 diff --git a/core/app/[locale]/(default)/(auth)/login/page.tsx b/core/app/[locale]/(default)/(auth)/login/page.tsx index d6bab2ddd6..c669bd33c4 100644 --- a/core/app/[locale]/(default)/(auth)/login/page.tsx +++ b/core/app/[locale]/(default)/(auth)/login/page.tsx @@ -14,6 +14,7 @@ interface Props { params: Promise<{ locale: string }>; searchParams: Promise<{ redirectTo?: string; + error?: string; }>; } @@ -29,7 +30,7 @@ export async function generateMetadata({ params }: Props): Promise { export default async function Login({ params, searchParams }: Props) { const { locale } = await params; - const { redirectTo = '/account/orders' } = await searchParams; + const { redirectTo = '/account/orders', error } = await searchParams; setRequestLocale(locale); @@ -38,6 +39,7 @@ export default async function Login({ params, searchParams }: Props) { const vanityUrl = buildConfig.get('urls').vanityUrl; const redirectUrl = new URL(redirectTo, vanityUrl); const redirectTarget = redirectUrl.pathname + redirectUrl.search; + const tokenErrorMessage = error === 'InvalidToken' ? t('invalidToken') : undefined; return ( <> @@ -45,6 +47,7 @@ export default async function Login({ params, searchParams }: Props) { { }; } +// There is currently a GraphQL gap where the "Exclusive Offers" field isn't accounted for +// during customer registration, so the field should not be shown on the Catalyst storefront until it is hooked up. +function removeExlusiveOffersField(field: Field | Field[]): boolean { + if (Array.isArray(field)) { + // Exclusive offers field will always have ID '25', since it is made upon store creation and is also read-only. + return !field.some((f) => f.id === '25'); + } + + return field.id !== '25'; +} + export default async function Register({ params }: Props) { const { locale } = await params; @@ -90,7 +102,8 @@ export default async function Register({ params }: Props) { return injectCountryCodeOptions(field, countries ?? []); }) - .filter(exists); + .filter(exists) + .filter(removeExlusiveOffersField); return ( { + const t = await getTranslations('Account.Settings.NewsletterSubscription'); + + const submission = parseWithZod(formData, { schema: updateNewsletterSubscriptionSchema }); + + if (submission.status !== 'success') { + return { lastResult: submission.reply() }; + } + + try { + let errors; + + if (submission.value.intent === 'subscribe') { + const response = await client.fetch({ + document: SubscribeToNewsletterMutation, + variables: { + input: { + email: customerInfo.email, + firstName: customerInfo.firstName, + lastName: customerInfo.lastName, + }, + }, + }); + + errors = response.data.newsletter.subscribe.errors; + } else { + const response = await client.fetch({ + document: UnsubscribeFromNewsletterMutation, + variables: { + input: { + email: customerInfo.email, + }, + }, + }); + + errors = response.data.newsletter.unsubscribe.errors; + } + + if (errors.length > 0) { + // Not handling returned errors from API since we will display a generic error message to the user + // Still returning the errors to the client for debugging purposes + return { + lastResult: submission.reply({ + formErrors: errors.map(({ message }) => message), + }), + }; + } + + unstable_expireTag(TAGS.customer); + + return { + lastResult: submission.reply(), + successMessage: t('marketingPreferencesUpdated'), + }; + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + + if (error instanceof BigCommerceGQLError) { + return { + lastResult: submission.reply({ + formErrors: error.errors.map(({ message }) => message), + }), + }; + } + + if (error instanceof Error) { + return { + lastResult: submission.reply({ formErrors: [error.message] }), + }; + } + + return { + lastResult: submission.reply({ formErrors: [String(error)] }), + }; + } +}; diff --git a/core/app/[locale]/(default)/account/settings/page-data.tsx b/core/app/[locale]/(default)/account/settings/page-data.tsx index 43dfa5d323..f0db7b0f6a 100644 --- a/core/app/[locale]/(default)/account/settings/page-data.tsx +++ b/core/app/[locale]/(default)/account/settings/page-data.tsx @@ -6,9 +6,9 @@ import { graphql, VariablesOf } from '~/client/graphql'; import { TAGS } from '~/client/tags'; import { FormFieldsFragment } from '~/data-transformers/form-field-transformer/fragment'; -const CustomerSettingsQuery = graphql( +const AccountSettingsQuery = graphql( ` - query CustomerSettingsQuery( + query AccountSettingsQuery( $customerFilters: FormFieldFiltersInput $customerSortBy: FormFieldSortInput $addressFilters: FormFieldFiltersInput @@ -20,6 +20,7 @@ const CustomerSettingsQuery = graphql( firstName lastName company + isSubscribedToNewsletter } site { settings { @@ -31,6 +32,9 @@ const CustomerSettingsQuery = graphql( ...FormFieldsFragment } } + newsletter { + showNewsletterSignup + } } } } @@ -38,7 +42,7 @@ const CustomerSettingsQuery = graphql( [FormFieldsFragment], ); -type Variables = VariablesOf; +type Variables = VariablesOf; interface Props { address?: { @@ -52,11 +56,11 @@ interface Props { }; } -export const getCustomerSettingsQuery = cache(async ({ address, customer }: Props = {}) => { +export const getAccountSettingsQuery = cache(async ({ address, customer }: Props = {}) => { const customerAccessToken = await getSessionCustomerAccessToken(); const response = await client.fetch({ - document: CustomerSettingsQuery, + document: AccountSettingsQuery, variables: { addressFilters: address?.filters, addressSortBy: address?.sortBy, @@ -70,6 +74,7 @@ export const getCustomerSettingsQuery = cache(async ({ address, customer }: Prop const addressFields = response.data.site.settings?.formFields.shippingAddress; const customerFields = response.data.site.settings?.formFields.customer; const customerInfo = response.data.customer; + const newsletterSettings = response.data.site.settings?.newsletter; if (!addressFields || !customerFields || !customerInfo) { return null; @@ -79,5 +84,6 @@ export const getCustomerSettingsQuery = cache(async ({ address, customer }: Prop addressFields, customerFields, customerInfo, + newsletterSettings, }; }); diff --git a/core/app/[locale]/(default)/account/settings/page.tsx b/core/app/[locale]/(default)/account/settings/page.tsx index b37db8e530..6d074e9431 100644 --- a/core/app/[locale]/(default)/account/settings/page.tsx +++ b/core/app/[locale]/(default)/account/settings/page.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/jsx-no-bind */ import { Metadata } from 'next'; import { notFound } from 'next/navigation'; import { getTranslations, setRequestLocale } from 'next-intl/server'; @@ -6,7 +7,8 @@ import { AccountSettingsSection } from '@/vibes/soul/sections/account-settings'; import { changePassword } from './_actions/change-password'; import { updateCustomer } from './_actions/update-customer'; -import { getCustomerSettingsQuery } from './page-data'; +import { updateNewsletterSubscription } from './_actions/update-newsletter-subscription'; +import { getAccountSettingsQuery } from './page-data'; interface Props { params: Promise<{ locale: string }>; @@ -29,24 +31,40 @@ export default async function Settings({ params }: Props) { const t = await getTranslations('Account.Settings'); - const customerSettings = await getCustomerSettingsQuery(); + const accountSettings = await getAccountSettingsQuery(); - if (!customerSettings) { + if (!accountSettings) { notFound(); } + const newsletterSubscriptionEnabled = accountSettings.newsletterSettings?.showNewsletterSignup; + const isAccountSubscribed = accountSettings.customerInfo.isSubscribedToNewsletter; + + const updateNewsletterSubscriptionActionWithCustomerInfo = updateNewsletterSubscription.bind( + null, + { + customerInfo: accountSettings.customerInfo, + }, + ); + return ( ); } diff --git a/core/app/[locale]/(default)/cart/page-data.ts b/core/app/[locale]/(default)/cart/page-data.ts index 0143a3de6d..83ff9ac449 100644 --- a/core/app/[locale]/(default)/cart/page-data.ts +++ b/core/app/[locale]/(default)/cart/page-data.ts @@ -19,19 +19,11 @@ export const PhysicalItemFragment = graphql(` quantity productEntityId variantEntityId - extendedListPrice { - currencyCode - value - } - extendedSalePrice { - currencyCode - value - } - originalPrice { + listPrice { currencyCode value } - listPrice { + salePrice { currencyCode value } @@ -79,19 +71,11 @@ export const DigitalItemFragment = graphql(` quantity productEntityId variantEntityId - extendedListPrice { - currencyCode - value - } - extendedSalePrice { - currencyCode - value - } - originalPrice { + listPrice { currencyCode value } - listPrice { + salePrice { currencyCode value } diff --git a/core/app/[locale]/(default)/cart/page.tsx b/core/app/[locale]/(default)/cart/page.tsx index 3b6705a112..878338114f 100644 --- a/core/app/[locale]/(default)/cart/page.tsx +++ b/core/app/[locale]/(default)/cart/page.tsx @@ -131,6 +131,10 @@ export default async function Cart({ params }: Props) { style: 'currency', currency: item.listPrice.currencyCode, }), + salePrice: format.number(item.salePrice.value, { + style: 'currency', + currency: item.salePrice.currencyCode, + }), subtitle: item.selectedOptions .map((option) => { switch (option.__typename) { diff --git a/core/components/subscribe/_actions/subscribe.ts b/core/components/subscribe/_actions/subscribe.ts index 149ce47d2e..62a82ec774 100644 --- a/core/components/subscribe/_actions/subscribe.ts +++ b/core/components/subscribe/_actions/subscribe.ts @@ -57,18 +57,25 @@ export const subscribe = async ( const errors = response.data.newsletter.subscribe.errors; - if (!errors.length) { - return { lastResult: submission.reply({ resetForm: true }), successMessage: t('success') }; + // If subscriber already exists, treat it as success for privacy reasons + // We don't want to reveal that the email is already subscribed + const subscriberAlreadyExists = errors.some( + ({ __typename }) => __typename === 'CreateSubscriberAlreadyExistsError', + ); + + if (subscriberAlreadyExists) { + return { + lastResult: submission.reply(), + successMessage: t('subscribedToNewsletter'), + }; } if (errors.length > 0) { + // If there are other errors, we want to show the error message to the user return { lastResult: submission.reply({ formErrors: errors.map(({ __typename }) => { switch (__typename) { - case 'CreateSubscriberAlreadyExistsError': - return t('Errors.subcriberAlreadyExists'); - case 'CreateSubscriberEmailInvalidError': return t('Errors.invalidEmail'); @@ -80,7 +87,11 @@ export const subscribe = async ( }; } - return { lastResult: submission.reply({ formErrors: [t('Errors.somethingWentWrong')] }) }; + // If there are no errors, we want to show the success message to the user + return { + lastResult: submission.reply(), + successMessage: t('subscribedToNewsletter'), + }; } catch (error) { // eslint-disable-next-line no-console console.error(error); @@ -97,6 +108,6 @@ export const subscribe = async ( return { lastResult: submission.reply({ formErrors: [error.message] }) }; } - return { lastResult: submission.reply({ formErrors: [t('Errors.somethingWentWrong')] }) }; + return { lastResult: submission.reply({ formErrors: [String(error)] }) }; } }; diff --git a/core/messages/da.json b/core/messages/da.json index 632ad156ea..10fc5ffde2 100644 --- a/core/messages/da.json +++ b/core/messages/da.json @@ -55,6 +55,7 @@ "invalidCredentials": "Din e-mailadresse eller adgangskode er forkert. Prøv at logge ind igen, eller nulstil din adgangskode", "somethingWentWrong": "Noget gik galt. Prøv igen senere.", "passwordResetRequired": "Nulstilling af adgangskode påkrævet. Kontrollér din e-mail for instruktioner til at nulstille din adgangskode.", + "invalidToken": "Dit login-link er ugyldigt eller udløbet. Prøv at logge ind igen.", "CreateAccount": { "title": "Ny kunde?", "accountBenefits": "Opret en konto hos os, og du vil kunne:", @@ -198,7 +199,13 @@ "currentPassword": "Nuværende adgangskode", "newPassword": "Den nye adgangskode", "confirmPassword": "Bekræft adgangskode", - "cta": "Opdater" + "cta": "Opdater", + "NewsletterSubscription": { + "title": "Markedsføringspræferencer", + "label": "Abonner på vores nyhedsbrev.", + "marketingPreferencesUpdated": "Markedsføringspræferencerne er blevet opdateret!", + "somethingWentWrong": "Noget gik galt. Prøv igen senere." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Tilmeld dig vores nyhedsbrev", "placeholder": "Indtast din e-mailadresse", "description": "Hold dig opdateret med de seneste nyheder og tilbud fra vores butik.", - "success": "Tak for din interesse! Nyhedsbrevsfunktionen kommer snart!" + "subscribedToNewsletter": "Du er blevet tilmeldt vores nyhedsbrev!", + "Errors": { + "invalidEmail": "Indtast en gyldig e-mailadresse.", + "somethingWentWrong": "Noget gik galt. Prøv igen senere." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Vi værdsætter dit privatliv", - "description": "Denne hjemmeside bruger cookies til at forbedre din browseroplevelse, analysere webstedstrafik og vise personligt tilpasset indhold." + "description": "Denne hjemmeside bruger cookies til at forbedre din browseroplevelse, analysere webstedstrafik og vise personligt tilpasset indhold.", + "privacyPolicy": "Politik om beskyttelse af personlige oplysninger" }, "Dialog": { "title": "Privatlivsindstillinger", diff --git a/core/messages/de.json b/core/messages/de.json index 920a1ce627..7a861be68f 100644 --- a/core/messages/de.json +++ b/core/messages/de.json @@ -55,6 +55,7 @@ "invalidCredentials": "Ihre E-Mail-Adresse oder Ihr Passwort ist falsch. Versuchen Sie, sich erneut anzumelden, oder setzen Sie Ihr Passwort zurück", "somethingWentWrong": "Es ist etwas schiefgegangen Bitte versuchen Sie es später erneut.", "passwordResetRequired": "Passwortzurücksetzung erforderlich. Bitte überprüfen Sie Ihre E-Mails für die Anleitung zum Zurücksetzen Ihres Passworts.", + "invalidToken": "Ihr Anmeldelink ist ungültig oder abgelaufen. Bitte versuchen Sie erneut, sich anzumelden.", "CreateAccount": { "title": "Neuer Kunde?", "accountBenefits": "Wenn Sie ein Konto bei uns erstellen, haben Sie folgende Möglichkeiten:", @@ -198,7 +199,13 @@ "currentPassword": "Aktuelles Passwort", "newPassword": "Neues Passwort", "confirmPassword": "Passwort bestätigen", - "cta": "Update" + "cta": "Update", + "NewsletterSubscription": { + "title": "Marketingpräferenzen", + "label": "Abonnieren Sie unseren Newsletter.", + "marketingPreferencesUpdated": "Marketingeinstellungen wurden erfolgreich aktualisiert!", + "somethingWentWrong": "Es ist etwas schiefgegangen Bitte versuchen Sie es später erneut." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Melden Sie sich für unseren Newsletter an", "placeholder": "Geben Sie Ihre E-Mail-Adresse ein", "description": "Bleiben Sie auf dem Laufenden über die aktuellen Neuigkeiten und Angebote in unserem Shop.", - "success": "Vielen Dank für Ihr Interesse! Die Newsletter-Funktion wird in Kürze verfügbar sein!" + "subscribedToNewsletter": "Sie haben unseren Newsletter abonniert!", + "Errors": { + "invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse an.", + "somethingWentWrong": "Es ist etwas schiefgegangen Bitte versuchen Sie es später erneut." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Wir respektieren Ihre Privatsphäre.", - "description": "Diese Website verwendet Cookies, um Ihr Surferlebnis zu verbessern, den Website-Traffic zu analysieren und personalisierte Inhalte anzuzeigen." + "description": "Diese Website verwendet Cookies, um Ihr Surferlebnis zu verbessern, den Website-Traffic zu analysieren und personalisierte Inhalte anzuzeigen.", + "privacyPolicy": "Datenschutzerklärung" }, "Dialog": { "title": "Datenschutzeinstellungen", diff --git a/core/messages/en.json b/core/messages/en.json index 13e41668ad..fd3d9cf734 100644 --- a/core/messages/en.json +++ b/core/messages/en.json @@ -55,6 +55,7 @@ "invalidCredentials": "Your email address or password is incorrect. Try signing in again or reset your password", "somethingWentWrong": "Something went wrong. Please try again later.", "passwordResetRequired": "Password reset required. Please check your email for instructions to reset your password.", + "invalidToken": "Your login link is invalid or has expired. Please try logging in again.", "CreateAccount": { "title": "New customer?", "accountBenefits": "Create an account with us and you'll be able to:", @@ -198,7 +199,13 @@ "currentPassword": "Current password", "newPassword": "New password", "confirmPassword": "Confirm password", - "cta": "Update" + "cta": "Update", + "NewsletterSubscription": { + "title": "Marketing preferences", + "label": "Subscribe to our newsletter.", + "marketingPreferencesUpdated": "Marketing preferences have been updated successfully!", + "somethingWentWrong": "Something went wrong. Please try again later." + } } }, "Wishlist": { @@ -560,9 +567,8 @@ "title": "Sign up for our newsletter", "placeholder": "Enter your email", "description": "Stay up to date with the latest news and offers from our store.", - "success": "You have been subscribed to our newsletter.", + "subscribedToNewsletter": "You have been subscribed to our newsletter!", "Errors": { - "subcriberAlreadyExists": "You are already subscribed to our newsletter.", "invalidEmail": "Please enter a valid email address.", "somethingWentWrong": "Something went wrong. Please try again later." } diff --git a/core/messages/es-419.json b/core/messages/es-419.json index 9fbaeb9b1f..2f6e4b1ea2 100644 --- a/core/messages/es-419.json +++ b/core/messages/es-419.json @@ -55,6 +55,7 @@ "invalidCredentials": "Tu dirección de correo electrónico o tu contraseña son incorrectas. Intenta iniciar sesión de nuevo o restablece tu contraseña", "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde.", "passwordResetRequired": "Se requiere restablecimiento de contraseña. Por favor, revisa tu email para ver instrucciones para restablecer tu contraseña.", + "invalidToken": "Tu enlace de acceso es inválido o caducó. Por favor, intenta iniciar sesión de nuevo.", "CreateAccount": { "title": "¿Nuevo cliente?", "accountBenefits": "Crea una cuenta con nosotros y podrás:", @@ -198,7 +199,13 @@ "currentPassword": "Contraseña actual", "newPassword": "Nueva contraseña", "confirmPassword": "Confirmar contraseña", - "cta": "Actualizar" + "cta": "Actualizar", + "NewsletterSubscription": { + "title": "Preferencias de marketing", + "label": "Suscríbete a nuestro boletín informativo.", + "marketingPreferencesUpdated": "¡Las preferencias de marketing se actualizaron con éxito!", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Suscríbete a nuestro boletín informativo", "placeholder": "Ingresa tu correo electrónico", "description": "Mantente al día con las últimas noticias y ofertas de nuestra tienda.", - "success": "¡Gracias por tu interés! ¡La función de boletín llegará pronto!" + "subscribedToNewsletter": "¡Te suscribiste a nuestro boletín!", + "Errors": { + "invalidEmail": "Ingrese una dirección de correo electrónico válida.", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Valoramos su privacidad", - "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado." + "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado.", + "privacyPolicy": "Política de Privacidad" }, "Dialog": { "title": "Configuración de privacidad", diff --git a/core/messages/es-AR.json b/core/messages/es-AR.json index 9fbaeb9b1f..2f6e4b1ea2 100644 --- a/core/messages/es-AR.json +++ b/core/messages/es-AR.json @@ -55,6 +55,7 @@ "invalidCredentials": "Tu dirección de correo electrónico o tu contraseña son incorrectas. Intenta iniciar sesión de nuevo o restablece tu contraseña", "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde.", "passwordResetRequired": "Se requiere restablecimiento de contraseña. Por favor, revisa tu email para ver instrucciones para restablecer tu contraseña.", + "invalidToken": "Tu enlace de acceso es inválido o caducó. Por favor, intenta iniciar sesión de nuevo.", "CreateAccount": { "title": "¿Nuevo cliente?", "accountBenefits": "Crea una cuenta con nosotros y podrás:", @@ -198,7 +199,13 @@ "currentPassword": "Contraseña actual", "newPassword": "Nueva contraseña", "confirmPassword": "Confirmar contraseña", - "cta": "Actualizar" + "cta": "Actualizar", + "NewsletterSubscription": { + "title": "Preferencias de marketing", + "label": "Suscríbete a nuestro boletín informativo.", + "marketingPreferencesUpdated": "¡Las preferencias de marketing se actualizaron con éxito!", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Suscríbete a nuestro boletín informativo", "placeholder": "Ingresa tu correo electrónico", "description": "Mantente al día con las últimas noticias y ofertas de nuestra tienda.", - "success": "¡Gracias por tu interés! ¡La función de boletín llegará pronto!" + "subscribedToNewsletter": "¡Te suscribiste a nuestro boletín!", + "Errors": { + "invalidEmail": "Ingrese una dirección de correo electrónico válida.", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Valoramos su privacidad", - "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado." + "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado.", + "privacyPolicy": "Política de Privacidad" }, "Dialog": { "title": "Configuración de privacidad", diff --git a/core/messages/es-CL.json b/core/messages/es-CL.json index 9fbaeb9b1f..2f6e4b1ea2 100644 --- a/core/messages/es-CL.json +++ b/core/messages/es-CL.json @@ -55,6 +55,7 @@ "invalidCredentials": "Tu dirección de correo electrónico o tu contraseña son incorrectas. Intenta iniciar sesión de nuevo o restablece tu contraseña", "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde.", "passwordResetRequired": "Se requiere restablecimiento de contraseña. Por favor, revisa tu email para ver instrucciones para restablecer tu contraseña.", + "invalidToken": "Tu enlace de acceso es inválido o caducó. Por favor, intenta iniciar sesión de nuevo.", "CreateAccount": { "title": "¿Nuevo cliente?", "accountBenefits": "Crea una cuenta con nosotros y podrás:", @@ -198,7 +199,13 @@ "currentPassword": "Contraseña actual", "newPassword": "Nueva contraseña", "confirmPassword": "Confirmar contraseña", - "cta": "Actualizar" + "cta": "Actualizar", + "NewsletterSubscription": { + "title": "Preferencias de marketing", + "label": "Suscríbete a nuestro boletín informativo.", + "marketingPreferencesUpdated": "¡Las preferencias de marketing se actualizaron con éxito!", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Suscríbete a nuestro boletín informativo", "placeholder": "Ingresa tu correo electrónico", "description": "Mantente al día con las últimas noticias y ofertas de nuestra tienda.", - "success": "¡Gracias por tu interés! ¡La función de boletín llegará pronto!" + "subscribedToNewsletter": "¡Te suscribiste a nuestro boletín!", + "Errors": { + "invalidEmail": "Ingrese una dirección de correo electrónico válida.", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Valoramos su privacidad", - "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado." + "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado.", + "privacyPolicy": "Política de Privacidad" }, "Dialog": { "title": "Configuración de privacidad", diff --git a/core/messages/es-CO.json b/core/messages/es-CO.json index 9fbaeb9b1f..2f6e4b1ea2 100644 --- a/core/messages/es-CO.json +++ b/core/messages/es-CO.json @@ -55,6 +55,7 @@ "invalidCredentials": "Tu dirección de correo electrónico o tu contraseña son incorrectas. Intenta iniciar sesión de nuevo o restablece tu contraseña", "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde.", "passwordResetRequired": "Se requiere restablecimiento de contraseña. Por favor, revisa tu email para ver instrucciones para restablecer tu contraseña.", + "invalidToken": "Tu enlace de acceso es inválido o caducó. Por favor, intenta iniciar sesión de nuevo.", "CreateAccount": { "title": "¿Nuevo cliente?", "accountBenefits": "Crea una cuenta con nosotros y podrás:", @@ -198,7 +199,13 @@ "currentPassword": "Contraseña actual", "newPassword": "Nueva contraseña", "confirmPassword": "Confirmar contraseña", - "cta": "Actualizar" + "cta": "Actualizar", + "NewsletterSubscription": { + "title": "Preferencias de marketing", + "label": "Suscríbete a nuestro boletín informativo.", + "marketingPreferencesUpdated": "¡Las preferencias de marketing se actualizaron con éxito!", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Suscríbete a nuestro boletín informativo", "placeholder": "Ingresa tu correo electrónico", "description": "Mantente al día con las últimas noticias y ofertas de nuestra tienda.", - "success": "¡Gracias por tu interés! ¡La función de boletín llegará pronto!" + "subscribedToNewsletter": "¡Te suscribiste a nuestro boletín!", + "Errors": { + "invalidEmail": "Ingrese una dirección de correo electrónico válida.", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Valoramos su privacidad", - "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado." + "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado.", + "privacyPolicy": "Política de Privacidad" }, "Dialog": { "title": "Configuración de privacidad", diff --git a/core/messages/es-LA.json b/core/messages/es-LA.json index 9fbaeb9b1f..2f6e4b1ea2 100644 --- a/core/messages/es-LA.json +++ b/core/messages/es-LA.json @@ -55,6 +55,7 @@ "invalidCredentials": "Tu dirección de correo electrónico o tu contraseña son incorrectas. Intenta iniciar sesión de nuevo o restablece tu contraseña", "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde.", "passwordResetRequired": "Se requiere restablecimiento de contraseña. Por favor, revisa tu email para ver instrucciones para restablecer tu contraseña.", + "invalidToken": "Tu enlace de acceso es inválido o caducó. Por favor, intenta iniciar sesión de nuevo.", "CreateAccount": { "title": "¿Nuevo cliente?", "accountBenefits": "Crea una cuenta con nosotros y podrás:", @@ -198,7 +199,13 @@ "currentPassword": "Contraseña actual", "newPassword": "Nueva contraseña", "confirmPassword": "Confirmar contraseña", - "cta": "Actualizar" + "cta": "Actualizar", + "NewsletterSubscription": { + "title": "Preferencias de marketing", + "label": "Suscríbete a nuestro boletín informativo.", + "marketingPreferencesUpdated": "¡Las preferencias de marketing se actualizaron con éxito!", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Suscríbete a nuestro boletín informativo", "placeholder": "Ingresa tu correo electrónico", "description": "Mantente al día con las últimas noticias y ofertas de nuestra tienda.", - "success": "¡Gracias por tu interés! ¡La función de boletín llegará pronto!" + "subscribedToNewsletter": "¡Te suscribiste a nuestro boletín!", + "Errors": { + "invalidEmail": "Ingrese una dirección de correo electrónico válida.", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Valoramos su privacidad", - "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado." + "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado.", + "privacyPolicy": "Política de Privacidad" }, "Dialog": { "title": "Configuración de privacidad", diff --git a/core/messages/es-MX.json b/core/messages/es-MX.json index 9fbaeb9b1f..2f6e4b1ea2 100644 --- a/core/messages/es-MX.json +++ b/core/messages/es-MX.json @@ -55,6 +55,7 @@ "invalidCredentials": "Tu dirección de correo electrónico o tu contraseña son incorrectas. Intenta iniciar sesión de nuevo o restablece tu contraseña", "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde.", "passwordResetRequired": "Se requiere restablecimiento de contraseña. Por favor, revisa tu email para ver instrucciones para restablecer tu contraseña.", + "invalidToken": "Tu enlace de acceso es inválido o caducó. Por favor, intenta iniciar sesión de nuevo.", "CreateAccount": { "title": "¿Nuevo cliente?", "accountBenefits": "Crea una cuenta con nosotros y podrás:", @@ -198,7 +199,13 @@ "currentPassword": "Contraseña actual", "newPassword": "Nueva contraseña", "confirmPassword": "Confirmar contraseña", - "cta": "Actualizar" + "cta": "Actualizar", + "NewsletterSubscription": { + "title": "Preferencias de marketing", + "label": "Suscríbete a nuestro boletín informativo.", + "marketingPreferencesUpdated": "¡Las preferencias de marketing se actualizaron con éxito!", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Suscríbete a nuestro boletín informativo", "placeholder": "Ingresa tu correo electrónico", "description": "Mantente al día con las últimas noticias y ofertas de nuestra tienda.", - "success": "¡Gracias por tu interés! ¡La función de boletín llegará pronto!" + "subscribedToNewsletter": "¡Te suscribiste a nuestro boletín!", + "Errors": { + "invalidEmail": "Ingrese una dirección de correo electrónico válida.", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Valoramos su privacidad", - "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado." + "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado.", + "privacyPolicy": "Política de Privacidad" }, "Dialog": { "title": "Configuración de privacidad", diff --git a/core/messages/es-PE.json b/core/messages/es-PE.json index 9fbaeb9b1f..2f6e4b1ea2 100644 --- a/core/messages/es-PE.json +++ b/core/messages/es-PE.json @@ -55,6 +55,7 @@ "invalidCredentials": "Tu dirección de correo electrónico o tu contraseña son incorrectas. Intenta iniciar sesión de nuevo o restablece tu contraseña", "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde.", "passwordResetRequired": "Se requiere restablecimiento de contraseña. Por favor, revisa tu email para ver instrucciones para restablecer tu contraseña.", + "invalidToken": "Tu enlace de acceso es inválido o caducó. Por favor, intenta iniciar sesión de nuevo.", "CreateAccount": { "title": "¿Nuevo cliente?", "accountBenefits": "Crea una cuenta con nosotros y podrás:", @@ -198,7 +199,13 @@ "currentPassword": "Contraseña actual", "newPassword": "Nueva contraseña", "confirmPassword": "Confirmar contraseña", - "cta": "Actualizar" + "cta": "Actualizar", + "NewsletterSubscription": { + "title": "Preferencias de marketing", + "label": "Suscríbete a nuestro boletín informativo.", + "marketingPreferencesUpdated": "¡Las preferencias de marketing se actualizaron con éxito!", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Suscríbete a nuestro boletín informativo", "placeholder": "Ingresa tu correo electrónico", "description": "Mantente al día con las últimas noticias y ofertas de nuestra tienda.", - "success": "¡Gracias por tu interés! ¡La función de boletín llegará pronto!" + "subscribedToNewsletter": "¡Te suscribiste a nuestro boletín!", + "Errors": { + "invalidEmail": "Ingrese una dirección de correo electrónico válida.", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Valoramos su privacidad", - "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado." + "description": "Este sitio emplea cookies para mejorar su experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado.", + "privacyPolicy": "Política de Privacidad" }, "Dialog": { "title": "Configuración de privacidad", diff --git a/core/messages/es.json b/core/messages/es.json index e2e35c1712..60bfd489ae 100644 --- a/core/messages/es.json +++ b/core/messages/es.json @@ -55,6 +55,7 @@ "invalidCredentials": "Tu dirección de correo electrónico o contraseña son incorrectas. Intenta iniciar sesión de nuevo o restablece tu contraseña", "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde.", "passwordResetRequired": "Es necesario restablecer la contraseña. Consulta tu correo electrónico para recibir instrucciones sobre cómo restablecer tu contraseña.", + "invalidToken": "Tu enlace de inicio de sesión no es válido o ha caducado. Intenta conectarte de nuevo.", "CreateAccount": { "title": "¿Cliente nuevo?", "accountBenefits": "Cree una cuenta con nosotros y podrá:", @@ -198,7 +199,13 @@ "currentPassword": "Contraseña actual", "newPassword": "Nueva contraseña", "confirmPassword": "Confirmar contraseña", - "cta": "Actualizar" + "cta": "Actualizar", + "NewsletterSubscription": { + "title": "Preferencias de marketing", + "label": "Suscríbase a nuestro boletín.", + "marketingPreferencesUpdated": "¡Las preferencias de marketing se han actualizado correctamente!", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Suscribirse a nuestro boletín", "placeholder": "Introduzca su correo electrónico", "description": "Entérate de todas las novedades y ofertas de nuestra tienda.", - "success": "Gracias por tu interés. ¡La función de boletín estará disponible pronto!" + "subscribedToNewsletter": "¡Te has suscrito a nuestro boletín!", + "Errors": { + "invalidEmail": "Ingrese una dirección de correo electrónico válida.", + "somethingWentWrong": "Algo salió mal. Vuelva a intentarlo más tarde." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Valoramos tu privacidad", - "description": "Este sitio utiliza cookies para mejorar tu experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado." + "description": "Este sitio utiliza cookies para mejorar tu experiencia de navegación, analizar el tráfico del sitio y mostrar contenido personalizado.", + "privacyPolicy": "Política de privacidad" }, "Dialog": { "title": "Configuración de privacidad", diff --git a/core/messages/fr.json b/core/messages/fr.json index f90a7d3f4b..bdaa38cb74 100644 --- a/core/messages/fr.json +++ b/core/messages/fr.json @@ -55,6 +55,7 @@ "invalidCredentials": "Votre adresse e-mail ou votre mot de passe est incorrect. Essayez de vous reconnecter ou réinitialisez votre mot de passe", "somethingWentWrong": "Une erreur s'est produite. Veuillez réessayer plus tard.", "passwordResetRequired": "Réinitialisation du mot de passe requise. Veuillez consulter votre e-mail pour les instructions de réinitialisation de votre mot de passe.", + "invalidToken": "Votre lien de connexion n'est pas valide ou a expiré. Veuillez réessayer de vous connecter.", "CreateAccount": { "title": "Nouveau client ?", "accountBenefits": "Créez un compte sur notre site et vous pourrez :", @@ -198,7 +199,13 @@ "currentPassword": "Mot de passe actuel", "newPassword": "Nouveau mot de passe", "confirmPassword": "Confirmer le mot de passe", - "cta": "Mettre à jour" + "cta": "Mettre à jour", + "NewsletterSubscription": { + "title": "Préférences de marketing", + "label": "Abonnez-vous à notre newsletter.", + "marketingPreferencesUpdated": "Les préférences marketing ont bien été mises à jour.", + "somethingWentWrong": "Une erreur s'est produite. Veuillez réessayer plus tard." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Abonnez-vous à notre newsletter", "placeholder": "Saisissez votre adresse e-mail", "description": "Restez informé des dernières nouvelles et offres de notre magasin.", - "success": "Merci de l’intérêt que vous nous portez ! La fonctionnalité Newsletter sera bientôt disponible !" + "subscribedToNewsletter": "Votre abonnement à la newsletter a bien été pris en compte !", + "Errors": { + "invalidEmail": "Veuillez saisir une adresse e-mail valide.", + "somethingWentWrong": "Une erreur s'est produite. Veuillez réessayer plus tard." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Nous respectons votre vie privée", - "description": "Ce site utilise des cookies pour améliorer votre expérience de navigation, analyser le trafic du site et afficher du contenu personnalisé." + "description": "Ce site utilise des cookies pour améliorer votre expérience de navigation, analyser le trafic du site et afficher du contenu personnalisé.", + "privacyPolicy": "Politique de confidentialité" }, "Dialog": { "title": "Paramètres de confidentialité", diff --git a/core/messages/it.json b/core/messages/it.json index bbeb1c2d3d..bf8fe8b84d 100644 --- a/core/messages/it.json +++ b/core/messages/it.json @@ -55,6 +55,7 @@ "invalidCredentials": "L'indirizzo e-mail o la password non sono corretti. Prova ad accedere di nuovo o reimposta la password", "somethingWentWrong": "Si è verificato un errore. Riprova più tardi.", "passwordResetRequired": "È necessario reimpostare la password. Controlla la tua email per le istruzioni su come reimpostare la password.", + "invalidToken": "Il tuo link di accesso non è valido o è scaduto. Riprova ad accedere.", "CreateAccount": { "title": "Nuovo cliente?", "accountBenefits": "Crea un account con noi e sarai in grado di:", @@ -198,7 +199,13 @@ "currentPassword": "Password attuale", "newPassword": "Nuova password", "confirmPassword": "Conferma password", - "cta": "Aggiorna" + "cta": "Aggiorna", + "NewsletterSubscription": { + "title": "Preferenze di marketing", + "label": "Iscriviti alla nostra newsletter.", + "marketingPreferencesUpdated": "Le preferenze di marketing sono state aggiornate.", + "somethingWentWrong": "Si è verificato un errore. Riprova più tardi." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Iscriviti alla nostra newsletter", "placeholder": "Inserisci la tua e-mail", "description": "Ricevi aggiornamenti sulle ultime novità e offerte dal nostro negozio.", - "success": "Grazie per il tuo interesse. La funzione Newsletter sarà presto disponibile!" + "subscribedToNewsletter": "Hai effettuato l'iscrizione alla nostra newsletter.", + "Errors": { + "invalidEmail": "Inserisci un indirizzo e-mail valido.", + "somethingWentWrong": "Si è verificato un errore. Riprova più tardi." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Rispettiamo la tua privacy", - "description": "Questo sito utilizza i cookie per migliorare la tua esperienza di navigazione, analizzare il traffico del sito e mostrare contenuti personalizzati." + "description": "Questo sito utilizza i cookie per migliorare la tua esperienza di navigazione, analizzare il traffico del sito e mostrare contenuti personalizzati.", + "privacyPolicy": "Informativa sulla privacy" }, "Dialog": { "title": "Impostazioni privacy", diff --git a/core/messages/ja.json b/core/messages/ja.json index 3a7044aa60..406a3131e8 100644 --- a/core/messages/ja.json +++ b/core/messages/ja.json @@ -55,6 +55,7 @@ "invalidCredentials": "メールアドレスまたはパスワードが間違っています。もう一度サインインするか、パスワードをリセットしてください", "somethingWentWrong": "何か問題が発生しました。後でもう一度やり直してください。", "passwordResetRequired": "パスワードのリセットが必要です。パスワードをリセットするための手順については、メールを確認してください。", + "invalidToken": "ログインリンクが無効または期限切れです。もう一度ログインしてください。", "CreateAccount": { "title": "新規のお客様ですか?", "accountBenefits": "アカウントを作成すると、次のことができるようになります:", @@ -198,7 +199,13 @@ "currentPassword": "現在のパスワード", "newPassword": "新しいパスワード", "confirmPassword": "パスワード確認", - "cta": "アップデート" + "cta": "アップデート", + "NewsletterSubscription": { + "title": "マーケティングの好み", + "label": "ニュースレターに登録してください。", + "marketingPreferencesUpdated": "マーケティング設定が正常に更新されました。", + "somethingWentWrong": "何か問題が発生しました。後でもう一度やり直してください。" + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "ニュースレターにご登録ください", "placeholder": "メールアドレスを入力してください", "description": "当店の最新ニュースやオファーをぜひチェックしてください。", - "success": "ご関心をお寄せいただきありがとうございます!ニュースレター機能は近日開始予定です!" + "subscribedToNewsletter": "ニュースレターを購読されました。", + "Errors": { + "invalidEmail": "有効なメールアドレスを入力してください。", + "somethingWentWrong": "何か問題が発生しました。後でもう一度やり直してください。" + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "私たちはあなたのプライバシーを大切にしています", - "description": "このサイトでは、ブラウジング体験の向上、サイトトラフィックの分析、パーソナライズされたコンテンツの表示のために Cookie を使用しています。" + "description": "このサイトでは、ブラウジング体験の向上、サイトトラフィックの分析、パーソナライズされたコンテンツの表示のために Cookie を使用しています。", + "privacyPolicy": "プライバシーポリシー" }, "Dialog": { "title": "プライバシー設定", diff --git a/core/messages/nl.json b/core/messages/nl.json index 3beb0e5029..b455b0fca8 100644 --- a/core/messages/nl.json +++ b/core/messages/nl.json @@ -55,6 +55,7 @@ "invalidCredentials": "Je e-mailadres of wachtwoord is niet correct. Probeer opnieuw in te loggen of stel je wachtwoord opnieuw in", "somethingWentWrong": "Er is iets fout gegaan. Probeer het later opnieuw.", "passwordResetRequired": "Wachtwoord opnieuw instellen is vereist. Controleer uw e-mail voor instructies om uw wachtwoord opnieuw in te stellen.", + "invalidToken": "Je aanmeldingslink is ongeldig of verlopen. Probeer opnieuw in te loggen.", "CreateAccount": { "title": "Nieuwe klant?", "accountBenefits": "Maak een account aan bij ons om:", @@ -198,7 +199,13 @@ "currentPassword": "Huidig wachtwoord", "newPassword": "Nieuw wachtwoord", "confirmPassword": "Wachtwoord bevestigen", - "cta": "Bijwerken" + "cta": "Bijwerken", + "NewsletterSubscription": { + "title": "Marketingvoorkeuren", + "label": "Meld u aan voor onze nieuwsbrief.", + "marketingPreferencesUpdated": "Marketingvoorkeuren zijn bijgewerkt!", + "somethingWentWrong": "Er is iets fout gegaan. Probeer het later opnieuw." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Aanmelden voor onze nieuwsbrief", "placeholder": "Voer uw e-mail in", "description": "Blijf op de hoogte van het laatste nieuws en aanbiedingen van onze winkel.", - "success": "Bedankt voor je interesse! De nieuwsbrieffunctie komt binnenkort!" + "subscribedToNewsletter": "Je bent nu geabonneerd op onze nieuwsbrief!", + "Errors": { + "invalidEmail": "Voer een geldig e-mailadres in.", + "somethingWentWrong": "Er is iets fout gegaan. Probeer het later opnieuw." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "We waarderen je privacy", - "description": "Deze site maakt gebruik van cookies om je browse-ervaring te verbeteren, siteverkeer te analyseren en gepersonaliseerde inhoud weer te geven." + "description": "Deze site maakt gebruik van cookies om je browse-ervaring te verbeteren, siteverkeer te analyseren en gepersonaliseerde inhoud weer te geven.", + "privacyPolicy": "Privacybeleid" }, "Dialog": { "title": "Privacy-instellingen", diff --git a/core/messages/no.json b/core/messages/no.json index 1b32711a7e..b469114e09 100644 --- a/core/messages/no.json +++ b/core/messages/no.json @@ -55,6 +55,7 @@ "invalidCredentials": "E-postadressen eller passordet er feil. Prøv å logge på igjen, eller tilbakestill passordet ditt.", "somethingWentWrong": "Noe gikk galt. Prøv igjen senere.", "passwordResetRequired": "Krever tilbakestilling av passord. Kontroller e-posten din for instruksjoner om hvordan du tilbakestiller passordet.", + "invalidToken": "Innloggingskoblingen din er ugyldig eller har utløpt. Prøv å logge inn igjen.", "CreateAccount": { "title": "Ny kunde?", "accountBenefits": "Opprett en konto hos oss, så kan du:", @@ -198,7 +199,13 @@ "currentPassword": "Nåværende passord", "newPassword": "Nytt passord", "confirmPassword": "Bekreft passord", - "cta": "Oppdater" + "cta": "Oppdater", + "NewsletterSubscription": { + "title": "Markedsføringsinnstillinger", + "label": "Abonner på nyhetsbrevet vårt.", + "marketingPreferencesUpdated": "Markedsføringsinnstillingene er oppdatert!", + "somethingWentWrong": "Noe gikk galt. Prøv igjen senere." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Meld deg på nyhetsbrevet vårt", "placeholder": "Skriv inn epostadressen din", "description": "Hold deg oppdatert med de siste nyhetene og tilbudene fra butikken vår.", - "success": "Takk for interessen! Nyhetsbrevfunksjonen kommer snart!" + "subscribedToNewsletter": "Du har abonnert på nyhetsbrevet vårt.", + "Errors": { + "invalidEmail": "Skriv inn en gyldig e-postadresse.", + "somethingWentWrong": "Noe gikk galt. Prøv igjen senere." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Vi verdsetter personvernet ditt", - "description": "Dette nettstedet bruker informasjonskapsler for å forbedre nettopplevelsen, analysere trafikken på nettstedet, og vise personlig tilpasset innhold." + "description": "Dette nettstedet bruker informasjonskapsler for å forbedre nettopplevelsen, analysere trafikken på nettstedet, og vise personlig tilpasset innhold.", + "privacyPolicy": "Personvernerklæring" }, "Dialog": { "title": "Personverninnstillinger", diff --git a/core/messages/pl.json b/core/messages/pl.json index ebb605dfe9..5c7786997f 100644 --- a/core/messages/pl.json +++ b/core/messages/pl.json @@ -55,6 +55,7 @@ "invalidCredentials": "Twój adres e-mail lub hasło są nieprawidłowe. Spróbuj zalogować się ponownie lub zresetuj hasło", "somethingWentWrong": "Coś poszło nie tak. Proszę spróbować później.", "passwordResetRequired": "Password reset required. Please check your email for instructions to reset your password.", + "invalidToken": "Your login link is invalid or has expired. Please try logging in again.", "CreateAccount": { "title": "Nowy klient?", "accountBenefits": "Załóż u nas konto, a będziesz mógł:", @@ -198,7 +199,13 @@ "currentPassword": "Aktualne hasło", "newPassword": "Nowe hasło", "confirmPassword": "Potwierdź hasło", - "cta": "Aktualizacja" + "cta": "Aktualizacja", + "NewsletterSubscription": { + "title": "Marketing preferences", + "label": "Zapisz się do naszego newslettera.", + "marketingPreferencesUpdated": "Marketing preferences have been updated successfully!", + "somethingWentWrong": "Coś poszło nie tak. Proszę spróbować później." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Zapisz się do naszego newslettera", "placeholder": "Wpisz swój adres e-mail", "description": "Bądź na bieżąco z najnowszymi informacjami i ofertami naszego sklepu.", - "success": "Dziękujemy za Państwa zainteresowanie! Funkcja newslettera wkrótce będzie dostępna!" + "subscribedToNewsletter": "You have been subscribed to our newsletter!", + "Errors": { + "invalidEmail": "Wpisz prawidłowy adres e-mail.", + "somethingWentWrong": "Coś poszło nie tak. Proszę spróbować później." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "We value your privacy", - "description": "This site uses cookies to improve your browsing experience, analyze site traffic, and show personalized content." + "description": "This site uses cookies to improve your browsing experience, analyze site traffic, and show personalized content.", + "privacyPolicy": "Polityka prywatności" }, "Dialog": { "title": "Privacy Settings", diff --git a/core/messages/pt-BR.json b/core/messages/pt-BR.json index 44553769ec..e722a3eb41 100644 --- a/core/messages/pt-BR.json +++ b/core/messages/pt-BR.json @@ -55,6 +55,7 @@ "invalidCredentials": "O endereço de email ou a senha está incorreto. Tente fazer login novamente ou redefinir sua senha", "somethingWentWrong": "Ocorreu um erro. Tente novamente mais tarde.", "passwordResetRequired": "Password reset required. Please check your email for instructions to reset your password.", + "invalidToken": "Your login link is invalid or has expired. Please try logging in again.", "CreateAccount": { "title": "Cliente novo?", "accountBenefits": "Crie uma conta conosco para poder:", @@ -198,7 +199,13 @@ "currentPassword": "Senha atual", "newPassword": "Nova senha", "confirmPassword": "Confirmar senha", - "cta": "Atualizar" + "cta": "Atualizar", + "NewsletterSubscription": { + "title": "Marketing preferences", + "label": "Inscreva-se no nosso boletim informativo.", + "marketingPreferencesUpdated": "Marketing preferences have been updated successfully!", + "somethingWentWrong": "Ocorreu um erro. Tente novamente mais tarde." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Assine nosso boletim informativo", "placeholder": "Digite seu email", "description": "Fique por dentro das últimas novidades e ofertas da nossa loja.", - "success": "Agradecemos o seu interesse! O recurso de newsletter estará disponível em breve!" + "subscribedToNewsletter": "You have been subscribed to our newsletter!", + "Errors": { + "invalidEmail": "Informe um endereço de email válido.", + "somethingWentWrong": "Ocorreu um erro. Tente novamente mais tarde." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "We value your privacy", - "description": "This site uses cookies to improve your browsing experience, analyze site traffic, and show personalized content." + "description": "This site uses cookies to improve your browsing experience, analyze site traffic, and show personalized content.", + "privacyPolicy": "Política de privacidade" }, "Dialog": { "title": "Privacy Settings", diff --git a/core/messages/pt.json b/core/messages/pt.json index 44553769ec..e722a3eb41 100644 --- a/core/messages/pt.json +++ b/core/messages/pt.json @@ -55,6 +55,7 @@ "invalidCredentials": "O endereço de email ou a senha está incorreto. Tente fazer login novamente ou redefinir sua senha", "somethingWentWrong": "Ocorreu um erro. Tente novamente mais tarde.", "passwordResetRequired": "Password reset required. Please check your email for instructions to reset your password.", + "invalidToken": "Your login link is invalid or has expired. Please try logging in again.", "CreateAccount": { "title": "Cliente novo?", "accountBenefits": "Crie uma conta conosco para poder:", @@ -198,7 +199,13 @@ "currentPassword": "Senha atual", "newPassword": "Nova senha", "confirmPassword": "Confirmar senha", - "cta": "Atualizar" + "cta": "Atualizar", + "NewsletterSubscription": { + "title": "Marketing preferences", + "label": "Inscreva-se no nosso boletim informativo.", + "marketingPreferencesUpdated": "Marketing preferences have been updated successfully!", + "somethingWentWrong": "Ocorreu um erro. Tente novamente mais tarde." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Assine nosso boletim informativo", "placeholder": "Digite seu email", "description": "Fique por dentro das últimas novidades e ofertas da nossa loja.", - "success": "Agradecemos o seu interesse! O recurso de newsletter estará disponível em breve!" + "subscribedToNewsletter": "You have been subscribed to our newsletter!", + "Errors": { + "invalidEmail": "Informe um endereço de email válido.", + "somethingWentWrong": "Ocorreu um erro. Tente novamente mais tarde." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "We value your privacy", - "description": "This site uses cookies to improve your browsing experience, analyze site traffic, and show personalized content." + "description": "This site uses cookies to improve your browsing experience, analyze site traffic, and show personalized content.", + "privacyPolicy": "Política de privacidade" }, "Dialog": { "title": "Privacy Settings", diff --git a/core/messages/sv.json b/core/messages/sv.json index d51bfb5d33..6d41c4af4a 100644 --- a/core/messages/sv.json +++ b/core/messages/sv.json @@ -55,6 +55,7 @@ "invalidCredentials": "Din e-postadress eller ditt lösenord är felaktigt. Försök att logga in igen eller återställ ditt lösenord", "somethingWentWrong": "Någonting gick fel. Vänligen försök igen senare.", "passwordResetRequired": "Återställning av lösenord krävs. Kontrollera din e-post för instruktioner om hur du återställer ditt lösenord.", + "invalidToken": "Din inloggningslänk är ogiltig eller har löpt ut. Försök logga in igen.", "CreateAccount": { "title": "Nya kunder?", "accountBenefits": "Skapa ett konto hos oss, så kan du:", @@ -198,7 +199,13 @@ "currentPassword": "Nuvarande lösenord", "newPassword": "Nytt lösenord", "confirmPassword": "Bekräfta lösenord", - "cta": "Uppdatering" + "cta": "Uppdatering", + "NewsletterSubscription": { + "title": "Marknadsföringsinställningar", + "label": "Prenumerera på vårt nyhetsbrev.", + "marketingPreferencesUpdated": "Marknadsföringsinställningarna har uppdaterats framgångsrikt!", + "somethingWentWrong": "Någonting gick fel. Vänligen försök igen senare." + } } }, "Wishlist": { @@ -560,7 +567,11 @@ "title": "Anmäl dig till vårt nyhetsbrev", "placeholder": "Ange din e-postadress", "description": "Håll dig uppdaterad med de senaste nyheterna och erbjudandena från vår butik.", - "success": "Tack för ditt intresse! Funktionen för nyhetsbrev kommer snart!" + "subscribedToNewsletter": "Du prenumererar nu på vårt nyhetsbrev!", + "Errors": { + "invalidEmail": "Vänligen ange en giltig e-postadress", + "somethingWentWrong": "Någonting gick fel. Vänligen försök igen senare." + } }, "ConsentManager": { "Common": { @@ -571,7 +582,8 @@ }, "CookieBanner": { "title": "Vi värdesätter din integritet", - "description": "Denna webbplats använder cookies för att förbättra din surfupplevelse, analysera webbplatstrafik och visa personligt innehåll." + "description": "Denna webbplats använder cookies för att förbättra din surfupplevelse, analysera webbplatstrafik och visa personligt innehåll.", + "privacyPolicy": "Sekretesspolicy" }, "Dialog": { "title": "Sekretessinställningar", diff --git a/core/tests/fixtures/subscribe/index.ts b/core/tests/fixtures/subscribe/index.ts index 4bb2f7af0c..84ba6518b1 100644 --- a/core/tests/fixtures/subscribe/index.ts +++ b/core/tests/fixtures/subscribe/index.ts @@ -7,6 +7,20 @@ export class SubscribeFixture extends Fixture { this.subscribedEmails.push(email); } + async subscribe(email: string, firstName: string, lastName: string): Promise { + this.skipIfReadonly(); + + await this.api.subscribe.subscribe(email, firstName, lastName); + + this.trackSubscription(email); + } + + async unsubscribe(email: string): Promise { + this.skipIfReadonly(); + + await this.api.subscribe.unsubscribe(email); + } + async cleanup(): Promise { this.skipIfReadonly(); diff --git a/core/tests/fixtures/utils/api/customers/http.ts b/core/tests/fixtures/utils/api/customers/http.ts index 9a5ce90a95..856fc4722a 100644 --- a/core/tests/fixtures/utils/api/customers/http.ts +++ b/core/tests/fixtures/utils/api/customers/http.ts @@ -212,7 +212,10 @@ export const customersHttpClient: CustomersApi = { throw new Error(`No customer found with the provided ID: ${customerId}`); } - if (customer.originChannelId !== (testEnv.BIGCOMMERCE_CHANNEL_ID ?? 1)) { + if ( + customer.originChannelId !== (testEnv.BIGCOMMERCE_CHANNEL_ID ?? 1) && + customer.channelIds !== null // if channelIds is null, the customer belongs to all channels + ) { throw new Error( `Customer ${customerId} is not from the correct channel. Expected ${ testEnv.BIGCOMMERCE_CHANNEL_ID ?? 1 diff --git a/core/tests/fixtures/utils/api/subscribe/http.ts b/core/tests/fixtures/utils/api/subscribe/http.ts index e02729828b..b6c6876a43 100644 --- a/core/tests/fixtures/utils/api/subscribe/http.ts +++ b/core/tests/fixtures/utils/api/subscribe/http.ts @@ -3,6 +3,13 @@ import { httpClient } from '../client'; import { SubscribeApi } from '.'; export const subscribeHttpClient: SubscribeApi = { + subscribe: async (email: string, firstName: string, lastName: string) => { + await httpClient.post('/v3/customers/subscribers', { + email, + first_name: firstName, + last_name: lastName, + }); + }, unsubscribe: async (email: string) => { await httpClient.delete(`/v3/customers/subscribers?email=${encodeURIComponent(email)}`); }, diff --git a/core/tests/fixtures/utils/api/subscribe/index.ts b/core/tests/fixtures/utils/api/subscribe/index.ts index 68bd544b07..cfe108c234 100644 --- a/core/tests/fixtures/utils/api/subscribe/index.ts +++ b/core/tests/fixtures/utils/api/subscribe/index.ts @@ -1,4 +1,5 @@ export interface SubscribeApi { + subscribe(email: string, firstName: string, lastName: string): Promise; unsubscribe(email: string): Promise; } diff --git a/core/tests/ui/e2e/account/account-settings.spec.ts b/core/tests/ui/e2e/account/account-settings.spec.ts index 1545115344..ce8dabf08e 100644 --- a/core/tests/ui/e2e/account/account-settings.spec.ts +++ b/core/tests/ui/e2e/account/account-settings.spec.ts @@ -2,6 +2,7 @@ import { faker } from '@faker-js/faker'; import { expect, test } from '~/tests/fixtures'; import { getTranslations } from '~/tests/lib/i18n'; +import { TAGS } from '~/tests/tags'; test('Updating account information works as expected', async ({ page, customer }) => { const t = await getTranslations('Account.Settings'); @@ -18,7 +19,7 @@ test('Updating account information works as expected', async ({ page, customer } // TODO: Account settings form fields need to be translated await expect(page.getByLabel('First Name')).toHaveValue(testCustomer.firstName); await expect(page.getByLabel('Last Name')).toHaveValue(testCustomer.lastName); - await expect(page.getByLabel('Email')).toHaveValue(testCustomer.email); + await expect(page.getByLabel('Email').first()).toHaveValue(testCustomer.email); await page.getByLabel('First Name').fill(updatedFirstName); await page.getByLabel('Last Name').fill(updatedLastName); @@ -61,7 +62,7 @@ test('Changing password works as expected', async ({ page, customer }) => { await page.getByLabel(t('confirmPassword')).fill(newPassword); await page .getByRole('button', { name: t('cta') }) - .last() + .nth(1) // The second button is the update password button .click(); await page.waitForLoadState('networkidle'); @@ -77,3 +78,93 @@ test('Changing password works as expected', async ({ page, customer }) => { await expect(page).toHaveURL('/account/orders/'); }); + +test( + 'Subscribing to newsletter works as expected', + { tag: [TAGS.writesData] }, + async ({ page, customer, subscribe }) => { + const t = await getTranslations('Account.Settings'); + const tNewsletter = await getTranslations('Account.Settings.NewsletterSubscription'); + const testCustomer = await customer.createNewCustomer(); + + // Ensure customer is unsubscribed initially + await subscribe.unsubscribe(testCustomer.email); + + await customer.loginAs(testCustomer); + + await page.goto('/account/settings'); + await expect(page.getByRole('heading', { name: t('title') })).toBeVisible(); + + // Find the newsletter subscription switch + const newsletterSwitch = page.getByLabel(t('NewsletterSubscription.label')); + + await expect(newsletterSwitch).toBeVisible(); + + // Verify switch is unchecked (customer is not subscribed) + const switchElement = page.getByRole('switch', { name: t('NewsletterSubscription.label') }); + + await expect(switchElement).not.toBeChecked(); + + // Click the switch to subscribe + await switchElement.click(); + + // Click the submit button (should be the last button on the page) + await page + .getByRole('button', { name: t('cta') }) + .last() + .click(); + + await page.waitForLoadState('networkidle'); + + // Verify success message appears + await expect(page.getByText(tNewsletter('marketingPreferencesUpdated'))).toBeVisible(); + + // Track subscription for cleanup + subscribe.trackSubscription(testCustomer.email); + }, +); + +test( + 'Unsubscribing from newsletter works as expected', + { tag: [TAGS.writesData] }, + async ({ page, customer, subscribe }) => { + const t = await getTranslations('Account.Settings'); + const tNewsletter = await getTranslations('Account.Settings.NewsletterSubscription'); + const testCustomer = await customer.createNewCustomer(); + + // Ensure customer is subscribed initially + await subscribe.subscribe(testCustomer.email, testCustomer.firstName, testCustomer.lastName); + + await customer.loginAs(testCustomer); + + await page.goto('/account/settings'); + await expect(page.getByRole('heading', { name: t('title') })).toBeVisible(); + + // Find the newsletter subscription switch + const newsletterSwitch = page.getByLabel(t('NewsletterSubscription.label')); + + await expect(newsletterSwitch).toBeVisible(); + + // Verify switch is checked (customer is subscribed) + const switchElement = page.getByRole('switch', { name: t('NewsletterSubscription.label') }); + + await expect(switchElement).toBeChecked(); + + // Click the switch to unsubscribe + await switchElement.click(); + + // Click the submit button (should be the last button on the page) + await page + .getByRole('button', { name: t('cta') }) + .last() + .click(); + + await page.waitForLoadState('networkidle'); + + // Verify success message appears + await expect(page.getByText(tNewsletter('marketingPreferencesUpdated'))).toBeVisible(); + + // Track subscription for cleanup + subscribe.trackSubscription(testCustomer.email); + }, +); diff --git a/core/tests/ui/e2e/auth/login.spec.ts b/core/tests/ui/e2e/auth/login.spec.ts index 8774849c17..fb9df72817 100644 --- a/core/tests/ui/e2e/auth/login.spec.ts +++ b/core/tests/ui/e2e/auth/login.spec.ts @@ -88,3 +88,35 @@ test('JWT login redirects to the specified redirect_to value in the token payloa await page.waitForURL('/account/addresses/'); await expect(page.getByRole('heading', { name: t('title') })).toBeVisible(); }); + +test('JWT login with an invalid/expired token shows an error message', async ({ page }) => { + const t = await getTranslations('Auth.Login'); + + const invalidJwt = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'; + + await page.goto(`/login/token/${invalidJwt}`); + await page.waitForURL('/login/?error=InvalidToken'); + await expect(page.getByText(t('invalidToken'))).toBeVisible(); +}); + +test('After invalid JWT login, manually logging in with invalid credentials will replace the error message displayed', async ({ + page, +}) => { + const t = await getTranslations('Auth.Login'); + + const invalidJwt = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'; + + await page.goto(`/login/token/${invalidJwt}`); + await page.waitForURL('/login/?error=InvalidToken'); + await expect(page.getByText(t('invalidToken'))).toBeVisible(); + + await page.getByLabel(t('email')).fill('invalid-email-testing@testing.com'); + await page.getByLabel(t('password')).fill('invalid-password'); + await page.getByRole('button', { name: t('cta') }).click(); + + await page.waitForURL('/login/'); + await expect(page.getByText(t('invalidToken'))).not.toBeVisible(); + await expect(page.getByText(t('invalidCredentials'))).toBeVisible(); +}); diff --git a/core/tests/ui/e2e/subscribe.spec.ts b/core/tests/ui/e2e/subscribe.spec.ts index 4330417760..273971b736 100644 --- a/core/tests/ui/e2e/subscribe.spec.ts +++ b/core/tests/ui/e2e/subscribe.spec.ts @@ -24,13 +24,14 @@ test( await submitButton.click(); await page.waitForLoadState('networkidle'); - await expect(page.getByText(t('success'))).toBeVisible(); + await expect(page.getByText(t('subscribedToNewsletter'))).toBeVisible(); + // Track that we attempted to subscribe this email subscribe.trackSubscription(email); }, ); -test('Shows error when user tries to subscribe again with the same email', async ({ +test('Shows success message when user tries to subscribe again with the same email', async ({ page, subscribe, }) => { @@ -50,14 +51,14 @@ test('Shows error when user tries to subscribe again with the same email', async await submitButton.click(); await page.waitForLoadState('networkidle'); - await expect(page.getByText(t('success'))).toBeVisible(); + await expect(page.getByText(t('subscribedToNewsletter'))).toBeVisible(); // Try to subscribe again with the same email await emailInput.fill(email); await submitButton.click(); await page.waitForLoadState('networkidle'); - await expect(page.getByText(t('Errors.subcriberAlreadyExists'))).toBeVisible(); + await expect(page.getByText(t('subscribedToNewsletter'))).toBeVisible(); // Track that we attempted to subscribe this email subscribe.trackSubscription(email); diff --git a/core/vibes/soul/sections/account-settings/index.tsx b/core/vibes/soul/sections/account-settings/index.tsx index 583894341d..8c6377d621 100644 --- a/core/vibes/soul/sections/account-settings/index.tsx +++ b/core/vibes/soul/sections/account-settings/index.tsx @@ -1,4 +1,8 @@ import { ChangePasswordAction, ChangePasswordForm } from './change-password-form'; +import { + NewsletterSubscriptionForm, + UpdateNewsletterSubscriptionAction, +} from './newsletter-subscription-form'; import { Account, UpdateAccountAction, UpdateAccountForm } from './update-account-form'; export interface AccountSettingsSectionProps { @@ -12,6 +16,12 @@ export interface AccountSettingsSectionProps { confirmPasswordLabel?: string; currentPasswordLabel?: string; newPasswordLabel?: string; + newsletterSubscriptionEnabled?: boolean; + isAccountSubscribed?: boolean; + newsletterSubscriptionTitle?: string; + newsletterSubscriptionLabel?: string; + newsletterSubscriptionCtaLabel?: string; + updateNewsletterSubscriptionAction?: UpdateNewsletterSubscriptionAction; } // eslint-disable-next-line valid-jsdoc @@ -39,6 +49,12 @@ export function AccountSettingsSection({ confirmPasswordLabel, currentPasswordLabel, newPasswordLabel, + newsletterSubscriptionEnabled = false, + isAccountSubscribed = false, + newsletterSubscriptionTitle = 'Marketing preferences', + newsletterSubscriptionLabel = 'Opt-in to receive emails about new products and promotions.', + newsletterSubscriptionCtaLabel = 'Save preferences', + updateNewsletterSubscriptionAction, }: AccountSettingsSectionProps) { return (
@@ -56,7 +72,7 @@ export function AccountSettingsSection({ submitLabel={updateAccountSubmitLabel} /> -
+

{changePasswordTitle}

@@ -68,6 +84,19 @@ export function AccountSettingsSection({ submitLabel={changePasswordSubmitLabel} />
+ {newsletterSubscriptionEnabled && updateNewsletterSubscriptionAction && ( +
+

+ {newsletterSubscriptionTitle} +

+ +
+ )}
diff --git a/core/vibes/soul/sections/account-settings/newsletter-subscription-form.tsx b/core/vibes/soul/sections/account-settings/newsletter-subscription-form.tsx new file mode 100644 index 0000000000..efc24eb954 --- /dev/null +++ b/core/vibes/soul/sections/account-settings/newsletter-subscription-form.tsx @@ -0,0 +1,71 @@ +'use client'; + +import { SubmissionResult } from '@conform-to/react'; +import { useTranslations } from 'next-intl'; +import { useActionState, useEffect, useState } from 'react'; + +import { Switch } from '@/vibes/soul/form/switch'; +import { Button } from '@/vibes/soul/primitives/button'; +import { toast } from '@/vibes/soul/primitives/toaster'; + +type Action = (state: Awaited, payload: P) => S | Promise; + +interface State { + lastResult: SubmissionResult | null; + successMessage?: string; +} + +export type UpdateNewsletterSubscriptionAction = Action; + +export interface NewsletterSubscriptionFormProps { + action: UpdateNewsletterSubscriptionAction; + isAccountSubscribed: boolean; + label?: string; + ctaLabel?: string; +} + +export function NewsletterSubscriptionForm({ + action, + isAccountSubscribed, + label = 'Opt-in to receive emails about new products and promotions.', + ctaLabel = 'Update', +}: NewsletterSubscriptionFormProps) { + const t = useTranslations('Account.Settings.NewsletterSubscription'); + + const [checked, setChecked] = useState(isAccountSubscribed); + const [state, formAction, isPending] = useActionState(action, { + lastResult: null, + }); + + const onCheckedChange = (value: boolean) => { + setChecked(value); + }; + + useEffect(() => { + if (state.lastResult?.status === 'success' && state.successMessage != null) { + toast.success(state.successMessage); + } + + if (state.lastResult?.error) { + // eslint-disable-next-line no-console + console.log(state.lastResult.error); + toast.error(t('somethingWentWrong')); + } + }, [state, t]); + + return ( +
+ + + + + ); +} diff --git a/core/vibes/soul/sections/cart/client.tsx b/core/vibes/soul/sections/cart/client.tsx index 10e7fd6966..33f08662d1 100644 --- a/core/vibes/soul/sections/cart/client.tsx +++ b/core/vibes/soul/sections/cart/client.tsx @@ -41,6 +41,7 @@ export interface CartLineItem { subtitle: string; quantity: number; price: string; + salePrice?: string; href?: string; } @@ -558,8 +559,13 @@ function CounterForm({
- {lineItem.price} - + {lineItem.salePrice && lineItem.salePrice !== lineItem.price ? ( + + {lineItem.price} {lineItem.salePrice} + + ) : ( + {lineItem.price} + )} {/* Counter */}
-