mirror of
https://github.com/vercel/commerce.git
synced 2025-05-19 16:07:01 +00:00
feat(poc): changes for new design
This commit is contained in:
parent
82c0fb8813
commit
5ad4654c55
@ -8,7 +8,7 @@ import { Gallery } from 'components/product/gallery';
|
||||
import { ProductDescription } from 'components/product/product-description';
|
||||
import { HIDDEN_PRODUCT_TAG } from 'lib/constants';
|
||||
import { getProduct, getProductRecommendations } from 'lib/shopware';
|
||||
import { Image } from 'lib/shopify/types';
|
||||
import { Image } from 'lib/shopware/types';
|
||||
import Link from 'next/link';
|
||||
|
||||
export const runtime = 'edge';
|
||||
@ -132,8 +132,8 @@ async function RelatedProducts({ id }: { id: string }) {
|
||||
currencyCode: product.priceRange.maxVariantPrice.currencyCode
|
||||
}}
|
||||
src={product.featuredImage?.url}
|
||||
width={600}
|
||||
height={600}
|
||||
width={500}
|
||||
height={500}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
|
@ -58,13 +58,18 @@ export default async function CategoryPage({
|
||||
<Grid className="grid-cols-2 lg:grid-cols-3">
|
||||
<ProductGridItems products={products} />
|
||||
</Grid>
|
||||
<nav aria-label="Collection pagination" className="block items-center sm:flex">
|
||||
<Pagination
|
||||
itemsPerPage={limit}
|
||||
itemsTotal={total}
|
||||
currentPage={page ? parseInt(page) - 1 : 0}
|
||||
/>
|
||||
</nav>
|
||||
{total > limit ? (
|
||||
<nav
|
||||
aria-label="Collection pagination"
|
||||
className="mb-2 mt-4 block items-center sm:flex"
|
||||
>
|
||||
<Pagination
|
||||
itemsPerPage={limit}
|
||||
itemsTotal={total}
|
||||
currentPage={page ? parseInt(page) - 1 : 0}
|
||||
/>
|
||||
</nav>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="order-none flex-none md:order-last md:w-[125px]">
|
||||
<FilterList list={sorting} title="Sort by" />
|
||||
|
@ -25,7 +25,7 @@ export default async function SearchPage({
|
||||
return (
|
||||
<>
|
||||
{searchValue && products.length === 0 ? (
|
||||
<div className="mx-auto flex max-w-7xl flex-col bg-white py-6 text-black dark:bg-black dark:text-white md:flex-row">
|
||||
<div className="mx-auto flex max-w-screen-2xl flex-col gap-8 px-4 pb-4 text-black dark:text-white md:flex-row">
|
||||
<p>
|
||||
{'There are no products that match '}
|
||||
<span className="font-bold">"{searchValue}"</span>
|
||||
@ -33,20 +33,22 @@ export default async function SearchPage({
|
||||
</div>
|
||||
) : null}
|
||||
{products.length > 0 ? (
|
||||
<div className="mx-auto flex max-w-7xl flex-col bg-white py-6 text-black dark:bg-black dark:text-white md:flex-row">
|
||||
<div className="order-first flex-none md:w-1/6">
|
||||
<div className="mx-auto flex max-w-screen-2xl flex-col gap-8 px-4 pb-4 text-black dark:text-white md:flex-row">
|
||||
<div className="order-first w-full flex-none md:max-w-[125px]">
|
||||
{searchValue ? (
|
||||
<p>
|
||||
<p className="text-sm text-neutral-500">
|
||||
{`Showing ${products.length} ${resultsText} for `}
|
||||
<span className="font-bold">"{searchValue}"</span>
|
||||
</p>
|
||||
) : null}
|
||||
<p className="pt-4">Good place to add other suggest search terms ;)</p>
|
||||
<p className="pt-4 text-xs text-neutral-500">
|
||||
Good place to add other suggested search terms ;)
|
||||
</p>
|
||||
</div>
|
||||
<Grid className="grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<Grid className="grid-cols-2 lg:grid-cols-3">
|
||||
<ProductGridItems products={products} />
|
||||
</Grid>
|
||||
<div className="order-none md:order-last md:w-1/6 md:flex-none">
|
||||
<div className="order-none flex-none md:order-last md:w-[125px]">
|
||||
<FilterList list={sorting} title="Sort by" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import ReactPaginate from 'react-paginate';
|
||||
import { ArrowLeftIcon } from '@heroicons/react/24/outline';
|
||||
import { ArrowRightIcon } from '@heroicons/react/24/outline';
|
||||
import { createUrl } from 'lib/utils';
|
||||
import { usePathname, useSearchParams, useRouter } from 'next/navigation';
|
||||
|
||||
@ -52,18 +54,18 @@ export default function Pagination({
|
||||
initialPage={currentPage}
|
||||
pageCount={pageCount}
|
||||
breakLabel="..."
|
||||
nextLabel=">>"
|
||||
previousLabel="<<"
|
||||
nextLabel=<ArrowRightIcon className="h-5" />
|
||||
previousLabel=<ArrowLeftIcon className="h-5" />
|
||||
renderOnZeroPageCount={null}
|
||||
containerClassName="inline sm:flex text-base h-10 mx-auto"
|
||||
activeClassName="active"
|
||||
pageClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.active]:bg-gray-100"
|
||||
pageClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border rounded-lg border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.active]:bg-gray-100"
|
||||
pageLinkClassName="flex items-center justify-center px-4 h-10 ml-0 leading-tight"
|
||||
previousClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.disabled]:hidden"
|
||||
previousClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border rounded-lg border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.disabled]:hidden"
|
||||
previousLinkClassName="flex items-center justify-center px-4 h-10 ml-0 leading-tight"
|
||||
nextClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.disabled]:hidden"
|
||||
nextClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border rounded-lg border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.disabled]:hidden"
|
||||
nextLinkClassName="flex items-center justify-center px-4 h-10 ml-0 leading-tight"
|
||||
breakClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white"
|
||||
breakClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border rounded-lg border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white"
|
||||
breakLinkClassName="flex items-center justify-center px-4 h-10 ml-0 leading-tight"
|
||||
/>
|
||||
</>
|
||||
|
@ -33,7 +33,8 @@ export function GridTileImage({
|
||||
<Image
|
||||
className={clsx('relative h-full w-full object-contain', {
|
||||
'transition duration-300 ease-in-out hover:scale-105': isInteractive,
|
||||
'm-4 max-h-[8rem] min-h-[8rem]': props.width === 200 && props.height === 200 // this styling is for the thumbnails below gallery on product detail page
|
||||
'max-h-[4rem] min-h-[4rem]': props.width === 200 && props.height === 200, // this styling is for the thumbnails below gallery on product detail page
|
||||
'max-h-[20rem] min-h-[20rem]': props.width === 500 && props.height === 500 // this styling is for the gallery in recommendations on product detail page
|
||||
})}
|
||||
{...props}
|
||||
/>
|
||||
|
@ -1,16 +1,18 @@
|
||||
import clsx from 'clsx';
|
||||
import Image from 'next/image';
|
||||
|
||||
export default function LogoIcon(props: React.ComponentProps<'svg'>) {
|
||||
export type ImageProps = {
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export default function LogoIcon(props: ImageProps) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label={`${process.env.SITE_NAME} logo`}
|
||||
viewBox="0 0 32 28"
|
||||
{...props}
|
||||
className={clsx('h-4 w-4 fill-black dark:fill-white', props.className)}
|
||||
>
|
||||
<path d="M21.5758 9.75769L16 0L0 28H11.6255L21.5758 9.75769Z" />
|
||||
<path d="M26.2381 17.9167L20.7382 28H32L26.2381 17.9167Z" />
|
||||
</svg>
|
||||
<Image
|
||||
src="https://www.shopware.com/media/pages/solutions/shopware-frontends/shopware-frontends-intro-graphic-base.svg"
|
||||
alt="Shopware Composable Frontends Logo"
|
||||
width={Number(props.width)}
|
||||
height={Number(props.height)}
|
||||
></Image>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { Menu } from 'lib/shopify/types';
|
||||
import { Menu } from 'lib/shopware/types';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
@ -15,7 +15,7 @@ const FooterMenuItem = ({ item }: { item: Menu }) => {
|
||||
}, [pathname, item.path]);
|
||||
|
||||
return (
|
||||
<li className="mt-2 first:mt-1">
|
||||
<li key={item.title} className="mt-2 first:mt-1">
|
||||
<Link
|
||||
href={item.path}
|
||||
className={clsx(
|
||||
@ -34,13 +34,25 @@ const FooterMenuItem = ({ item }: { item: Menu }) => {
|
||||
export default function FooterMenu({ menu }: { menu: Menu[] }) {
|
||||
if (!menu.length) return null;
|
||||
|
||||
return (
|
||||
<nav>
|
||||
<ul>
|
||||
{menu.map((item: Menu) => {
|
||||
return <FooterMenuItem key={item.title} item={item} />;
|
||||
})}
|
||||
</ul>
|
||||
return menu.map((item: Menu) => (
|
||||
<nav className="col-span-1 lg:col-span-3" key={item.title + item.type}>
|
||||
{item.type === 'headline' ? <span className="font-bold">{item.title}</span> : null}
|
||||
{item.children.length > 0 ? (
|
||||
<ul className="py-3 md:py-0 md:pt-4" key={item.title}>
|
||||
{item.children.map((item: Menu) => (
|
||||
<FooterMenuItem key={item.title} item={item} />
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
// if there are no children, at least display a link
|
||||
<Link
|
||||
key={item.title}
|
||||
href={item.path}
|
||||
className="text-gray-800 transition duration-150 ease-in-out hover:text-gray-300 dark:text-gray-100"
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
));
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import GitHubIcon from 'components/icons/github';
|
||||
import FooterMenu from 'components/layout/footer-menu';
|
||||
import LogoSquare from 'components/logo-square';
|
||||
import { getMenu } from 'lib/shopware';
|
||||
import { Menu } from 'lib/shopware/types';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
const { COMPANY_NAME, SITE_NAME } = process.env;
|
||||
@ -18,44 +17,30 @@ export default async function Footer() {
|
||||
const copyrightName = COMPANY_NAME || SITE_NAME || '';
|
||||
|
||||
return (
|
||||
<footer className="border-t border-gray-700 bg-white text-black dark:bg-black dark:text-white">
|
||||
<div className="mx-auto w-full max-w-7xl px-6">
|
||||
<div className="grid grid-cols-1 gap-8 border-b border-gray-700 py-12 transition-colors duration-150 lg:grid-cols-12">
|
||||
<div className="col-span-1 lg:col-span-4">
|
||||
<footer className="text-sm text-neutral-400 dark:text-neutral-600">
|
||||
<div className="mx-auto flex w-full max-w-7xl flex-col gap-6 border-t border-neutral-200 px-6 py-12 text-sm dark:border-neutral-700 md:flex-row md:gap-12 md:px-4 xl:px-0">
|
||||
<div className="grid grid-cols-1 gap-8 transition-colors duration-150 lg:grid-cols-12">
|
||||
<div className="col-span-1 lg:col-span-3">
|
||||
<Link className="flex items-center gap-2 text-black dark:text-white" href="/">
|
||||
<LogoSquare size="sm" />
|
||||
<span className="uppercase">{SITE_NAME}</span>
|
||||
</Link>
|
||||
</div>
|
||||
{menu.map((item: Menu) => (
|
||||
<nav className="col-span-1 lg:col-span-3" key={item.title + item.type}>
|
||||
{item.type === 'headline' ? <span className="font-bold">{item.title}</span> : null}
|
||||
{item.children.length > 0 ? (
|
||||
<ul className="py-3 md:py-0 md:pt-4" key={item.title}>
|
||||
{item.children.map((item: Menu) => (
|
||||
<li key={item.title} className="py-3 md:py-0 md:pb-4">
|
||||
<Link
|
||||
href={item.path}
|
||||
className="text-gray-800 transition duration-150 ease-in-out hover:text-gray-300 dark:text-gray-100"
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
// if there are no children, at least display a link
|
||||
<Link
|
||||
key={item.title}
|
||||
href={item.path}
|
||||
className="text-gray-800 transition duration-150 ease-in-out hover:text-gray-300 dark:text-gray-100"
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
)}
|
||||
</nav>
|
||||
))}
|
||||
<div className="col-span-1 inline-grid justify-items-end text-black dark:text-white lg:col-span-2">
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="flex h-[188px] w-[200px] flex-col gap-2">
|
||||
<div className={skeleton} />
|
||||
<div className={skeleton} />
|
||||
<div className={skeleton} />
|
||||
<div className={skeleton} />
|
||||
<div className={skeleton} />
|
||||
<div className={skeleton} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<FooterMenu menu={menu} />
|
||||
</Suspense>
|
||||
<div className="md:ml-auto">
|
||||
<a
|
||||
aria-label="Github Repository"
|
||||
href="https://github.com/shopware/frontends"
|
||||
@ -66,30 +51,6 @@ export default async function Footer() {
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="flex h-[188px] w-[200px] flex-col gap-2">
|
||||
<div className={skeleton} />
|
||||
<div className={skeleton} />
|
||||
<div className={skeleton} />
|
||||
<div className={skeleton} />
|
||||
<div className={skeleton} />
|
||||
<div className={skeleton} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<FooterMenu menu={menu} />
|
||||
</Suspense>
|
||||
<div className="md:ml-auto">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:text-black dark:hover:text-neutral-300"
|
||||
aria-label="Github Repository"
|
||||
href="https://github.com/vercel/commerce"
|
||||
>
|
||||
<GitHubIcon className="h-6" />
|
||||
<p>Source</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-neutral-200 py-6 text-sm dark:border-neutral-700">
|
||||
<div className="mx-auto flex w-full max-w-7xl flex-col items-center gap-1 md:flex-row md:gap-0">
|
||||
@ -97,10 +58,9 @@ export default async function Footer() {
|
||||
© {copyrightDate} {copyrightName}
|
||||
{copyrightName.length && !copyrightName.endsWith('.') ? '.' : ''} All rights reserved.
|
||||
</p>
|
||||
<div className="flex items-center text-sm text-white dark:text-black">
|
||||
<span className="text-black dark:text-white">
|
||||
Created by Shopware Composable Frontends
|
||||
</span>
|
||||
<hr className="mx-4 hidden h-4 w-[1px] border-l border-neutral-400 md:inline-block" />
|
||||
<p>Created by Shopware Composable Frontends</p>
|
||||
<div className="md:ml-auto">
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
href="https://frontends.shopware.com/"
|
||||
|
@ -7,7 +7,6 @@ import Link from 'next/link';
|
||||
import { Suspense } from 'react';
|
||||
import MobileMenu from './mobile-menu';
|
||||
import Search from './search';
|
||||
const { SITE_NAME } = process.env;
|
||||
|
||||
export default async function Navbar() {
|
||||
const menu = await getMenu({ type: 'main-navigation' });
|
||||
@ -18,16 +17,13 @@ export default async function Navbar() {
|
||||
<MobileMenu menu={menu} />
|
||||
</div>
|
||||
<div className="flex w-full items-center">
|
||||
<div className="flex w-full md:w-1/3">
|
||||
<div className="flex w-full md:w-4/6">
|
||||
<Link
|
||||
href="/"
|
||||
aria-label="Go back home"
|
||||
className="mr-2 flex w-full items-center justify-center md:w-auto lg:mr-6"
|
||||
>
|
||||
<LogoSquare />
|
||||
<div className="ml-2 flex-none text-sm font-medium uppercase md:hidden lg:block">
|
||||
{SITE_NAME}
|
||||
</div>
|
||||
</Link>
|
||||
{menu.length ? (
|
||||
<ul className="hidden text-sm md:flex md:items-center">
|
||||
@ -44,10 +40,10 @@ export default async function Navbar() {
|
||||
</ul>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="hidden justify-center md:flex md:w-1/3">
|
||||
<div className="hidden justify-center md:flex md:w-1/6">
|
||||
<Search />
|
||||
</div>
|
||||
<div className="flex justify-end md:w-1/3">
|
||||
<div className="flex justify-end md:w-1/6">
|
||||
<Suspense fallback={<OpenCart />}>
|
||||
<Cart />
|
||||
</Suspense>
|
||||
|
@ -13,6 +13,8 @@ export default function LogoSquare({ size }: { size?: 'sm' | undefined }) {
|
||||
)}
|
||||
>
|
||||
<LogoIcon
|
||||
width={20}
|
||||
height={20}
|
||||
className={clsx({
|
||||
'h-[16px] w-[16px]': !size,
|
||||
'h-[10px] w-[10px]': size === 'sm'
|
||||
|
@ -17,7 +17,7 @@ export default async function OpengraphImage(props?: Props): Promise<ImageRespon
|
||||
(
|
||||
<div tw="flex h-full w-full flex-col items-center justify-center bg-black">
|
||||
<div tw="flex flex-none items-center justify-center border border-neutral-700 h-[160px] w-[160px] rounded-3xl">
|
||||
<LogoIcon width="64" height="58" fill="white" />
|
||||
<LogoIcon width="64" height="58" />
|
||||
</div>
|
||||
<p tw="mt-12 text-6xl font-bold text-white">{title}</p>
|
||||
</div>
|
||||
|
@ -21,7 +21,7 @@ export function Gallery({ images }: { images: { src: string; altText: string }[]
|
||||
|
||||
return (
|
||||
<div className="mr-8 h-full">
|
||||
<div className="relative mb-12 h-full max-h-[550px] overflow-hidden">
|
||||
<div className="relative mb-12 h-full max-h-[550px] min-h-[550px] overflow-hidden">
|
||||
{images[currentImageIndex] && (
|
||||
<Image
|
||||
className="relative h-full w-full object-contain"
|
||||
|
@ -18,18 +18,18 @@ export function ProductDescription({ product }: { product: Product }) {
|
||||
</div>
|
||||
<VariantSelector options={product.options} variants={product.variants} />
|
||||
|
||||
{product.descriptionHtml ? (
|
||||
<Prose
|
||||
className="mb-6 text-sm leading-tight dark:text-white/[60%]"
|
||||
html={product.descriptionHtml}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<AddToCart
|
||||
product={product}
|
||||
variants={product.variants}
|
||||
availableForSale={product.availableForSale}
|
||||
/>
|
||||
|
||||
{product.descriptionHtml ? (
|
||||
<Prose
|
||||
className="m-6 max-h-96 overflow-y-auto text-sm leading-tight dark:text-white/[60%]"
|
||||
html={product.descriptionHtml}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user