mirror of
https://github.com/vercel/commerce.git
synced 2025-05-19 07:56:59 +00:00
fix: Add to cart buttons on homepage
This commit is contained in:
parent
f43226198d
commit
d9838f8807
@ -25,5 +25,5 @@ button {
|
||||
}
|
||||
|
||||
[lang='ja'] .font-multilingual {
|
||||
@apply font-japan;
|
||||
@apply font-japan tracking-widest;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { ThreeItemGrid } from 'components/grid/three-items';
|
||||
import Footer from 'components/layout/footer';
|
||||
import { SupportedLocale } from 'components/layout/navbar/language-control';
|
||||
|
||||
@ -11,6 +10,7 @@ import HomeImage006 from '@images/home-images/home-image-006.webp';
|
||||
import HomeImage007 from '@images/home-images/home-image-007.webp';
|
||||
import HomeImage008 from '@images/home-images/home-image-008.webp';
|
||||
import clsx from 'clsx';
|
||||
import { HomepageProducts } from 'components/grid/homepage-products';
|
||||
import AboutNaraiPreview from 'components/layout/about-narai-preview';
|
||||
import ConceptPreview from 'components/layout/concept-preview';
|
||||
import LocationPreview from 'components/layout/location-preview';
|
||||
@ -52,7 +52,7 @@ export default async function HomePage({
|
||||
<div>
|
||||
<Navbar cart={cart} locale={locale} />
|
||||
<div className="pt-12 md:pt-48">
|
||||
<ThreeItemGrid lang={locale} />
|
||||
<HomepageProducts lang={locale} />
|
||||
</div>
|
||||
<div className="py-48">
|
||||
<NewsletterSignup />
|
||||
|
75
components/cart/inline-add-to-cart.tsx
Normal file
75
components/cart/inline-add-to-cart.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { addItem } from 'components/cart/actions';
|
||||
import LoadingDots from 'components/loading-dots';
|
||||
import { ProductVariant } from 'lib/shopify/types';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useTransition } from 'react';
|
||||
|
||||
export function InlineAddToCart({
|
||||
variants,
|
||||
availableForSale
|
||||
}: {
|
||||
variants: ProductVariant[];
|
||||
availableForSale: boolean;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const t = useTranslations('Index');
|
||||
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const defaultVariantId = variants.length === 1 ? variants[0]?.id : undefined;
|
||||
const variant = variants.find((variant: ProductVariant) =>
|
||||
variant.selectedOptions.every(
|
||||
(option) => option.value === searchParams.get(option.name.toLowerCase())
|
||||
)
|
||||
);
|
||||
const selectedVariantId = variant?.id || defaultVariantId;
|
||||
const title = !availableForSale
|
||||
? 'Out of stock'
|
||||
: !selectedVariantId
|
||||
? 'Please select options'
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<button
|
||||
aria-label="Add item to cart"
|
||||
disabled={isPending || !availableForSale || !selectedVariantId}
|
||||
title={title}
|
||||
onClick={() => {
|
||||
// Safeguard in case someone messes with `disabled` in devtools.
|
||||
if (!availableForSale || !selectedVariantId) return;
|
||||
|
||||
startTransition(async () => {
|
||||
const error = await addItem(selectedVariantId);
|
||||
|
||||
if (error) {
|
||||
// Trigger the error boundary in the root error.js
|
||||
throw new Error(error.toString());
|
||||
}
|
||||
|
||||
router.refresh();
|
||||
});
|
||||
}}
|
||||
className={clsx(
|
||||
'p-4',
|
||||
'relative flex w-full items-center justify-center',
|
||||
'border border-white/20 hover:border-white',
|
||||
'font-serif text-base tracking-wider text-white',
|
||||
'transition-colors duration-150',
|
||||
{
|
||||
'cursor-not-allowed opacity-60 hover:opacity-60': !availableForSale || !selectedVariantId,
|
||||
'cursor-not-allowed': isPending
|
||||
}
|
||||
)}
|
||||
>
|
||||
{!isPending ? (
|
||||
<span>{availableForSale ? t('cart.add') : t('cart.out-of-stock')}</span>
|
||||
) : (
|
||||
<LoadingDots className="my-3 bg-white" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import { InlineAddToCart } from 'components/cart/inline-add-to-cart';
|
||||
import { SupportedLocale } from 'components/layout/navbar/language-control';
|
||||
import { getCollectionProducts } from 'lib/shopify';
|
||||
import type { Product } from 'lib/shopify/types';
|
||||
@ -6,13 +7,17 @@ import Link from 'next/link';
|
||||
import Label from '../label';
|
||||
import { GridTileImage } from './tile';
|
||||
|
||||
function ThreeItemGridItem({ item, priority }: { item: Product; priority?: boolean }) {
|
||||
function HomepageProductsItem({ item, priority }: { item: Product; priority?: boolean }) {
|
||||
const size = item?.variants?.[0]?.selectedOptions?.find((option) => option.name === 'Size');
|
||||
const image = item?.variants?.[0]?.image;
|
||||
|
||||
return !!image ? (
|
||||
<div className={clsx('col-span-1 row-span-1 md:col-span-2 md:row-span-1')}>
|
||||
<Link className="w-full bg-black/30" href={`/product/${item.handle}`}>
|
||||
<div
|
||||
className={clsx(
|
||||
'col-span-1 row-span-1 flex flex-col justify-between space-y-6 md:col-span-2 md:row-span-1'
|
||||
)}
|
||||
>
|
||||
<Link className="block w-full" href={`/product/${item.handle}`}>
|
||||
<div className="relative block aspect-tall overflow-hidden ">
|
||||
<GridTileImage
|
||||
src={image?.url}
|
||||
@ -32,11 +37,12 @@ function ThreeItemGridItem({ item, priority }: { item: Product; priority?: boole
|
||||
<div className="line-clamp-4 pt-2 font-extralight">{item?.summary?.value}</div>
|
||||
</div>
|
||||
</Link>
|
||||
<InlineAddToCart variants={item.variants} availableForSale={item.availableForSale} />
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
|
||||
export async function ThreeItemGrid({ lang }: { lang?: SupportedLocale }) {
|
||||
export async function HomepageProducts({ lang }: { lang?: SupportedLocale }) {
|
||||
// Collections that start with `hidden-*` are hidden from the search page.
|
||||
const homepageItems = await getCollectionProducts({
|
||||
collection: 'hidden-homepage-featured-items',
|
||||
@ -50,14 +56,14 @@ export async function ThreeItemGrid({ lang }: { lang?: SupportedLocale }) {
|
||||
return (
|
||||
<section
|
||||
className={clsx(
|
||||
'mx-auto grid max-w-screen-xl gap-6 px-4 pb-4 ',
|
||||
'mx-auto grid max-w-screen-xl gap-12 px-4 pb-4 ',
|
||||
'grid-cols-1 md:grid-cols-6',
|
||||
'grid-rows-3 md:grid-rows-1'
|
||||
)}
|
||||
>
|
||||
<ThreeItemGridItem item={firstProduct} priority={true} />
|
||||
<ThreeItemGridItem item={secondProduct} priority={true} />
|
||||
<ThreeItemGridItem item={thirdProduct} />
|
||||
<HomepageProductsItem item={firstProduct} priority={true} />
|
||||
<HomepageProductsItem item={secondProduct} priority={true} />
|
||||
<HomepageProductsItem item={thirdProduct} />
|
||||
</section>
|
||||
);
|
||||
}
|
@ -15,7 +15,9 @@ const Label = ({
|
||||
return (
|
||||
<div className={clsx('@container/label')}>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<h3 className="mr-4 line-clamp-2 flex-grow font-serif text-3xl md:text-4xl">{title}</h3>
|
||||
<h3 className="mr-4 line-clamp-2 flex-grow font-serif text-3xl tracking-wider md:text-4xl">
|
||||
{title}
|
||||
</h3>
|
||||
<div className="font-multilingual flex flex-row items-center space-x-2">
|
||||
<Price
|
||||
className="flex-none"
|
||||
|
@ -6,12 +6,10 @@ import Link from 'next/link';
|
||||
export default function Shoplist() {
|
||||
const t = useTranslations('Index');
|
||||
return (
|
||||
<div className="mx-auto max-w-screen-xl space-y-4 px-6" id="shops">
|
||||
<div className="font-multilingual mx-auto max-w-screen-xl space-y-4 px-6" id="shops">
|
||||
<div className="flex w-full flex-row items-baseline space-x-12 pb-6">
|
||||
<h2 className="font-serif text-6xl tracking-wider">shop list</h2>
|
||||
<h3 className="font-multilingual text-2xl font-extralight tracking-wider">
|
||||
{t('shops.subtitle')}
|
||||
</h3>
|
||||
<h3 className="text-2xl font-extralight tracking-wider">{t('shops.subtitle')}</h3>
|
||||
</div>
|
||||
<div className="grid w-full grid-cols-2 gap-px">
|
||||
<Link
|
||||
|
@ -71,8 +71,8 @@
|
||||
}
|
||||
},
|
||||
"cart": {
|
||||
"add": "Add to cart",
|
||||
"out-of-stock": "Out of stock",
|
||||
"add": "add to cart",
|
||||
"out-of-stock": "out of stock",
|
||||
"title": "Shopping Bag",
|
||||
"subtitle": "Review your Order",
|
||||
"empty": "Your shopping bag is empty",
|
||||
@ -92,7 +92,7 @@
|
||||
"editNote": "Edit note",
|
||||
"hideNote": "Hide",
|
||||
"saving": "Saving...",
|
||||
"addFeaturedProduct": "+ Add"
|
||||
"addFeaturedProduct": "+ add"
|
||||
},
|
||||
"age-gate": {
|
||||
"title": "Confirm Your Age",
|
||||
|
Loading…
x
Reference in New Issue
Block a user