Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

fix(vue-demo): double opt in registration #1489

Merged
merged 7 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/funny-chicken-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopware-pwa/composables-next": minor
---

Changed `registration` method in the `useUser` composable. Because of changes in the double opt-in on registration flow in the Shopware backend we are adjusting this method on our side. In new approach we are checking `active` and `doubleOptInRegistration` properties that represents current status of the user.
5 changes: 5 additions & 0 deletions .changeset/light-games-appear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"vue-demo-store": patch
---

Fix registration form for the double opt in registration flow
patzick marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 5 additions & 0 deletions .changeset/young-chicken-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"vue-demo-store": minor
---

Added a confirmation page after the double opt-in option. The confirmation link is included in the email after registration.
25 changes: 25 additions & 0 deletions packages/composables/src/useUser/useUser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,31 @@ describe("useUser", () => {
expect(vm.isLoggedIn).toBe(true);
});

it("register function with double opt-in option should not automatically log user in", async () => {
const { vm, injections } = useSetup(() => useUser());
injections.apiClient.invoke.mockResolvedValue({
data: {
active: true,
doubleOptInRegistration: true,
id: "test123",
guest: false,
},
});

await vm.register(REGISTRATION_DATA);

expect(injections.apiClient.invoke).toHaveBeenCalledWith(
expect.stringContaining("register"),
expect.objectContaining({
body: {
...REGISTRATION_DATA,
storefrontUrl: "http://localhost:3000", // This is the default value from the useInternationalization
},
}),
);
expect(vm.isLoggedIn).toBe(false);
});

it("logout", async () => {
const { vm, injections } = useSetup(() => useUser());
injections.apiClient.invoke.mockResolvedValue({ data: {} });
Expand Down
7 changes: 5 additions & 2 deletions packages/composables/src/useUser/useUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,11 @@ export function useUser(): UseUserReturn {
storefrontUrl: getStorefrontUrl(),
},
});
_user.value = data;
if (_user.value?.active) await refreshSessionContext();
// Update the user data in the context if the user is active and not using double opt-in registration set in the Shopware Admin
if (data.active && !data.doubleOptInRegistration) {
_user.value = data;
}
await refreshSessionContext();
return data;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,13 @@ const invokeSubmit = async () => {
try {
loading.value = true;
const response = await register(state);
if (response?.active) router.push("/");
else if (response && !response.active) {
if (response?.doubleOptInRegistration) {
Object.assign(state, JSON.parse(JSON.stringify(initialState)));
showDoubleOptInBox.value = true;
await nextTick();
doubleOptInBox.value?.scrollIntoView();
$v.value.$reset();
}
} else if (response?.active) router.push("/");
} catch (error) {
if (error instanceof ApiClientError) {
const errors = resolveApiErrors(error.details.errors);
Expand Down Expand Up @@ -147,7 +146,8 @@ useBreadcrumbs([
{{ $t("account.messages.signUpSuccess") }}
</div>
<form
class="w-full relative"
v-if="!showDoubleOptInBox"
class="w-full relative mt-10"
data-testid="registration-form"
@submit.prevent="invokeSubmit"
>
Expand Down
3 changes: 2 additions & 1 deletion templates/vue-demo-store/i18n/de-DE/account.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
"paymentSetSuccessfully": "Standardzahlungsmethode erfolgreich festgelegt",
"signUpSuccess": "Vielen Dank für Ihre Registrierung! Sie erhalten in Kürze eine Bestätigungs-E-Mail. Klicken Sie auf den Link darin, um die Registrierung abzuschließen.",
"paymentMethodChanged": "Zahlungsmethode geändert",
"productsAdded": "Produkte hinzugefügt"
"productsAdded": "Produkte hinzugefügt",
"verifying": "Konto bestätigen"
}
}
}
3 changes: 2 additions & 1 deletion templates/vue-demo-store/i18n/de-DE/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"CHECKOUT__UNKNOWN_PAYMENT_METHOD": "Die ausgewählte Zahlungsmethode existiert nicht.",
"VIOLATION::TOO_SHORT_ERROR": "{field} ist zu kurz.",
"CHECKOUT__ORDER_ORDER_ALREADY_PAID": "Die Bestellung mit der Bestellnummer {orderNumber} wurde bereits bezahlt und kann nicht mehr bearbeitet werden.",
"CHECKOUT__ORDER_ORDER_NOT_FOUND": "Diese Bestellung konnte nicht gefunden werden."
"CHECKOUT__ORDER_ORDER_NOT_FOUND": "Diese Bestellung konnte nicht gefunden werden.",
"CHECKOUT__CUSTOMER_IS_ALREADY_CONFIRMED": "Die E-Mail-Adresse wurde bereits bestätigt oder die URL ist ungültig."
}
}
3 changes: 2 additions & 1 deletion templates/vue-demo-store/i18n/en-GB/account.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@
"paymentSetSuccessfully": "Set default payment method successfully",
"signUpSuccess": "Thank you for signing up! You will receive a confirmation email shortly. Click on the link in it to complete the sign-up.",
"paymentMethodChanged": "Payment method changed successfully",
"productsAdded": "Products added to cart"
"productsAdded": "Products added to cart",
"verifying": "Account confirmation"
}
}
}
3 changes: 2 additions & 1 deletion templates/vue-demo-store/i18n/en-GB/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"CHECKOUT__UNKNOWN_PAYMENT_METHOD": "The selected payment method does not exist.",
"VIOLATION::TOO_SHORT_ERROR": "{field} is too short.",
"CHECKOUT__ORDER_ORDER_ALREADY_PAID": "The order with the order number {orderNumber} was already paid and cannot be edited afterwards.",
"CHECKOUT__ORDER_ORDER_NOT_FOUND": "This order could not be found."
"CHECKOUT__ORDER_ORDER_NOT_FOUND": "This order could not be found.",
"CHECKOUT__CUSTOMER_IS_ALREADY_CONFIRMED": "Either the email address has already been confirmed or the URL is invalid."
}
}
3 changes: 2 additions & 1 deletion templates/vue-demo-store/i18n/pl-PL/account.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
"paymentSetSuccessfully": "Ustawiono domyślną metodę płatności",
"signUpSuccess": "Dziękujemy za rejestrację! Wkrótce otrzymasz e-mail z potwierdzeniem. Kliknij w link w nim zawarty, aby ukończyć rejestrację.",
"paymentMethodChanged": "Metoda płatności została zmieniona",
"productsAdded": "Produkty zostały dodane do koszyka"
"productsAdded": "Produkty zostały dodane do koszyka",
"verifying": "Potwierdzenie konta"
}
}
}
3 changes: 2 additions & 1 deletion templates/vue-demo-store/i18n/pl-PL/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"CHECKOUT__UNKNOWN_PAYMENT_METHOD": "Wybrana metoda płatności nie istnieje.",
"VIOLATION::TOO_SHORT_ERROR": "{field} jest za krótkie.",
"CHECKOUT__ORDER_ORDER_ALREADY_PAID": "Zamówienie o numerze {orderNumber} zostało już opłacone i nie może zostać zrealizowane.",
"CHECKOUT__ORDER_ORDER_NOT_FOUND": "Nie udało się znaleźć tego zamówienia."
"CHECKOUT__ORDER_ORDER_NOT_FOUND": "Nie udało się znaleźć tego zamówienia.",
"CHECKOUT__CUSTOMER_IS_ALREADY_CONFIRMED": "Adres e-mail został już potwierdzony lub URL jest nieprawidłowy."
}
}
57 changes: 57 additions & 0 deletions templates/vue-demo-store/pages/registration/confirm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script setup lang="ts">
import { ApiClientError } from "@shopware/api-client";
import type { ApiError } from "@shopware/api-client";

