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

chore(core): fetch checkout url until click #912

Merged
merged 1 commit into from
May 14, 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/lazy-tools-collect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

fetch checkout redirect url when user clicks proceed to checkout button
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use server';

import { redirect } from 'next/navigation';
import { z } from 'zod';

import { client } from '~/client';
import { graphql } from '~/client/graphql';

const CheckoutRedirectMutation = graphql(`
mutation CheckoutRedirectMutation($cartId: String!) {
cart {
createCartRedirectUrls(input: { cartEntityId: $cartId }) {
redirectUrls {
redirectedCheckoutUrl
}
}
}
}
`);

export const redirectToCheckout = async (formData: FormData) => {
const cartId = z.string().parse(formData.get('cartId'));

const { data } = await client.fetch({
document: CheckoutRedirectMutation,
variables: { cartId },
fetchOptions: { cache: 'no-store' },
});

const url = data.cart.createCartRedirectUrls.redirectUrls?.redirectedCheckoutUrl;

if (!url) {
throw new Error('Invalid checkout url.');
}

redirect(url);
};
51 changes: 28 additions & 23 deletions core/app/[locale]/(default)/cart/_components/checkout-button.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
import { client } from '~/client';
import { graphql } from '~/client/graphql';
import { Button } from '~/components/ui/button';
'use client';

import { Loader2 as Spinner } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { useFormStatus } from 'react-dom';

export const CheckoutButtonMutation = graphql(`
mutation CheckoutButtonMutation($cartId: String!) {
cart {
createCartRedirectUrls(input: { cartEntityId: $cartId }) {
redirectUrls {
redirectedCheckoutUrl
}
}
}
}
`);
import { Button } from '~/components/ui/button';

export const CheckoutButton = async ({ cartId, label }: { cartId: string; label: string }) => {
const { data } = await client.fetch({
document: CheckoutButtonMutation,
variables: { cartId },
fetchOptions: { cache: 'no-store' },
});
import { redirectToCheckout } from '../_actions/redirect-to-checkout';

const checkoutUrl = data.cart.createCartRedirectUrls.redirectUrls?.redirectedCheckoutUrl;
const InternalButton = () => {
const t = useTranslations('Cart');
const { pending } = useFormStatus();

return (
<Button asChild className="mt-6">
<a href={checkoutUrl}>{label}</a>
<Button className="mt-6" disabled={pending}>
{pending ? (
<>
<Spinner aria-hidden="true" className="animate-spin text-primary " />
<span className="sr-only">{t('loading')}</span>
</>
) : (
t('proceedToCheckout')
)}
</Button>
);
};

export const CheckoutButton = ({ cartId }: { cartId: string }) => {
return (
<form action={redirectToCheckout}>
<input name="cartId" type="hidden" value={cartId} />
<InternalButton />
</form>
);
};
13 changes: 8 additions & 5 deletions core/app/[locale]/(default)/cart/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { cookies } from 'next/headers';
import { getTranslations } from 'next-intl/server';
import { Suspense } from 'react';
import { NextIntlClientProvider } from 'next-intl';
import { getMessages, getTranslations } from 'next-intl/server';

import { getSessionCustomerId } from '~/auth';
import { client } from '~/client';
Expand Down Expand Up @@ -50,7 +50,10 @@ export default async function CartPage({ params: { locale } }: Props) {
return <EmptyCart locale={locale} />;
}

const messages = await getMessages({ locale });
const Cart = messages.Cart ?? {};
const t = await getTranslations({ locale, namespace: 'Cart' });

const customerId = await getSessionCustomerId();

const { data } = await client.fetch({
Expand Down Expand Up @@ -87,9 +90,9 @@ export default async function CartPage({ params: { locale } }: Props) {
<div className="col-span-1 col-start-2 lg:col-start-3">
{checkout && <CheckoutSummary data={checkout} />}

<Suspense fallback={t('loading')}>
<CheckoutButton cartId={cartId} label={t('proceedToCheckout')} />
</Suspense>
<NextIntlClientProvider locale={locale} messages={{ Cart }}>
<CheckoutButton cartId={cartId} />
</NextIntlClientProvider>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default async function RegisterCustomer({ params: { locale } }: Props) {

return (
<div className="mx-auto mb-10 mt-8 text-base lg:w-2/3">
<h1 className="my-6 my-8 text-4xl font-black lg:my-8 lg:text-5xl">{t('heading')}</h1>
<h1 className="my-6 text-4xl font-black lg:my-8 lg:text-5xl">{t('heading')}</h1>
<NextIntlClientProvider locale={locale} messages={{ Account }}>
<RegisterCustomerForm
addressFields={addressFields}
Expand Down
4 changes: 2 additions & 2 deletions core/tests/ui/desktop/e2e/cart.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ test('Edit product quantity in cart', async ({ page }) => {
await page.getByRole('link', { name: 'Cart Items 1' }).click();

await expect(page.getByRole('heading', { level: 1, name: 'Your cart' })).toBeVisible();
await expect(page.getByRole('link', { name: 'Proceed to checkout' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Proceed to checkout' })).toBeVisible();

await page.getByRole('button', { name: 'Increase count' }).click();

Expand All @@ -37,7 +37,7 @@ test('Proceed to checkout', async ({ page }) => {

await expect(page.getByRole('heading', { level: 1, name: 'Your cart' })).toBeVisible();

await page.getByRole('link', { name: 'Proceed to checkout' }).click();
await page.getByRole('button', { name: 'Proceed to checkout' }).click();

await expect(page.getByRole('heading', { name: 'Order Summary', level: 3 })).toBeVisible();
await expect(page.getByRole('heading', { name: `1 x ${sampleProduct}`, level: 4 })).toBeVisible();
Expand Down
4 changes: 2 additions & 2 deletions core/tests/ui/desktop/e2e/coupon.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ test.beforeEach(async ({ page }) => {
await page.getByRole('link', { name: 'Cart Items 1' }).click();

await expect(page.getByRole('heading', { level: 1, name: 'Your cart' })).toBeVisible();
await expect(page.getByRole('link', { name: 'Proceed to checkout' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Proceed to checkout' })).toBeVisible();

await page.getByRole('button', { name: 'Add' }).nth(1).click();
});
Expand Down Expand Up @@ -46,7 +46,7 @@ test('Coupon code fails', async ({ page }) => {
});

test('Apply coupon on checkout', async ({ page }) => {
await page.getByRole('link', { name: 'Proceed to checkout' }).click();
await page.getByRole('button', { name: 'Proceed to checkout' }).click();

await expect(page.getByRole('link', { name: 'Coupon/Gift Certificate' })).toBeVisible();
await expect(page.getByText('Total (USD) $225.00')).toBeVisible();
Expand Down
2 changes: 1 addition & 1 deletion core/tests/ui/mobile/checkout-experience.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ test('Checkout experience on ios mobile', async ({ page }) => {
await page.getByRole('link', { name: 'Cart Items 1' }).click();
await expect(page.getByRole('heading', { level: 1, name: 'Your cart' })).toBeVisible();

await page.getByRole('link', { name: 'Proceed to checkout' }).click();
await page.getByRole('button', { name: 'Proceed to checkout' }).click();

await page.getByLabel('Email').fill(faker.internet.email());

Expand Down
Loading