Skip to content

Commit

Permalink
Merge branch 'main' into add-address-mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
bc-alexsaiannyi authored Apr 18, 2024
2 parents 8caa837 + d3cb5bd commit 746d656
Show file tree
Hide file tree
Showing 40 changed files with 957 additions and 203 deletions.
5 changes: 5 additions & 0 deletions .changeset/angry-nails-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

Apply the edge runtime to missing routes.
5 changes: 5 additions & 0 deletions .changeset/bright-plants-lick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

add update customer mutation
5 changes: 5 additions & 0 deletions .changeset/clean-jokes-sneeze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/create-catalyst": minor
---

Adds the `.vscode/settings.json` file pointing to the correct typescript sdk for gql-tada support.
5 changes: 5 additions & 0 deletions .changeset/empty-carrots-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

use LRU cache for DevKvAdapter
5 changes: 5 additions & 0 deletions .changeset/flat-squids-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": minor
---

Add customer addresses tab content
5 changes: 5 additions & 0 deletions .changeset/ninety-hornets-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-core": patch
---

add loading state on item quantity update and remove when quantity equals 0
6 changes: 6 additions & 0 deletions .changeset/thin-pumpkins-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@bigcommerce/components": minor
"@bigcommerce/docs": minor
---

Add dialog component
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ test-results/
playwright-report/
playwright/.cache/
graphql-env.d.ts
.DS_Store
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,5 @@ export default async function Category({ params: { locale, slug }, searchParams
</div>
);
}