const { apiClient } = useShopwareContext();
const { refreshSessionContext } = useSessionContext();
const { pushSuccess, pushError } = useNotifications();
const { query } = useRoute();
const router = useRouter();
const { t } = useI18n();
const hash = query.hash;
const em = query.em;
const alreadyConfirmedError = ref(false);

onMounted(async () => {
try {
await apiClient.invoke("registerConfirm post /account/register-confirm", {
body: {
hash: hash as string,
em: em as string,
},
});
await refreshSessionContext();
pushSuccess(t("account.messages.loggedInSuccess"));
router.push({ path: "/" });
} catch (error) {
if (error instanceof ApiClientError) {
if (
error.details.errors[0].code ===
"CHECKOUT__CUSTOMER_IS_ALREADY_CONFIRMED"
) {
alreadyConfirmedError.value = true;
} else {
for (const singleError of error.details.errors) {
if (singleError?.detail) {
pushError(singleError.detail);
}
}
}
}
}
});
</script>
<template>
<div class="flex justify-center items-center my-20">
<div v-if="!alreadyConfirmedError" class="flex flex-row items-center">
<p class="text-15">{{ $t("account.messages.verifying") }}</p>
<div class="ml-10 size-15 i-carbon-circle-dash animate-spin animate-count-infinite animate-duration-2000" />
</div>
<div v-else class="text-red flex items-center gap-5">
<div class="w-10 h-10 i-carbon-error" />
<p class="text-xl">
{{ $t("errors.CHECKOUT__CUSTOMER_IS_ALREADY_CONFIRMED") }}
</p>
</div>
</div>
</template>
Loading