medusa-ui-home 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -1
- package/src/home/components/about-brand/index.tsx +71 -0
- package/src/home/components/baptism-picks/index.tsx +70 -0
- package/src/home/components/baptism-section/index.tsx +68 -0
- package/src/home/components/blog-posts/index.tsx +85 -0
- package/src/home/components/brand-marquee/index.tsx +39 -0
- package/src/home/components/brand-pillars/index.tsx +77 -0
- package/src/home/components/category-pills/index.tsx +75 -0
- package/src/home/components/celebrity-trust/index.tsx +80 -0
- package/src/home/components/features/index.tsx +26 -23
- package/src/home/components/loved-by-moms/index.tsx +16 -8
- package/src/home/components/luxe-favourites/index.tsx +88 -0
- package/src/home/components/new-arrivals/index.tsx +18 -4
- package/src/home/components/promo-announcements/index.tsx +46 -0
- package/src/home/components/promo-countdown/index.tsx +87 -0
- package/src/home/components/shared/product-grid-section.tsx +114 -0
- package/src/home/components/shop-by-age/index.tsx +16 -6
- package/src/home/components/shop-by-category/index.tsx +18 -4
- package/src/home/components/testimonials/index.tsx +1 -1
- package/src/home/components/theme-dresses/index.tsx +39 -0
- package/src/home/components/video-stories/index.tsx +488 -0
- package/src/home/components/why-choose-us/dynamic-features.tsx +8 -6
- package/src/home/home-content.tsx +33 -0
- package/src/home/lib/section-copy.ts +35 -0
- package/src/home/register-sections.ts +69 -0
- package/src/home/sections/about-brand-section.tsx +23 -0
- package/src/home/sections/baptism-picks-section.tsx +8 -0
- package/src/home/sections/baptism-section.tsx +17 -0
- package/src/home/sections/blog-posts-section.tsx +8 -0
- package/src/home/sections/brand-marquee-section.tsx +13 -0
- package/src/home/sections/brand-pillars-section.tsx +19 -0
- package/src/home/sections/category-pills-section.tsx +6 -0
- package/src/home/sections/celebrity-trust-section.tsx +20 -0
- package/src/home/sections/features-section.tsx +20 -0
- package/src/home/sections/hero-section.tsx +6 -0
- package/src/home/sections/loved-by-moms-section.tsx +21 -0
- package/src/home/sections/luxe-favourites-section.tsx +15 -0
- package/src/home/sections/new-arrivals-section.tsx +21 -0
- package/src/home/sections/promo-announcements-section.tsx +6 -0
- package/src/home/sections/promo-countdown-section.tsx +15 -0
- package/src/home/sections/shop-by-age-section.tsx +19 -0
- package/src/home/sections/shop-by-category-section.tsx +18 -0
- package/src/home/sections/testimonials-section.tsx +28 -0
- package/src/home/sections/theme-dresses-section.tsx +21 -0
- package/src/home/sections/video-stories-section.tsx +15 -0
- package/src/home/sections/why-choose-us-section.tsx +8 -0
- package/src/theme/default-home-theme.ts +13 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "medusa-ui-home",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Homepage sections.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"medusa-storefront-data": "^2.0.0",
|
|
27
27
|
"medusa-storefront-hooks": "^1.0.0",
|
|
28
28
|
"medusa-wishlist-logic": "^2.0.0",
|
|
29
|
+
"medusa-storefront-compose": "^1.0.0",
|
|
29
30
|
"medusa-storefront-theme-base": "^2.0.0",
|
|
30
31
|
"medusa-ui-common": "^2.0.0",
|
|
31
32
|
"medusa-ui-product": "^2.0.0",
|
|
@@ -49,6 +50,16 @@
|
|
|
49
50
|
"types": "./src/home/*",
|
|
50
51
|
"import": "./src/home/*",
|
|
51
52
|
"default": "./src/home/*"
|
|
53
|
+
},
|
|
54
|
+
"./home/register-sections": {
|
|
55
|
+
"types": "./src/home/register-sections.ts",
|
|
56
|
+
"import": "./src/home/register-sections.ts",
|
|
57
|
+
"default": "./src/home/register-sections.ts"
|
|
58
|
+
},
|
|
59
|
+
"./home/home-content": {
|
|
60
|
+
"types": "./src/home/home-content.tsx",
|
|
61
|
+
"import": "./src/home/home-content.tsx",
|
|
62
|
+
"default": "./src/home/home-content.tsx"
|
|
52
63
|
}
|
|
53
64
|
},
|
|
54
65
|
"typesVersions": {
|
|
@@ -68,6 +79,7 @@
|
|
|
68
79
|
"typescript": "^5.6.0",
|
|
69
80
|
"medusa-storefront-analytics": "workspace:*",
|
|
70
81
|
"medusa-storefront-data": "workspace:*",
|
|
82
|
+
"medusa-storefront-compose": "workspace:*",
|
|
71
83
|
"medusa-storefront-theme-base": "workspace:*",
|
|
72
84
|
"medusa-ui-common": "workspace:*",
|
|
73
85
|
"medusa-ui-product": "workspace:*"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import LocalizedClientLink from "medusa-ui-common/common/components/localized-client-link"
|
|
4
|
+
import type { HomeThemeClassNames } from "medusa-storefront-theme-base"
|
|
5
|
+
import { useThemeSection } from "medusa-ui-common/providers"
|
|
6
|
+
|
|
7
|
+
export type AboutBrandStat = {
|
|
8
|
+
label: string
|
|
9
|
+
value: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type AboutBrandContent = {
|
|
13
|
+
eyebrow?: string
|
|
14
|
+
title: string
|
|
15
|
+
description: string
|
|
16
|
+
readMoreHref?: string
|
|
17
|
+
stats: AboutBrandStat[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type AboutBrandProps = {
|
|
21
|
+
content: AboutBrandContent | null
|
|
22
|
+
classNames?: Partial<HomeThemeClassNames>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default function AboutBrand({ content, classNames: classNamesProp }: AboutBrandProps) {
|
|
26
|
+
const cn = useThemeSection("home", classNamesProp)
|
|
27
|
+
if (!content) return null
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<section className={`${cn.sectionPad} ${cn.section}`}>
|
|
31
|
+
<div className="px-4 sm:px-6 md:px-8 lg:px-12">
|
|
32
|
+
<div className="mx-auto max-w-[1100px] text-center">
|
|
33
|
+
{content.eyebrow ? (
|
|
34
|
+
<p className={`text-xs sm:text-sm uppercase tracking-[0.25em] mb-3 ${cn.itemSubtitle}`}>
|
|
35
|
+
{content.eyebrow}
|
|
36
|
+
</p>
|
|
37
|
+
) : null}
|
|
38
|
+
<h2 className={`text-2xl sm:text-3xl md:text-4xl font-bold mb-4 ${cn.bannerTitle}`}>
|
|
39
|
+
{content.title}
|
|
40
|
+
</h2>
|
|
41
|
+
<p className={`text-sm sm:text-base leading-relaxed max-w-3xl mx-auto mb-8 ${cn.bannerText}`}>
|
|
42
|
+
{content.description}
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 sm:gap-6 mb-8">
|
|
46
|
+
{content.stats.map((stat) => (
|
|
47
|
+
<div
|
|
48
|
+
key={stat.label}
|
|
49
|
+
className={`rounded-2xl px-6 py-6 sm:py-8 ${cn.cardSurface}`}
|
|
50
|
+
>
|
|
51
|
+
<p className={`text-2xl sm:text-3xl font-bold mb-1 ${cn.itemTitle}`}>
|
|
52
|
+
{stat.value}
|
|
53
|
+
</p>
|
|
54
|
+
<p className={`text-sm ${cn.itemSubtitle}`}>{stat.label}</p>
|
|
55
|
+
</div>
|
|
56
|
+
))}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
{content.readMoreHref ? (
|
|
60
|
+
<LocalizedClientLink
|
|
61
|
+
href={content.readMoreHref}
|
|
62
|
+
className={`inline-flex rounded-full border-2 border-[var(--sf-color-primary)] px-8 py-3 text-sm font-bold text-[var(--sf-color-primary)] hover:bg-[var(--sf-color-primary)] hover:text-[var(--sf-color-text-on-primary)] transition`}
|
|
63
|
+
>
|
|
64
|
+
Read More
|
|
65
|
+
</LocalizedClientLink>
|
|
66
|
+
) : null}
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</section>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Image from "next/image"
|
|
4
|
+
import { HttpTypes } from "@medusajs/types"
|
|
5
|
+
import LocalizedClientLink from "medusa-ui-common/common/components/localized-client-link"
|
|
6
|
+
import type { HomeThemeClassNames } from "medusa-storefront-theme-base"
|
|
7
|
+
import { useThemeSection } from "medusa-ui-common/providers"
|
|
8
|
+
|
|
9
|
+
type BaptismPicksProps = {
|
|
10
|
+
title?: string
|
|
11
|
+
products: HttpTypes.StoreProduct[]
|
|
12
|
+
classNames?: Partial<HomeThemeClassNames>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function BaptismPicks({
|
|
16
|
+
title,
|
|
17
|
+
products,
|
|
18
|
+
classNames: classNamesProp,
|
|
19
|
+
}: BaptismPicksProps) {
|
|
20
|
+
const cn = useThemeSection("home", classNamesProp)
|
|
21
|
+
const items = (products ?? []).slice(0, 5)
|
|
22
|
+
if (items.length === 0) return null
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<section className={`${cn.sectionPad} ${cn.section}`}>
|
|
26
|
+
<div className="px-4 sm:px-6 md:px-8">
|
|
27
|
+
<div className="mx-auto max-w-[1360px]">
|
|
28
|
+
{title ? <h2 className={`${cn.sectionTitle} mb-8`}>{title}</h2> : null}
|
|
29
|
+
<div className="flex gap-4 sm:gap-6 overflow-x-auto pb-2 justify-center">
|
|
30
|
+
{items.map((product, index) => {
|
|
31
|
+
const thumb = product.thumbnail || product.images?.[0]?.url
|
|
32
|
+
return (
|
|
33
|
+
<LocalizedClientLink
|
|
34
|
+
key={product.id}
|
|
35
|
+
href={`/products/${product.handle}`}
|
|
36
|
+
className="shrink-0 flex flex-col items-center gap-3 group"
|
|
37
|
+
>
|
|
38
|
+
<div className="relative">
|
|
39
|
+
<span className="absolute -top-2 -right-2 z-10 flex h-7 w-7 items-center justify-center rounded-full bg-[var(--sf-btn-primary)] text-xs font-bold text-[var(--sf-color-text-on-primary)]">
|
|
40
|
+
{index + 1}
|
|
41
|
+
</span>
|
|
42
|
+
<div
|
|
43
|
+
className={`w-24 h-24 sm:w-28 sm:h-28 rounded-2xl overflow-hidden ring-2 ring-[var(--sf-color-border)] group-hover:ring-[var(--sf-color-primary)] ${cn.cardSurface}`}
|
|
44
|
+
>
|
|
45
|
+
{thumb ? (
|
|
46
|
+
<Image
|
|
47
|
+
src={thumb}
|
|
48
|
+
alt={product.title ?? ""}
|
|
49
|
+
width={112}
|
|
50
|
+
height={112}
|
|
51
|
+
className="w-full h-full object-cover"
|
|
52
|
+
unoptimized
|
|
53
|
+
/>
|
|
54
|
+
) : (
|
|
55
|
+
<div className={`w-full h-full ${cn.imagePlaceholder}`} />
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
<span className={`text-[11px] sm:text-xs text-center max-w-[100px] line-clamp-2 ${cn.itemTitle}`}>
|
|
60
|
+
{product.title}
|
|
61
|
+
</span>
|
|
62
|
+
</LocalizedClientLink>
|
|
63
|
+
)
|
|
64
|
+
})}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</section>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useMemo, useState } from "react"
|
|
4
|
+
import { HttpTypes } from "@medusajs/types"
|
|
5
|
+
import ProductGridSection from "medusa-ui-home/home/components/shared/product-grid-section"
|
|
6
|
+
import type { HomeThemeClassNames } from "medusa-storefront-theme-base"
|
|
7
|
+
import { useThemeSection } from "medusa-ui-common/providers"
|
|
8
|
+
|
|
9
|
+
type BaptismSectionProps = {
|
|
10
|
+
title?: string
|
|
11
|
+
girlProducts: HttpTypes.StoreProduct[]
|
|
12
|
+
boyProducts: HttpTypes.StoreProduct[]
|
|
13
|
+
region: HttpTypes.StoreRegion
|
|
14
|
+
ratings?: Array<{ product_id?: string; [key: string]: unknown }>
|
|
15
|
+
classNames?: Partial<HomeThemeClassNames>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function BaptismSection({
|
|
19
|
+
title,
|
|
20
|
+
girlProducts,
|
|
21
|
+
boyProducts,
|
|
22
|
+
region,
|
|
23
|
+
ratings,
|
|
24
|
+
classNames: classNamesProp,
|
|
25
|
+
}: BaptismSectionProps) {
|
|
26
|
+
const cn = useThemeSection("home", classNamesProp)
|
|
27
|
+
const [tab, setTab] = useState<"girl" | "boy">("girl")
|
|
28
|
+
|
|
29
|
+
const products = useMemo(
|
|
30
|
+
() => (tab === "girl" ? girlProducts : boyProducts),
|
|
31
|
+
[tab, girlProducts, boyProducts]
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
if (girlProducts.length === 0 && boyProducts.length === 0) return null
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className={cn.section}>
|
|
38
|
+
<div className="px-4 sm:px-6 md:px-8 lg:px-12">
|
|
39
|
+
<div className="mx-auto max-w-[1360px] flex justify-center gap-3 mb-6 sm:mb-8">
|
|
40
|
+
{(["girl", "boy"] as const).map((key) => (
|
|
41
|
+
<button
|
|
42
|
+
key={key}
|
|
43
|
+
type="button"
|
|
44
|
+
onClick={() => setTab(key)}
|
|
45
|
+
className={`rounded-full px-8 sm:px-12 py-2.5 text-sm font-bold capitalize transition ${
|
|
46
|
+
tab === key
|
|
47
|
+
? "bg-[var(--sf-btn-primary)] text-[var(--sf-color-text-on-primary)]"
|
|
48
|
+
: `border border-[var(--sf-color-border)] ${cn.cardSurface} text-[var(--sf-color-text)]`
|
|
49
|
+
}`}
|
|
50
|
+
>
|
|
51
|
+
{key}
|
|
52
|
+
</button>
|
|
53
|
+
))}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<ProductGridSection
|
|
57
|
+
title={title}
|
|
58
|
+
products={products}
|
|
59
|
+
region={region}
|
|
60
|
+
ratings={ratings}
|
|
61
|
+
columns={4}
|
|
62
|
+
maxProducts={8}
|
|
63
|
+
classNames={classNamesProp}
|
|
64
|
+
centeredTitle
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Image from "next/image"
|
|
4
|
+
import LocalizedClientLink from "medusa-ui-common/common/components/localized-client-link"
|
|
5
|
+
import type { HomeThemeClassNames } from "medusa-storefront-theme-base"
|
|
6
|
+
import { useThemeSection } from "medusa-ui-common/providers"
|
|
7
|
+
|
|
8
|
+
export type BlogPostCard = {
|
|
9
|
+
title: string
|
|
10
|
+
excerpt?: string
|
|
11
|
+
category?: string
|
|
12
|
+
date?: string
|
|
13
|
+
href: string
|
|
14
|
+
image?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type BlogPostsProps = {
|
|
18
|
+
title?: string
|
|
19
|
+
posts: BlogPostCard[]
|
|
20
|
+
classNames?: Partial<HomeThemeClassNames>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default function BlogPosts({
|
|
24
|
+
title,
|
|
25
|
+
posts,
|
|
26
|
+
classNames: classNamesProp,
|
|
27
|
+
}: BlogPostsProps) {
|
|
28
|
+
const cn = useThemeSection("home", classNamesProp)
|
|
29
|
+
const items = (posts ?? []).slice(0, 4)
|
|
30
|
+
if (items.length === 0) return null
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<section className={`${cn.sectionPad} ${cn.section}`}>
|
|
34
|
+
<div className="px-4 sm:px-6 md:px-8 lg:px-12">
|
|
35
|
+
<div className="mx-auto max-w-[1360px]">
|
|
36
|
+
{title ? (
|
|
37
|
+
<h2 className={`${cn.sectionTitleLeft} mb-8 sm:mb-10`}>{title}</h2>
|
|
38
|
+
) : null}
|
|
39
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5 sm:gap-6">
|
|
40
|
+
{items.map((post) => (
|
|
41
|
+
<LocalizedClientLink
|
|
42
|
+
key={post.href}
|
|
43
|
+
href={post.href}
|
|
44
|
+
className={`group flex flex-col overflow-hidden rounded-2xl ${cn.cardSurface}`}
|
|
45
|
+
>
|
|
46
|
+
<div className="relative aspect-[4/3] w-full overflow-hidden">
|
|
47
|
+
{post.image ? (
|
|
48
|
+
<Image
|
|
49
|
+
src={post.image}
|
|
50
|
+
alt={post.title}
|
|
51
|
+
fill
|
|
52
|
+
className="object-cover transition-transform duration-500 group-hover:scale-105"
|
|
53
|
+
sizes="(max-width: 640px) 100vw, 25vw"
|
|
54
|
+
unoptimized
|
|
55
|
+
/>
|
|
56
|
+
) : (
|
|
57
|
+
<div className={`w-full h-full ${cn.imagePlaceholder}`} />
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
<div className="p-4 flex flex-col flex-1">
|
|
61
|
+
{post.category ? (
|
|
62
|
+
<span className={`text-xs font-semibold uppercase mb-2 ${cn.itemSubtitle}`}>
|
|
63
|
+
{post.category}
|
|
64
|
+
</span>
|
|
65
|
+
) : null}
|
|
66
|
+
<h3 className={`font-bold text-sm sm:text-base mb-2 line-clamp-2 group-hover:text-[var(--sf-color-primary)] ${cn.itemTitle}`}>
|
|
67
|
+
{post.title}
|
|
68
|
+
</h3>
|
|
69
|
+
{post.excerpt ? (
|
|
70
|
+
<p className={`text-xs sm:text-sm line-clamp-2 mb-2 flex-1 ${cn.itemSubtitle}`}>
|
|
71
|
+
{post.excerpt}
|
|
72
|
+
</p>
|
|
73
|
+
) : null}
|
|
74
|
+
{post.date ? (
|
|
75
|
+
<span className={`text-[11px] ${cn.itemSubtitle}`}>{post.date}</span>
|
|
76
|
+
) : null}
|
|
77
|
+
</div>
|
|
78
|
+
</LocalizedClientLink>
|
|
79
|
+
))}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</section>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { HomeThemeClassNames } from "medusa-storefront-theme-base"
|
|
4
|
+
import { useThemeSection } from "medusa-ui-common/providers"
|
|
5
|
+
|
|
6
|
+
type BrandMarqueeProps = {
|
|
7
|
+
text?: string | null
|
|
8
|
+
classNames?: Partial<HomeThemeClassNames>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function BrandMarquee({
|
|
12
|
+
text,
|
|
13
|
+
classNames: classNamesProp,
|
|
14
|
+
}: BrandMarqueeProps) {
|
|
15
|
+
if (!text) return null
|
|
16
|
+
const cn = useThemeSection("home", classNamesProp)
|
|
17
|
+
const segment = `${text} - `
|
|
18
|
+
const line = segment.repeat(12)
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
className={`w-full py-4 sm:py-5 overflow-hidden border-y border-[var(--sf-color-border)] ${cn.section}`}
|
|
23
|
+
style={{
|
|
24
|
+
background: "var(--sf-color-primary)",
|
|
25
|
+
color: "var(--sf-color-text-on-primary)",
|
|
26
|
+
}}
|
|
27
|
+
>
|
|
28
|
+
<div className="flex animate-[sf-brand-marquee_20s_linear_infinite] whitespace-nowrap text-sm sm:text-base font-bold uppercase tracking-[0.35em]">
|
|
29
|
+
<span>{line}</span>
|
|
30
|
+
<span>{line}</span>
|
|
31
|
+
</div>
|
|
32
|
+
<style
|
|
33
|
+
dangerouslySetInnerHTML={{
|
|
34
|
+
__html: `@keyframes sf-brand-marquee{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}`,
|
|
35
|
+
}}
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Image from "next/image"
|
|
4
|
+
import type { HomeThemeClassNames } from "medusa-storefront-theme-base"
|
|
5
|
+
import { useThemeSection } from "medusa-ui-common/providers"
|
|
6
|
+
|
|
7
|
+
export type BrandPillar = {
|
|
8
|
+
title: string
|
|
9
|
+
description: string
|
|
10
|
+
image?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type BrandPillarsProps = {
|
|
14
|
+
sectionTitle?: string
|
|
15
|
+
pillars: BrandPillar[]
|
|
16
|
+
classNames?: Partial<HomeThemeClassNames>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default function BrandPillars({
|
|
20
|
+
sectionTitle,
|
|
21
|
+
description,
|
|
22
|
+
pillars,
|
|
23
|
+
classNames: classNamesProp,
|
|
24
|
+
}: BrandPillarsProps & { description?: string }) {
|
|
25
|
+
const cn = useThemeSection("home", classNamesProp)
|
|
26
|
+
if (!pillars?.length) return null
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<section className={`${cn.sectionPad} ${cn.section}`}>
|
|
30
|
+
<div className="px-4 sm:px-6 md:px-8 lg:px-12">
|
|
31
|
+
<div className="mx-auto max-w-[1360px]">
|
|
32
|
+
{sectionTitle ? (
|
|
33
|
+
<h2 className={`${cn.sectionTitle} mb-8 sm:mb-12 max-w-3xl mx-auto`}>
|
|
34
|
+
{sectionTitle}
|
|
35
|
+
</h2>
|
|
36
|
+
) : null}
|
|
37
|
+
{description ? (
|
|
38
|
+
<p className={`text-sm sm:text-base text-center max-w-2xl mx-auto mb-8 ${cn.itemSubtitle}`}>
|
|
39
|
+
{description}
|
|
40
|
+
</p>
|
|
41
|
+
) : null}
|
|
42
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 sm:gap-8">
|
|
43
|
+
{pillars.map((pillar, index) => (
|
|
44
|
+
<div
|
|
45
|
+
key={`${pillar.title}-${index}`}
|
|
46
|
+
className={`overflow-hidden rounded-2xl sm:rounded-3xl ${cn.cardSurface}`}
|
|
47
|
+
>
|
|
48
|
+
{pillar.image ? (
|
|
49
|
+
<div className="relative aspect-[16/10] w-full">
|
|
50
|
+
<Image
|
|
51
|
+
src={pillar.image}
|
|
52
|
+
alt={pillar.title}
|
|
53
|
+
fill
|
|
54
|
+
className="object-cover"
|
|
55
|
+
sizes="(max-width: 768px) 100vw, 50vw"
|
|
56
|
+
unoptimized
|
|
57
|
+
/>
|
|
58
|
+
</div>
|
|
59
|
+
) : (
|
|
60
|
+
<div className={`aspect-[16/10] w-full ${cn.imagePlaceholder}`} />
|
|
61
|
+
)}
|
|
62
|
+
<div className="p-5 sm:p-6">
|
|
63
|
+
<h3 className={`text-lg sm:text-xl font-bold mb-2 ${cn.itemTitle}`}>
|
|
64
|
+
{pillar.title}
|
|
65
|
+
</h3>
|
|
66
|
+
<p className={`text-sm sm:text-base leading-relaxed ${cn.itemSubtitle}`}>
|
|
67
|
+
{pillar.description}
|
|
68
|
+
</p>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
))}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</section>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Image from "next/image"
|
|
4
|
+
import { HttpTypes } from "@medusajs/types"
|
|
5
|
+
import LocalizedClientLink from "medusa-ui-common/common/components/localized-client-link"
|
|
6
|
+
import type { HomeThemeClassNames } from "medusa-storefront-theme-base"
|
|
7
|
+
import { useThemeSection } from "medusa-ui-common/providers"
|
|
8
|
+
|
|
9
|
+
type CategoryPillsProps = {
|
|
10
|
+
categories: HttpTypes.StoreProductCategory[]
|
|
11
|
+
classNames?: Partial<HomeThemeClassNames>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function CategoryPills({
|
|
15
|
+
categories,
|
|
16
|
+
classNames: classNamesProp,
|
|
17
|
+
}: CategoryPillsProps) {
|
|
18
|
+
const cn = useThemeSection("home", classNamesProp)
|
|
19
|
+
|
|
20
|
+
const pills = (categories ?? [])
|
|
21
|
+
.filter((c) => c.name && c.handle)
|
|
22
|
+
.slice(0, 14)
|
|
23
|
+
|
|
24
|
+
if (pills.length === 0) return null
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<section className={`w-full py-6 sm:py-8 ${cn.section}`}>
|
|
28
|
+
<div className="px-4 sm:px-6 md:px-8">
|
|
29
|
+
<div className="mx-auto max-w-[1360px]">
|
|
30
|
+
<div className="flex gap-3 sm:gap-4 overflow-x-auto pb-2 scrollbar-hide snap-x snap-mandatory">
|
|
31
|
+
{pills.map((category) => {
|
|
32
|
+
const thumb =
|
|
33
|
+
(category as { thumbnail?: string }).thumbnail ||
|
|
34
|
+
(category.metadata as { image?: string } | undefined)?.image
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<LocalizedClientLink
|
|
38
|
+
key={category.id}
|
|
39
|
+
href={`/categories/${category.handle}`}
|
|
40
|
+
className={`snap-start shrink-0 flex flex-col items-center gap-2 min-w-[88px] sm:min-w-[100px] group`}
|
|
41
|
+
>
|
|
42
|
+
<div
|
|
43
|
+
className={`w-16 h-16 sm:w-20 sm:h-20 rounded-full overflow-hidden ring-2 ring-[var(--sf-color-border)] group-hover:ring-[var(--sf-color-primary)] transition-all ${cn.cardSurface}`}
|
|
44
|
+
>
|
|
45
|
+
{thumb ? (
|
|
46
|
+
<Image
|
|
47
|
+
src={thumb}
|
|
48
|
+
alt={category.name ?? ""}
|
|
49
|
+
width={80}
|
|
50
|
+
height={80}
|
|
51
|
+
className="w-full h-full object-cover"
|
|
52
|
+
unoptimized
|
|
53
|
+
/>
|
|
54
|
+
) : (
|
|
55
|
+
<div
|
|
56
|
+
className={`w-full h-full flex items-center justify-center text-lg font-bold ${cn.imagePlaceholder} text-[var(--sf-color-primary)]`}
|
|
57
|
+
>
|
|
58
|
+
{category.name?.charAt(0)}
|
|
59
|
+
</div>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
<span
|
|
63
|
+
className={`text-[11px] sm:text-xs text-center font-medium leading-tight max-w-[92px] ${cn.categoryTitle}`}
|
|
64
|
+
>
|
|
65
|
+
{category.name}
|
|
66
|
+
</span>
|
|
67
|
+
</LocalizedClientLink>
|
|
68
|
+
)
|
|
69
|
+
})}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</section>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { HttpTypes } from "@medusajs/types"
|
|
4
|
+
import type { HomeThemeClassNames } from "medusa-storefront-theme-base"
|
|
5
|
+
import { useThemeSection } from "medusa-ui-common/providers"
|
|
6
|
+
import { ProductCardView } from "medusa-ui-product/product-cards"
|
|
7
|
+
import { getProductPrice } from "medusa-ui-common/util/get-product-price"
|
|
8
|
+
import { convertToLocale } from "medusa-ui-common/util/money"
|
|
9
|
+
import LocalizedClientLink from "medusa-ui-common/common/components/localized-client-link"
|
|
10
|
+
|
|
11
|
+
type CelebrityTrustProps = {
|
|
12
|
+
title?: string
|
|
13
|
+
subtitle?: string
|
|
14
|
+
product?: HttpTypes.StoreProduct | null
|
|
15
|
+
region: HttpTypes.StoreRegion
|
|
16
|
+
classNames?: Partial<HomeThemeClassNames>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default function CelebrityTrust({
|
|
20
|
+
title,
|
|
21
|
+
subtitle,
|
|
22
|
+
product,
|
|
23
|
+
region,
|
|
24
|
+
classNames: classNamesProp,
|
|
25
|
+
}: CelebrityTrustProps) {
|
|
26
|
+
const cn = useThemeSection("home", classNamesProp)
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<section className={`${cn.sectionPad} ${cn.section}`}>
|
|
30
|
+
<div className="px-4 sm:px-6 md:px-8 lg:px-12">
|
|
31
|
+
<div className="mx-auto max-w-[1360px]">
|
|
32
|
+
{title || subtitle ? (
|
|
33
|
+
<div className="text-center mb-8 sm:mb-10">
|
|
34
|
+
{title ? <h2 className={cn.sectionTitle}>{title}</h2> : null}
|
|
35
|
+
{subtitle ? (
|
|
36
|
+
<p className={`mt-3 text-sm sm:text-base ${cn.itemSubtitle}`}>{subtitle}</p>
|
|
37
|
+
) : null}
|
|
38
|
+
</div>
|
|
39
|
+
) : null}
|
|
40
|
+
|
|
41
|
+
{product ? (
|
|
42
|
+
<div className="grid md:grid-cols-2 gap-8 items-center">
|
|
43
|
+
<div className="flex justify-center">
|
|
44
|
+
<div className="w-full max-w-[320px]">
|
|
45
|
+
<ProductCardView product={product} region={region} />
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div className="space-y-4 text-center md:text-left">
|
|
49
|
+
<h3 className={`text-xl sm:text-2xl font-bold ${cn.itemTitle}`}>
|
|
50
|
+
{product.title}
|
|
51
|
+
</h3>
|
|
52
|
+
{(() => {
|
|
53
|
+
const { cheapestPrice } = getProductPrice({ product })
|
|
54
|
+
return cheapestPrice ? (
|
|
55
|
+
<p className={`text-lg font-semibold ${cn.itemTitle}`}>
|
|
56
|
+
{convertToLocale({
|
|
57
|
+
amount: cheapestPrice.calculated_price_number,
|
|
58
|
+
currency_code: cheapestPrice.currency_code,
|
|
59
|
+
})}
|
|
60
|
+
</p>
|
|
61
|
+
) : null
|
|
62
|
+
})()}
|
|
63
|
+
<LocalizedClientLink
|
|
64
|
+
href={`/products/${product.handle}`}
|
|
65
|
+
className={`inline-flex items-center justify-center rounded-full px-8 py-3 text-sm font-bold transition ${cn.cardSurface} bg-[var(--sf-btn-primary)] text-[var(--sf-color-text-on-primary)] hover:bg-[var(--sf-btn-primary-hover)]`}
|
|
66
|
+
>
|
|
67
|
+
Shop now
|
|
68
|
+
</LocalizedClientLink>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
) : (
|
|
72
|
+
<div className={`text-center py-12 rounded-2xl ${cn.banner}`}>
|
|
73
|
+
<p className={cn.emptyState}>Featured celebrity look coming soon.</p>
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</section>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
@@ -4,44 +4,48 @@ import {
|
|
|
4
4
|
type HomeThemeClassNames,
|
|
5
5
|
} from "medusa-storefront-theme-base"
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
name: "Easy Exchanges",
|
|
14
|
-
icon: "/exchnage.svg",
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
name: "Fast Delivery",
|
|
18
|
-
icon: "/fast.svg",
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
name: "Cash On Delivery",
|
|
22
|
-
icon: "/cash.svg",
|
|
23
|
-
},
|
|
24
|
-
]
|
|
7
|
+
export type TrustFeatureItem = {
|
|
8
|
+
name: string
|
|
9
|
+
icon: string
|
|
10
|
+
}
|
|
25
11
|
|
|
26
12
|
type FeaturesProps = {
|
|
13
|
+
title?: string | null
|
|
14
|
+
description?: string | null
|
|
15
|
+
features: TrustFeatureItem[]
|
|
27
16
|
classNames?: Partial<HomeThemeClassNames>
|
|
28
17
|
}
|
|
29
18
|
|
|
30
|
-
const Features = ({
|
|
19
|
+
const Features = ({
|
|
20
|
+
title,
|
|
21
|
+
description,
|
|
22
|
+
features,
|
|
23
|
+
classNames: classNamesProp,
|
|
24
|
+
}: FeaturesProps) => {
|
|
31
25
|
const cn = resolveThemeSection("home", classNamesProp)
|
|
26
|
+
if (!features.length) return null
|
|
32
27
|
|
|
33
28
|
return (
|
|
34
29
|
<div className={`w-full py-4 min-[350px]:py-5 sm:py-6 md:py-8 lg:py-10 px-3 min-[350px]:px-4 sm:px-6 md:px-8 lg:px-12 ${cn.section}`}>
|
|
35
30
|
<div className="max-w-[1360px] mx-auto w-full">
|
|
36
|
-
{
|
|
31
|
+
{title || description ? (
|
|
32
|
+
<div className="text-center mb-6 sm:mb-8 max-w-2xl mx-auto">
|
|
33
|
+
{title ? (
|
|
34
|
+
<h2 className={`${cn.sectionTitle} mb-2`}>{title}</h2>
|
|
35
|
+
) : null}
|
|
36
|
+
{description ? (
|
|
37
|
+
<p className={`text-sm sm:text-base ${cn.itemSubtitle}`}>{description}</p>
|
|
38
|
+
) : null}
|
|
39
|
+
</div>
|
|
40
|
+
) : null}
|
|
37
41
|
<div className="grid grid-cols-4 gap-2 sm:gap-4 md:gap-6 lg:gap-8 items-start justify-items-center">
|
|
38
42
|
{features.map((feature, index) => (
|
|
39
43
|
<div
|
|
40
|
-
key={feature.name}
|
|
44
|
+
key={`${feature.name}-${index}`}
|
|
41
45
|
className={`
|
|
42
46
|
flex flex-col sm:flex-row items-center justify-start
|
|
43
47
|
w-full relative px-1 sm:px-2
|
|
44
|
-
${index < features.length - 1 ? `sm:border-r ${cn.featureBorder}` :
|
|
48
|
+
${index < features.length - 1 ? `sm:border-r ${cn.featureBorder}` : ""}
|
|
45
49
|
`}
|
|
46
50
|
>
|
|
47
51
|
<div className="relative w-8 h-8 xs:w-10 xs:h-10 sm:w-10 sm:h-10 md:w-12 md:h-12 lg:w-14 lg:h-14 xl:w-20 xl:h-20 mb-1.5 sm:mb-0 sm:mr-2 md:mr-3 lg:mr-4 flex-shrink-0">
|
|
@@ -70,4 +74,3 @@ const Features = ({ classNames: classNamesProp }: FeaturesProps) => {
|
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
export default Features
|
|
73
|
-
|