mirror of
https://github.com/vercel/commerce.git
synced 2025-05-29 20:56:58 +00:00
checkpoint for currency and variant selection
This commit is contained in:
parent
5812416bd3
commit
34009d2600
@ -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}>
|
||||
|
@ -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 />
|
||||
</>
|
||||
);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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={{
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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}¤cy=${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}¤cy=${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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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[];
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user