feat: add images to improve homepage quality

This commit is contained in:
paolosantarsiero 2025-01-19 15:39:18 +01:00
parent 57c718f388
commit eae24f1f35
15 changed files with 132 additions and 68 deletions

View File

@ -17,7 +17,7 @@ export default async function ProductPage(props: { params: Promise<{ slug: strin
<div className="mb-6 px-4">
<span>{category.description}</span>
</div>
<section className="mx-auto grid max-w-screen-2xl flex-col gap-4 px-4 pb-4 md:grid-cols-8 md:grid-rows-2">
<section className="mx-auto grid max-w-screen-2xl flex-col gap-6 px-4 pb-4 md:grid-cols-8 md:grid-rows-2">
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}

View File

@ -2,6 +2,7 @@ import { CartProvider } from 'components/cart/cart-context';
import { CheckoutProvider } from 'components/checkout/checkout-provider';
import Footer from 'components/layout/footer';
import { Navbar } from 'components/layout/navbar';
import Subscriptions from 'components/layout/subscriptions';
import { NextAuthProvider } from 'components/next-session-provider';
import { WelcomeToast } from 'components/welcome-toast';
import { GeistSans } from 'geist/font/sans';
@ -55,6 +56,7 @@ export default async function RootLayout({ children }: { children: ReactNode })
{children}
<Toaster closeButton />
<WelcomeToast />
<Subscriptions />
</main>
</CheckoutProvider>
</CartProvider>

View File

@ -15,8 +15,8 @@ import { getTranslations } from 'next-intl/server';
import Image from 'next/image';
import Link from 'next/link';
import { Suspense } from 'react';
import Flowers from '../assets/images/fiori.png';
import ManWild from '../assets/images/man-wild.png';
import Bg1 from '../assets/images/slide-bg-01.webp';
import Bg2 from '../assets/images/slide-bg-02.webp';
async function Products({ category }: { category: Category }) {
const products: Product[] = await woocommerce.get('products', {
@ -32,23 +32,27 @@ async function ProductsByCategory() {
return (
<>
<div>
<Image alt="" src={Flowers} className="mb-4 h-[440px] w-full object-cover" />
<div className="relative">
<Image alt="" src={Bg1} className="mb-4 h-[640px] w-full object-cover" />
<div className="absolute bottom-0 left-0 h-1/3 w-full bg-gradient-to-t from-black/60 to-transparent" />
<Link
href={''}
className="absolute right-20 top-80 text-2xl font-bold text-white underline"
className="absolute bottom-[10vh] right-20 text-2xl font-bold text-white underline"
>
{t('helpIA')}
</Link>
</div>
{categories.map((category, index) => (
<div key={category.id}>
<div className="mb-2 mt-6 flex items-center justify-between px-4">
<div key={category.id} className="my-8 p-4">
<div className="mb-2 flex items-center justify-between">
<span className="text-2xl font-bold">{category.name}</span>
<Link href={`/collection/${category.slug}`} className="pe-2 hover:text-indigo-500">
{t('viewAll')}
</Link>
</div>
<Suspense
fallback={
<div className="mx-auto grid max-w-screen-2xl gap-4 px-4 pb-4 md:grid-cols-6 md:grid-rows-2">
<div className="mx-auto grid max-w-screen-2xl gap-6 px-4 pb-4 md:grid-cols-6 md:grid-rows-2">
{[...Array(3)].map((_, i) => (
<ProductSuspense key={i} />
))}
@ -58,13 +62,15 @@ async function ProductsByCategory() {
<Products category={category} />
</Suspense>
{index === 1 && (
<div className="my-6 flex flex-col px-4">
<>
<div className="-mx-4">
<Image alt="" src={ManWild} className="my-4 h-[440px] w-full object-cover" />
<Image alt="" src={Bg2} className="my-4 h-[540px] w-full object-cover" />
</div>
<div className="my-6 flex flex-col px-4">
<span className="mb-2 text-2xl font-bold">{t('topProducts')}</span>
<Carousel />
</div>
</>
)}
</div>
))}
@ -79,7 +85,7 @@ async function LatestPosts() {
return (
<div className="my-6 flex flex-col px-4">
<span className="mb-2 text-2xl font-bold">{t('latestPosts')}</span>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{posts.map((post: any) => (
<div
key={post.id + '-post'}
@ -106,7 +112,7 @@ export default async function HomePage() {
<section>
<Suspense
fallback={
<div className="mx-auto grid max-w-screen-2xl gap-4 px-4 pb-4 md:grid-cols-6 md:grid-rows-2">
<div className="mx-auto grid max-w-screen-2xl gap-6 px-4 pb-4 md:grid-cols-6 md:grid-rows-2">
{[...Array(3)].map((_, i) => (
<ProductSuspense key={i} />
))}
@ -117,7 +123,7 @@ export default async function HomePage() {
</Suspense>
<Suspense
fallback={
<div className="mx-auto grid max-w-screen-2xl gap-4 px-4 pb-4 md:grid-cols-6 md:grid-rows-2">
<div className="mx-auto grid max-w-screen-2xl gap-6 px-4 pb-4 md:grid-cols-6 md:grid-rows-2">
{[...Array(3)].map((_, i) => (
<ProductSuspense key={i} />
))}

View File

@ -3,8 +3,10 @@ import { notFound } from 'next/navigation';
import { AddToCart } from 'components/cart/add-to-cart';
import { Gallery } from 'components/product/gallery';
import { ProductCard } from 'components/product/product-card';
import { ProductProvider } from 'components/product/product-context';
import { ProductDescription } from 'components/product/product-description';
import ProductSpecifications from 'components/product/product-specifications';
import { ProductVariants } from 'components/product/product-variants';
import { VariantSelector } from 'components/product/variant-selector';
import Prose from 'components/prose';
import { HIDDEN_PRODUCT_TAG } from 'lib/constants';
@ -12,7 +14,6 @@ import { Image } from 'lib/woocomerce/models/base';
import { Product, ProductVariations } from 'lib/woocomerce/models/product';
import { woocommerce } from 'lib/woocomerce/woocommerce';
import { getTranslations } from 'next-intl/server';
import Link from 'next/link';
import { Suspense } from 'react';
export async function generateMetadata(props: {
@ -52,25 +53,9 @@ async function RelatedProducts({ product }: { product: Product }) {
{relatedProducts.length > 0 && (
<div className="mt-8 py-4">
<h3 className="text-2xl font-bold">{t('relatedProducts')}</h3>
<div className="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<div className="mx-auto mt-4 grid max-w-screen-2xl flex-col gap-6 pb-4 md:grid-cols-8">
{relatedProducts.map((relatedProduct) => {
return (
<Link
key={relatedProduct.id}
className="rounded-lg border border-neutral-200 bg-white dark:border-neutral-800 dark:bg-black"
href={`/product/${relatedProduct.slug}`}
>
<img
src={relatedProduct.images?.[0].src}
alt={relatedProduct.name}
className="h-48 w-full object-cover"
/>
<div className="p-4">
<h2 className="text-xl font-bold">{relatedProduct.name}</h2>
<div dangerouslySetInnerHTML={{ __html: relatedProduct.short_description }} />
</div>
</Link>
);
return <ProductCard key={relatedProduct.id} product={relatedProduct} />;
})}
</div>
</div>
@ -116,8 +101,9 @@ export default async function ProductPage(props: { params: Promise<{ name: strin
}}
/>
<div className="mx-auto max-w-screen-2xl px-4">
<div className="flex flex-col rounded-lg border border-neutral-200 bg-white p-8 dark:border-neutral-800 dark:bg-black md:p-12 lg:flex-row lg:gap-8">
<div className="h-full w-full basis-full lg:basis-4/6">
<div className="grid items-start gap-8 rounded-lg border border-neutral-200 bg-white p-4 dark:border-neutral-800 dark:bg-black md:flex-col-reverse lg:grid-cols-2 lg:flex-row lg:flex-col">
<h1 className="mb-2 text-5xl font-medium md:hidden lg:hidden">{product.name}</h1>
<div className="sticky top-4 w-full self-start">
<Suspense
fallback={
<div className="relative aspect-square h-full max-h-[550px] w-full overflow-hidden" />
@ -131,27 +117,30 @@ export default async function ProductPage(props: { params: Promise<{ name: strin
}))}
/>
</Suspense>
<div className="mt-4 text-center text-sm">
{product.description ? (
<Prose
className="mb-6 text-sm leading-tight dark:text-white/[60%]"
html={product.description}
/>
) : null}
</div>
</div>
<div className="basis-full lg:basis-2/6">
<h1 className="mb-2 text-5xl font-medium">{product.name}</h1>
<div className="">
<h1 className="mb-2 hidden text-5xl font-medium md:block lg:block">{product.name}</h1>
{variations && (
<Suspense fallback={null}>
<VariantSelector options={product.attributes} variations={variations} />
</Suspense>
)}
<div>
<Suspense fallback={null}>
<ProductDescription product={product} variations={variations} />
<ProductVariants product={product} variations={variations} />
</Suspense>
<AddToCart product={product} variations={variations} />
<div className="mt-4 text-center text-sm">
{product.short_description ? (
<Prose
className="mb-6 text-sm leading-tight dark:text-white/[60%]"
html={product.short_description}
/>
) : null}
</div>
<ProductSpecifications product={product} />
</div>
</div>
</div>
<Suspense fallback={null}>

View File

@ -37,7 +37,7 @@ export default function ProfileLayout({ user }: { user: React.ReactNode }) {
<div>
<Avatar src={customer.avatar_url} alt="avatar" className="h-24 w-24" />
<div className="mt-2">
<span> </span>
<span>{t('user.hi')} </span>
<span className="text-lg font-bold">{customer.first_name}</span>
</div>
<div className="flex-start mt-2 flex">

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -18,7 +18,7 @@ export async function Carousel() {
{carouselProducts.map((product, i) => (
<li
key={`${product.id}${i}`}
className="relative aspect-square h-[30vh] max-h-[275px] w-2/3 max-w-[475px] flex-none md:w-1/3"
className="relative aspect-square h-[60vh] max-h-[359px] w-2/3 max-w-[475px] flex-none md:w-1/3"
>
<Link href={`/product/${product.slug}`} className="relative h-full w-full">
<GridTileImage

View File

@ -43,7 +43,7 @@ export function ThreeItemGridItem({
export async function ThreeItemGrid({ products }: { products: Product[] }) {
return (
<section className="mx-auto grid gap-4 px-4 pb-4 md:grid-cols-8">
<section className="mx-auto grid gap-6 pb-4 md:grid-cols-8">
{products.map((product, index) => (
<ProductCard key={product.id} product={product} />
))}

View File

@ -0,0 +1,47 @@
import Link from 'next/link';
export default function Subscriptions() {
return (
<div className="p-4">
<h2 className="mb-6 text-3xl font-bold">Abbonamenti</h2>
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-4">
{/* Settimanale */}
<div className="flex h-[40vh] w-full flex-col items-center justify-center rounded-lg border p-6 text-center">
<h3 className="mb-2 text-xl font-semibold">Settimanale</h3>
<p className="mb-4">Abbonati per una settimana</p>
<p className="my-2 text-xl font-bold">4 w/</p>
<Link href="/subscribe/settimanale" className="rounded bg-blue-600 px-4 py-2 text-white">
Iscriviti
</Link>
</div>
{/* Mensile */}
<div className="flex h-[40vh] flex-col items-center justify-center rounded-lg border p-6 text-center">
<h3 className="mb-2 text-xl font-semibold">Mensile</h3>
<p className="mb-4">Abbonati per un mese</p>
<p className="my-2 text-xl font-bold">10 m/</p>
<Link href="/subscribe/mensile" className="rounded bg-blue-600 px-4 py-2 text-white">
Iscriviti
</Link>
</div>
{/* Annuale */}
<div className="flex h-[40vh] flex-col items-center justify-center rounded-lg border p-6 text-center">
<h3 className="mb-2 text-xl font-semibold">Annuale</h3>
<p className="mb-4">Abbonati per un anno</p>
<p className="my-2 text-xl font-bold">100 y/</p>
<Link href="/subscribe/annuale" className="rounded bg-blue-600 px-4 py-2 text-white">
Iscriviti
</Link>
</div>
{/* Luxury */}
<div className="flex h-[40vh] flex-col items-center justify-center rounded-lg border p-6 text-center">
<h3 className="mb-2 text-xl font-semibold">Luxury</h3>
<p className="mb-4">Esperienza premium</p>
<p className="my-2 text-xl font-bold">20 m/</p>
<Link href="/subscribe/luxury" className="rounded bg-blue-600 px-4 py-2 text-white">
Iscriviti
</Link>
</div>
</div>
</div>
);
}

View File

@ -21,7 +21,7 @@ export function Gallery({ images }: { images: { id: number; src: string; altText
<div className="relative aspect-square h-full max-h-[550px] w-full overflow-hidden">
{images[imageIndex] && (
<Image
className="h-full w-full object-contain"
className="h-full w-full rounded object-cover"
fill
sizes="(min-width: 1024px) 66vw, 100vw"
alt={images[imageIndex]?.altText as string}

View File

@ -0,0 +1,25 @@
'use client';
import { Accordion, AccordionItem } from '@nextui-org/react';
import Prose from 'components/prose';
import { Product } from 'lib/woocomerce/models/product';
export default function ProductSpecifications({ product }: { product: Product }) {
return (
<Accordion selectionMode="multiple">
{product.description ? (
<AccordionItem key="1" title="Descrizione">
<Prose
className="mb-6 text-sm leading-tight dark:text-white/[60%]"
html={product.description}
/>
</AccordionItem>
) : null}
{product.sku ? (
<AccordionItem key="2" title="Specifiche">
<span>SKU: {product.sku}</span>
</AccordionItem>
) : null}
</Accordion>
);
}

View File

@ -1,10 +1,9 @@
'use client';
import Price from 'components/price';
import Prose from 'components/prose';
import { Product, ProductVariations } from 'lib/woocomerce/models/product';
import { useProduct } from './product-context';
export function ProductDescription({
export function ProductVariants({
product,
variations
}: {
@ -18,7 +17,7 @@ export function ProductDescription({
return (
<>
<div className="mb-6 flex flex-col border-b pb-6 dark:border-neutral-700">
<div className="mb-6 flex flex-col pb-6">
<div className="mr-auto w-auto rounded-full bg-blue-600 p-2 text-sm text-white">
<Price
amount={productVariant ? productVariant.price : product.price}
@ -26,12 +25,6 @@ export function ProductDescription({
/>
</div>
</div>
{product.short_description ? (
<Prose
className="mb-6 text-sm leading-tight dark:text-white/[60%]"
html={product.short_description}
/>
) : null}
</>
);
}

View File

@ -2,7 +2,8 @@
"HomePage": {
"helpIA": "Buy with IA help",
"topProducts": "Top products",
"latestPosts": "Latest posts"
"latestPosts": "Latest posts",
"viewAll": "View all"
},
"ProductPage": {
"buy": "Buy",

View File

@ -2,7 +2,8 @@
"HomePage": {
"helpIA": "Guida all'acquisto con IA",
"topProducts": "Prodotti più venduti",
"latestPosts": "Ultimi articoli"
"latestPosts": "Ultimi articoli",
"viewAll": "Vedi tutti"
},
"ProductPage": {
"buy": "Acquista",