-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
52 changed files
with
540 additions
and
491 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,57 @@ | ||
import { Suspense } from "react" | ||
import { BUCKETS } from "constants/index" | ||
import { BestOffersSection } from "views/homepage/best-offers-skeleton" | ||
import { CarouselSectionSkeleton } from "views/homepage/carousel-section" | ||
import { CategoriesSection, CategoriesSectionSkeleton } from "views/homepage/categories-section" | ||
import { EverythingUnderSection } from "views/homepage/everything-under-section" | ||
import { AnnouncementBar } from "components/announcement-bar" | ||
import { HeroSection } from "views/homepage/hero-section" | ||
import { meilisearch } from "clients/search" | ||
import { CommerceProduct } from "types" | ||
import { env } from "env.mjs" | ||
import type { Hits } from "meilisearch" | ||
import { CategoriesSection } from "views/homepage/categories-section" | ||
import { FeaturedProductsSection } from "views/homepage/featured-products-section" | ||
import { PlatformCollection } from "lib/shopify/types" | ||
|
||
export const revalidate = 86400 | ||
|
||
export const dynamic = "force-static" | ||
|
||
export const dynamicParams = true | ||
|
||
export default function Homepage({ params: { bucket } }: { params: { bucket: string } }) { | ||
export default async function Homepage({ params: { bucket } }: { params: { bucket: string } }) { | ||
const heroTitles = { | ||
a: "Your daily trendsetting deals", | ||
b: "Spring into Savings! Up to 60% Off", | ||
a: "Discover Your Next Favorite Thing", | ||
b: "Shop the best Deals on Top Brands & Unique Finds", | ||
} | ||
|
||
const { products, categories } = await fetchFeaturedData() | ||
|
||
return ( | ||
<div className="flex w-full flex-col"> | ||
<HeroSection className="-order-1 md:-order-2" title={heroTitles[bucket as keyof typeof heroTitles]} /> | ||
<AnnouncementBar className="-order-2 md:-order-1" /> | ||
|
||
<Suspense fallback={<CategoriesSectionSkeleton />}> | ||
<CategoriesSection /> | ||
</Suspense> | ||
|
||
<Suspense fallback={<CarouselSectionSkeleton />}> | ||
<BestOffersSection /> | ||
</Suspense> | ||
|
||
<Suspense fallback={<CarouselSectionSkeleton />}> | ||
<EverythingUnderSection /> | ||
</Suspense> | ||
<AnnouncementBar className="-order-2" /> | ||
<HeroSection className="-order-1 self-center md:-order-2" title={heroTitles[bucket]} /> | ||
<FeaturedProductsSection products={products} /> | ||
<CategoriesSection categories={categories} /> | ||
</div> | ||
) | ||
} | ||
|
||
export async function generateStaticParams() { | ||
return BUCKETS.HOME.map((bucket) => ({ bucket })) | ||
} | ||
|
||
const fetchFeaturedData = async () => { | ||
const results = await meilisearch?.multiSearch({ | ||
queries: [ | ||
{ | ||
indexUid: env.MEILISEARCH_FEATURED_PRODUCTS_INDEX, | ||
q: "", | ||
limit: 6, | ||
attributesToRetrieve: ["id", "title", "featuredImage", "minPrice", "variants", "avgRating", "totalReviews", "vendor", "handle"], | ||
}, | ||
{ indexUid: env.MEILISEARCH_CATEGORIES_INDEX, q: "", limit: 4, attributesToRetrieve: ["id", "title", "handle"] }, | ||
], | ||
}) | ||
|
||
return { | ||
products: results[0].hits as Hits<CommerceProduct>, | ||
categories: results[1].hits as Hits<PlatformCollection>, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 0 additions & 32 deletions
32
starters/shopify-meilisearch/components/call-to-action.tsx
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { PlatformCollection } from "lib/shopify/types" | ||
import Image from "next/image" | ||
import Link from "next/link" | ||
import { cn } from "utils/cn" | ||
|
||
interface CategoryCardProps extends Pick<PlatformCollection, "title" | "image" | "handle"> { | ||
index: number | ||
className?: string | ||
} | ||
|
||
export const CategoryCard = ({ handle, image, title, index, className }: CategoryCardProps) => { | ||
const href = `/category/${handle}` | ||
return ( | ||
<Link href={href} className={cn("group relative overflow-hidden rounded-lg transition-all hover:shadow-md", className)} prefetch={false}> | ||
<div className="relative aspect-video"> | ||
<Image | ||
src={image?.url || `/category-placeholder-${index}.png`} | ||
alt={image?.altText || `${title} category`} | ||
className="transition-transform group-hover:scale-105" | ||
style={{ | ||
objectFit: "contain", | ||
}} | ||
fill | ||
/> | ||
<div className="absolute left-0 top-0 z-10 size-full bg-gradient-to-b from-white/90 to-60%" /> | ||
</div> | ||
<div className="absolute inset-x-4 top-0 z-20"> | ||
<h3 className="ml-3 mt-5 text-xl font-semibold text-black group-hover:text-orange-500">{title}</h3> | ||
</div> | ||
</Link> | ||
) | ||
} |
79 changes: 79 additions & 0 deletions
79
starters/shopify-meilisearch/components/compact-product-card.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import Image from "next/image" | ||
import Link from "next/link" | ||
import { cn } from "utils/cn" | ||
import { type CurrencyType, mapCurrencyToSign } from "utils/map-currency-to-sign" | ||
import type { CommerceProduct } from "types" | ||
import { StarIcon } from "components/icons/star-icon" | ||
|
||
interface ProductCardProps extends Pick<CommerceProduct, "variants" | "handle" | "images" | "title" | "featuredImage" | "minPrice" | "avgRating" | "totalReviews" | "vendor"> { | ||
priority?: boolean | ||
prefetch?: boolean | ||
className?: string | ||
} | ||
|
||
export const CompactProductCard = ({ | ||
variants, | ||
handle, | ||
title, | ||
featuredImage, | ||
minPrice, | ||
avgRating, | ||
totalReviews, | ||
className, | ||
priority, | ||
vendor, | ||
prefetch = false, | ||
}: ProductCardProps) => { | ||
const noOfVariants = variants?.length | ||
const href = `/product/${handle}` | ||
const linkAria = `Visit product: ${title}` | ||
const variantPrice = variants?.find(Boolean)?.price | ||
|
||
return ( | ||
<Link | ||
className={cn("group relative flex flex-col overflow-hidden rounded-lg border border-gray-100 transition-all", className)} | ||
aria-label={linkAria} | ||
href={href} | ||
prefetch={prefetch} | ||
> | ||
<div className="relative aspect-square overflow-hidden"> | ||
<Image | ||
priority={priority} | ||
className="object-cover transition-transform group-hover:scale-105" | ||
src={featuredImage?.url || "/default-product-image.svg"} | ||
alt={featuredImage?.altText || title} | ||
fill | ||
/> | ||
</div> | ||
<div className="absolute bottom-0 flex w-full shrink-0 grow translate-y-full flex-col overflow-hidden bg-gradient-to-t from-gray-100 to-transparent p-4 transition-transform group-hover:translate-y-0"> | ||
{/* remove first word from the title as it includes vendor (this just needs feed update and then can be removed) */} | ||
<h3 className="line-clamp-2 text-lg font-semibold">{title.split(" ").slice(1).join(" ")}</h3> | ||
<div className="mt-auto flex flex-col gap-1"> | ||
{!!variantPrice && <span>From {mapCurrencyToSign((variantPrice.currencyCode as CurrencyType) || "USD") + minPrice.toFixed(2)}</span>} | ||
|
||
{!!vendor && <p className="text-sm text-gray-500">{vendor}</p>} | ||
|
||
<div className="flex items-center gap-1"> | ||
{!!avgRating && !!totalReviews && ( | ||
<> | ||
<div className="flex items-center space-x-1"> | ||
<StarIcon className="size-4 fill-gray-400 stroke-gray-500" /> | ||
<span className="text-sm">{avgRating.toFixed(2)}</span> | ||
<span className="text-xs"> | ||
({totalReviews} review{totalReviews !== 1 && "s"}) | ||
</span> | ||
</div> | ||
• | ||
</> | ||
)} | ||
{noOfVariants > 0 && ( | ||
<p className="text-sm text-gray-500"> | ||
{noOfVariants} variant{noOfVariants > 1 ? "s" : ""} | ||
</p> | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
</Link> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
starters/shopify-meilisearch/components/featured-product-card.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import Image from "next/image" | ||
import Link from "next/link" | ||
import { cn } from "utils/cn" | ||
import { type CurrencyType, mapCurrencyToSign } from "utils/map-currency-to-sign" | ||
import type { CommerceProduct } from "types" | ||
import { StarIcon } from "components/icons/star-icon" | ||
|
||
interface ProductCardProps extends Pick<CommerceProduct, "variants" | "handle" | "images" | "title" | "featuredImage" | "minPrice" | "avgRating" | "totalReviews" | "vendor"> { | ||
priority?: boolean | ||
prefetch?: boolean | ||
className?: string | ||
} | ||
|
||
export const FeaturedProductCard = ({ | ||
variants, | ||
handle, | ||
title, | ||
featuredImage, | ||
minPrice, | ||
avgRating, | ||
totalReviews, | ||
className, | ||
priority, | ||
vendor, | ||
prefetch = false, | ||
}: ProductCardProps) => { | ||
const noOfVariants = variants?.length | ||
const href = `/product/${handle}` | ||
const linkAria = `Visit product: ${title}` | ||
const variantPrice = variants?.find(Boolean)?.price | ||
|
||
return ( | ||
<Link className={cn("group flex flex-col overflow-hidden rounded-lg border border-gray-100 transition-all", className)} aria-label={linkAria} href={href} prefetch={prefetch}> | ||
<div className="relative aspect-square overflow-hidden"> | ||
<Image | ||
priority={priority} | ||
className="object-cover transition-transform group-hover:scale-105" | ||
src={featuredImage?.url || "/default-product-image.svg"} | ||
alt={featuredImage?.altText || title} | ||
fill | ||
/> | ||
</div> | ||
<div className="flex shrink-0 grow items-start justify-between p-4 transition-colors group-hover:bg-gradient-to-t group-hover:from-gray-100 group-hover:to-transparent"> | ||
<div className="flex flex-col gap-1"> | ||
{/* remove first word from the title as it includes vendor (this just needs feed update and then can be removed) */} | ||
<h3 className="line-clamp-2 text-lg font-semibold">{title.split(" ").slice(1).join(" ")}</h3> | ||
{!!variantPrice && <span className="block sm:hidden">From {mapCurrencyToSign((variantPrice.currencyCode as CurrencyType) || "USD") + minPrice.toFixed(2)}</span>} | ||
|
||
<div className="mt-auto flex flex-col gap-1"> | ||
{!!vendor && <p className="text-sm text-gray-500">{vendor}</p>} | ||
<div className="flex items-center gap-1"> | ||
{!!avgRating && !!totalReviews && ( | ||
<> | ||
<div className="flex items-center space-x-1"> | ||
<StarIcon className="size-4 fill-gray-400 stroke-gray-500" /> | ||
<span className="text-sm">{avgRating.toFixed(2)}</span> | ||
<span className="text-xs"> | ||
({totalReviews} review{totalReviews !== 1 && "s"}) | ||
</span> | ||
</div> | ||
• | ||
</> | ||
)} | ||
{noOfVariants > 0 && ( | ||
<p className="text-sm text-gray-500"> | ||
{noOfVariants} variant{noOfVariants > 1 ? "s" : ""} | ||
</p> | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
{!!variantPrice && <span className="hidden sm:block">From {mapCurrencyToSign((variantPrice.currencyCode as CurrencyType) || "USD") + minPrice.toFixed(2)}</span>} | ||
</div> | ||
</Link> | ||
) | ||
} |
Oops, something went wrong.