export const runtime = 'edge';
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const AccountTabs = ({ children, activeTab, tabs }: Props) => {

return (
<Tabs activationMode="manual" defaultValue={activeTab}>
<TabsList aria-label={t('accountTabsLabel')} className="mb-5 pb-3">
<TabsList aria-label={t('accountTabsLabel')} className="mb-5 pb-3 pt-1">
{tabs.map((tab) => (
<TabsTrigger asChild key={tab} value={tab}>
<Link
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { getLocale, getTranslations } from 'next-intl/server';

import { getCustomerAddresses } from '~/client/queries/get-customer-addresses';

import { Pagination } from '../../../(faceted)/_components/pagination';
import { TabType } from '../layout';
import { tabHeading } from '../page';

import { AddressesList } from './addresses-list';

type CustomerAddresses = NonNullable<Awaited<ReturnType<typeof getCustomerAddresses>>>;

interface Props {
addresses: CustomerAddresses['addresses'];
pageInfo: CustomerAddresses['pageInfo'];
title: TabType;
}

export const AddressesContent = async ({ addresses, pageInfo, title }: Props) => {
const locale = await getLocale();
const tPagination = await getTranslations({ locale, namespace: 'Pagination' });
const { hasNextPage, hasPreviousPage, startCursor, endCursor } = pageInfo;

return (
<>
{tabHeading(title, locale)}
<AddressesList customerAddressBook={addresses} />
<Pagination
endCursor={endCursor}
hasNextPage={hasNextPage}
hasPreviousPage={hasPreviousPage}
nextLabel={tPagination('next')}
prevLabel={tPagination('prev')}
startCursor={startCursor}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Button } from '@bigcommerce/components/button';
import { useTranslations } from 'next-intl';

import { getCustomerAddresses } from '~/client/queries/get-customer-addresses';
import { Link } from '~/components/link';

type Addresses = NonNullable<Awaited<ReturnType<typeof getCustomerAddresses>>>['addresses'];

interface Props {
customerAddressBook: Addresses;
}

const AddressChangeButtons = () => {
const t = useTranslations('Account.Addresses');

return (
<div className="my-2 flex w-fit gap-x-2 divide-y-0">
<Button aria-label={t('editButton')} variant="secondary">
{t('editButton')}
</Button>
<Button aria-label={t('deleteButton')} variant="subtle">
{t('deleteButton')}
</Button>
</div>
);
};

export const AddressesList = ({ customerAddressBook }: Props) => {
const t = useTranslations('Account.Addresses');

return (
<ul className="mb-12">
{customerAddressBook.map(
({
entityId,
firstName,
lastName,
address1,
address2,
city,
stateOrProvince,
postalCode,
countryCode,
}) => (
<li
className="flex w-full border-collapse flex-col justify-start gap-2 border-t border-gray-200 pb-3 pt-5"
key={entityId}
>
<div className="inline-flex flex-col justify-start text-base">
<p>
{firstName} {lastName}
</p>
<p>{address1}</p>
{Boolean(address2) && <p>{address2}</p>}
<p>
{city}, {stateOrProvince} {postalCode}
</p>
<p>{countryCode}</p>
</div>
<AddressChangeButtons />
</li>
),
)}
<li className="flex w-full border-collapse flex-col justify-start gap-2 border-t border-gray-200 pt-8">
<Button aria-label={t('addNewAddress')} asChild className="w-fit hover:text-white">
<Link href="/account/add-new-address">{t('addNewAddress')}</Link>
</Button>
</li>
</ul>
);
};
30 changes: 26 additions & 4 deletions apps/core/app/[locale]/(default)/account/[tab]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { getTranslations } from 'next-intl/server';

import { getCustomerAddresses } from '~/client/queries/get-customer-addresses';
import { LocaleType } from '~/i18n';

import { AddressesContent } from './_components/addresses-content';
import { TabType } from './layout';

interface Props {
params: {
locale: LocaleType;
tab: TabType;
};
searchParams: {
[key: string]: string | string[] | undefined;
before?: string;
after?: string;
};
}

export async function generateMetadata({ params: { tab, locale } }: Props): Promise<Metadata> {
Expand All @@ -21,22 +28,36 @@ export async function generateMetadata({ params: { tab, locale } }: Props): Prom
};
}

const tabHeading = async (heading: string, locale: LocaleType) => {
const tabHeading = async (heading: string, locale: string) => {
const t = await getTranslations({ locale, namespace: 'Account.Home' });

return <h2 className="mb-8 text-3xl font-black lg:text-4xl">{t(heading)}</h2>;
};

export default async function AccountTabPage({ params: { tab, locale } }: Props) {
export default async function AccountTabPage({ params: { tab, locale }, searchParams }: Props) {
switch (tab) {
case 'orders':
return tabHeading(tab, locale);

case 'messages':
return tabHeading(tab, locale);

case 'addresses':
return tabHeading(tab, locale);
case 'addresses': {
const { before, after } = searchParams;
const customerAddressesDetails = await getCustomerAddresses({
...(after && { after }),
...(before && { before }),
limit: 2,
});

if (!customerAddressesDetails) {
notFound();
}

const { addresses, pageInfo } = customerAddressesDetails;

return <AddressesContent addresses={addresses} pageInfo={pageInfo} title={tab} />;
}

case 'wishlists':
return tabHeading(tab, locale);
Expand All @@ -52,4 +73,5 @@ export default async function AccountTabPage({ params: { tab, locale } }: Props)
}
}

export { tabHeading };
export const runtime = 'edge';
2 changes: 2 additions & 0 deletions apps/core/app/[locale]/(default)/account/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,5 @@ export default async function AccountPage({ params: { locale } }: Props) {
</div>
);
}

export const runtime = 'edge';
45 changes: 45 additions & 0 deletions apps/core/app/[locale]/(default)/cart/_actions/remove-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use server';

import { revalidateTag } from 'next/cache';
import { cookies } from 'next/headers';

import { graphql } from '~/client/graphql';
import { deleteCartLineItem } from '~/client/mutations/delete-cart-line-item';

type DeleteCartLineItemInput = ReturnType<typeof graphql.scalar<'DeleteCartLineItemInput'>>;

export async function removeItem({
lineItemEntityId,
}: Omit<DeleteCartLineItemInput, 'cartEntityId'>) {
try {
const cartId = cookies().get('cartId')?.value;

if (!cartId) {
return { status: 'error', error: 'No cartId cookie found' };
}

if (!lineItemEntityId) {
return { status: 'error', error: 'No lineItemEntityId found' };
}

const updatedCart = await deleteCartLineItem(cartId, lineItemEntityId);

// If we remove the last item in a cart the cart is deleted
// so we need to remove the cartId cookie and clear shipping data
if (!updatedCart) {
cookies().delete('cartId');
cookies().delete('shippingCosts');
revalidateTag('cart');
}

revalidateTag('cart');

return { status: 'success', data: updatedCart };
} catch (e: unknown) {
if (e instanceof Error) {
return { status: 'error', error: e.message };
}

return { status: 'error' };
}
}
32 changes: 0 additions & 32 deletions apps/core/app/[locale]/(default)/cart/_actions/remove-products.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
'use server';

import { revalidatePath } from 'next/cache';
import { cookies } from 'next/headers';

import { graphql } from '~/client/graphql';
import { updateCartLineItem } from '~/client/mutations/update-cart-line-item';

import { removeItem } from './remove-item';

type CartLineItemInput = ReturnType<typeof graphql.scalar<'CartLineItemInput'>>;
type UpdateCartLineItemInput = ReturnType<typeof graphql.scalar<'UpdateCartLineItemInput'>>;

interface UpdateProductQuantityParams extends CartLineItemInput {
lineItemEntityId: UpdateCartLineItemInput['lineItemEntityId'];
}

export async function updateItemQuantity({
lineItemEntityId,
productEntityId,
quantity,
variantEntityId,
selectedOptions,
}: UpdateProductQuantityParams) {
try {
const cartId = cookies().get('cartId')?.value;

if (!cartId) {
return { status: 'error', error: 'No cartId cookie found' };
}

if (!lineItemEntityId) {
return { status: 'error', error: 'No lineItemEntityId found' };
}

if (quantity === 0) {
const result = await removeItem({ lineItemEntityId });

return result;
}

const cartLineItemData = Object.assign(
{ quantity, productEntityId },
variantEntityId && { variantEntityId },
selectedOptions && { selectedOptions },
);

const updatedCart = await updateCartLineItem(cartId, lineItemEntityId, {
lineItem: cartLineItemData,
});

if (!updatedCart) {
return { status: 'error', error: 'Failed to change product quantity in Cart' };
}

revalidatePath('/cart');

return { status: 'success', data: updatedCart };
} catch (e: unknown) {
if (e instanceof Error) {
return { status: 'error', error: e.message };
}

return { status: 'error' };
}
}
Loading

0 comments on commit 746d656

Please sign in to comment.