checkpoint for currency and variant selection

This commit is contained in:
Jieren Chen 2024-09-08 13:07:01 -04:00
parent 5812416bd3
commit 34009d2600
No known key found for this signature in database
GPG Key ID: 2FF322D21B5DB10B
9 changed files with 71 additions and 31 deletions

View File

@ -39,7 +39,7 @@ export const metadata = {
export default async function RootLayout({ children }: { children: ReactNode }) {
const cartId = cookies().get('cartId')?.value;
// Don't await the fetch, pass the Promise to the context provider
const cart = getCart(cartId);
const cart = getCart(cartId, 'USD');
return (
<html lang="en" className={GeistSans.variable}>

View File

@ -9,11 +9,13 @@ export const metadata = {
}
};
export default function HomePage() {
export default function HomePage({ searchParams }: { searchParams: { currency?: string } }) {
const currency = searchParams.currency || 'USD';
return (
<>
<ThreeItemGrid />
<Carousel />
<ThreeItemGrid currency={currency} />
<Carousel currency={currency}/>
<Footer />
</>
);

View File

@ -17,7 +17,7 @@ export async function generateMetadata({
}: {
params: { handle: string };
}): Promise<Metadata> {
const product = await getProduct(params.handle);
const product = await getProduct({ handle: params.handle, currency: 'USD' });
if (!product) return notFound();
@ -50,8 +50,12 @@ export async function generateMetadata({
};
}
export default async function ProductPage({ params }: { params: { handle: string } }) {
const product = await getProduct(params.handle);
export default async function ProductPage({ params, searchParams }: { params: { handle: string }, searchParams: { currency?: string } }) {
const currency = searchParams.currency || 'USD';
const product = await getProduct({
handle: params.handle,
currency: searchParams.currency || 'USD'
});
if (!product) return notFound();

View File

@ -2,9 +2,12 @@ import { getCollectionProducts } from 'lib/fourthwall';
import Link from 'next/link';
import { GridTileImage } from './grid/tile';
export async function Carousel() {
export async function Carousel({currency}: {currency: string}) {
// Collections that start with `hidden-*` are hidden from the search page.
const products = await getCollectionProducts({ collection: process.env.FW_COLLECTION || '' });
const products = await getCollectionProducts({
collection: process.env.FW_COLLECTION || '',
currency,
});
if (!products?.length) return null;
@ -19,7 +22,7 @@ export async function Carousel() {
key={`${product.handle}${i}`}
className="relative aspect-square h-[30vh] max-h-[275px] w-2/3 max-w-[475px] flex-none md:w-1/3"
>
<Link href={`/product/${product.handle}`} className="relative h-full w-full">
<Link href={`/product/${product.handle}?currency=${currency}`} className="relative h-full w-full">
<GridTileImage
alt={product.title}
label={{

View File

@ -5,10 +5,12 @@ import Link from 'next/link';
function ThreeItemGridItem({
item,
currency,
size,
priority
}: {
item: Product;
currency: string;
size: 'full' | 'half';
priority?: boolean;
}) {
@ -18,7 +20,7 @@ function ThreeItemGridItem({
>
<Link
className="relative block aspect-square h-full w-full"
href={`/product/${item.handle}`}
href={`/product/${item.handle}?currency=${currency}`}
prefetch={true}
>
<GridTileImage
@ -41,10 +43,11 @@ function ThreeItemGridItem({
);
}
export async function ThreeItemGrid() {
export async function ThreeItemGrid({ currency } : { currency: string }) {
// Collections that start with `hidden-*` are hidden from the search page.
const homepageItems = await getCollectionProducts({
collection: process.env.FW_COLLECTION || '',
currency
});
if (!homepageItems[0] || !homepageItems[1] || !homepageItems[2]) return null;
@ -53,9 +56,9 @@ export async function ThreeItemGrid() {
return (
<section className="mx-auto grid max-w-screen-2xl gap-4 px-4 pb-4 md:grid-cols-6 md:grid-rows-2 lg:max-h-[calc(100vh-200px)]">
<ThreeItemGridItem size="full" item={firstProduct} priority={true} />
<ThreeItemGridItem size="half" item={secondProduct} priority={true} />
<ThreeItemGridItem size="half" item={thirdProduct} />
<ThreeItemGridItem size="full" item={firstProduct} priority={true} currency={currency}/>
<ThreeItemGridItem size="half" item={secondProduct} priority={true} currency={currency}/>
<ThreeItemGridItem size="half" item={thirdProduct} currency={currency}/>
</section>
);
}

View File

@ -53,6 +53,9 @@ export async function Navbar() {
</Suspense>
</div>
<div className="flex justify-end md:w-1/3">
<div className="relative flex h-11 w-11 items-center justify-center rounded-md border border-neutral-200 text-black transition-colors dark:border-neutral-700 dark:text-white">
USD
</div>
<CartModal />
</div>
</div>

View File

@ -65,19 +65,23 @@ async function fourthwallPost<T>(url: string, data: any, options: RequestInit =
*/
export async function getCollectionProducts({
collection,
currency,
reverse,
sortKey
}: {
collection: string;
currency: string;
reverse?: boolean;
sortKey?: string;
}): Promise<Product[]> {
const res = await fourthwallGet<{results: FourthwallProduct[]}>(`${process.env.FW_URL}/api/public/v1.0/collections/${collection}/products?secret=${process.env.FW_SECRET}`, {
const res = await fourthwallGet<{results: FourthwallProduct[]}>(`${process.env.FW_URL}/api/public/v1.0/collections/${collection}/products?secret=${process.env.FW_SECRET}&currency=${currency}`, {
headers: {
'X-ShopId': process.env.FW_SHOPID || ''
}
});
console.warn(JSON.stringify(res.body.results, null, 2));
if (!res.body.results) {
console.log(`No collection found for \`${collection}\``);
return [];
@ -90,9 +94,9 @@ export async function getCollectionProducts({
/**
* Product operations
*/
export async function getProduct(handle: string): Promise<Product | undefined> {
export async function getProduct({ handle, currency } : { handle: string, currency: string }): Promise<Product | undefined> {
// TODO: replace with real URL
const res = await fourthwallGet<{results: FourthwallProduct[]}>(`${process.env.FW_URL}/api/public/v1.0/collections/${process.env.FW_COLLECTION}/products?secret=${process.env.FW_SECRET}`, {
const res = await fourthwallGet<{results: FourthwallProduct[]}>(`${process.env.FW_URL}/api/public/v1.0/collections/${process.env.FW_COLLECTION}/products?secret=${process.env.FW_SECRET}&currency=${currency}`, {
headers: {
'X-ShopId': process.env.FW_SHOPID || ''
}
@ -111,7 +115,7 @@ export async function getProductRecommendations(productId: string): Promise<Prod
/**
* Cart operations
*/
export async function getCart(cartId: string | undefined): Promise<Cart | undefined> {
export async function getCart(cartId: string | undefined, currency: string): Promise<Cart | undefined> {
if (!cartId) {
return undefined;
}

View File

@ -15,7 +15,7 @@ const DEFAULT_IMAGE: Image = {
const reshapeMoney = (money: FourthwallMoney): Money => {
return {
amount: money.value.toString(),
currencyCode: money.currencyCode
currencyCode: money.currency
};
}
@ -48,7 +48,10 @@ export const reshapeProduct = (product: FourthwallProduct): Product | undefined
const minPrice = Math.min(...variants.map((v) => v.unitPrice.value));
const maxPrice = Math.max(...variants.map((v) => v.unitPrice.value));
const currencyCode = variants[0]?.unitPrice.currencyCode || 'USD';
const currencyCode = variants[0]?.unitPrice.currency || 'USD';
const sizes = new Set(variants.map((v) => v.attributes.size.name));
const colors = new Set(variants.map((v) => v.attributes.color.name));
return {
...rest,
@ -69,9 +72,17 @@ export const reshapeProduct = (product: FourthwallProduct): Product | undefined
}
},
featuredImage: reshapeImages(images, product.name)[0] || DEFAULT_IMAGE,
options: [{
id: 'color',
name: 'Color',
values: [...colors]
}, {
id: 'size',
name: 'Size',
values: [...sizes]
}],
// TODO: stubbed out
availableForSale: true,
options: [],
seo: {
title: product.name,
description: product.description,
@ -96,7 +107,13 @@ const reshapeVariants = (variants: FourthwallProductVariant[]): ProductVariant[]
id: v.id,
title: v.name,
availableForSale: true,
selectedOptions: [],
selectedOptions: [{
name: 'Size',
value: v.attributes.size.name
}, {
name: 'Color',
value: v.attributes.color.name
}],
price: reshapeMoney(v.unitPrice),
}))
}
@ -134,7 +151,7 @@ const reshapeCartItem = (item: FourthwallCartItem): CartItem => {
export const reshapeCart = (cart: FourthwallCart): Cart => {
const totalValue = cart.items.map((item) => item.quantity * item.variant.unitPrice.value).reduce((a, b) => a + b, 0);
const currencyCode = cart.items[0]?.variant.unitPrice.currencyCode || 'USD';
const currencyCode = cart.items[0]?.variant.unitPrice.currency || 'USD';
return {
...cart,

View File

@ -1,6 +1,6 @@
export type FourthwallMoney = {
value: number;
currencyCode: string;
currency: string;
}
export type FourthwallProduct = {
@ -29,17 +29,21 @@ export type FourthwallProductVariant = {
images: FourthwallProductImage[];
// other attr
attributes: {
description: string;
color: {
name: string;
swatch: string;
},
size: {
name: string;
};
}
};
export type FourthwallCart = {
id: string | undefined;
// checkoutUrl: string;
// cost: {
// subtotalAmount: Money;
// totalAmount: Money;
// totalTaxAmount: Money;
// };
items: FourthwallCartItem[];
};