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.
Files changed (47) hide show
  1. package/package.json +13 -1
  2. package/src/home/components/about-brand/index.tsx +71 -0
  3. package/src/home/components/baptism-picks/index.tsx +70 -0
  4. package/src/home/components/baptism-section/index.tsx +68 -0
  5. package/src/home/components/blog-posts/index.tsx +85 -0
  6. package/src/home/components/brand-marquee/index.tsx +39 -0
  7. package/src/home/components/brand-pillars/index.tsx +77 -0
  8. package/src/home/components/category-pills/index.tsx +75 -0
  9. package/src/home/components/celebrity-trust/index.tsx +80 -0
  10. package/src/home/components/features/index.tsx +26 -23
  11. package/src/home/components/loved-by-moms/index.tsx +16 -8
  12. package/src/home/components/luxe-favourites/index.tsx +88 -0
  13. package/src/home/components/new-arrivals/index.tsx +18 -4
  14. package/src/home/components/promo-announcements/index.tsx +46 -0
  15. package/src/home/components/promo-countdown/index.tsx +87 -0
  16. package/src/home/components/shared/product-grid-section.tsx +114 -0
  17. package/src/home/components/shop-by-age/index.tsx +16 -6
  18. package/src/home/components/shop-by-category/index.tsx +18 -4
  19. package/src/home/components/testimonials/index.tsx +1 -1
  20. package/src/home/components/theme-dresses/index.tsx +39 -0
  21. package/src/home/components/video-stories/index.tsx +488 -0
  22. package/src/home/components/why-choose-us/dynamic-features.tsx +8 -6
  23. package/src/home/home-content.tsx +33 -0
  24. package/src/home/lib/section-copy.ts +35 -0
  25. package/src/home/register-sections.ts +69 -0
  26. package/src/home/sections/about-brand-section.tsx +23 -0
  27. package/src/home/sections/baptism-picks-section.tsx +8 -0
  28. package/src/home/sections/baptism-section.tsx +17 -0
  29. package/src/home/sections/blog-posts-section.tsx +8 -0
  30. package/src/home/sections/brand-marquee-section.tsx +13 -0
  31. package/src/home/sections/brand-pillars-section.tsx +19 -0
  32. package/src/home/sections/category-pills-section.tsx +6 -0
  33. package/src/home/sections/celebrity-trust-section.tsx +20 -0
  34. package/src/home/sections/features-section.tsx +20 -0
  35. package/src/home/sections/hero-section.tsx +6 -0
  36. package/src/home/sections/loved-by-moms-section.tsx +21 -0
  37. package/src/home/sections/luxe-favourites-section.tsx +15 -0
  38. package/src/home/sections/new-arrivals-section.tsx +21 -0
  39. package/src/home/sections/promo-announcements-section.tsx +6 -0
  40. package/src/home/sections/promo-countdown-section.tsx +15 -0
  41. package/src/home/sections/shop-by-age-section.tsx +19 -0
  42. package/src/home/sections/shop-by-category-section.tsx +18 -0
  43. package/src/home/sections/testimonials-section.tsx +28 -0
  44. package/src/home/sections/theme-dresses-section.tsx +21 -0
  45. package/src/home/sections/video-stories-section.tsx +15 -0
  46. package/src/home/sections/why-choose-us-section.tsx +8 -0
  47. 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.2.0",
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
- const features = [
8
- {
9
- name: "100% Secure",
10
- icon: "/secure.svg",
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 = ({ classNames: classNamesProp }: FeaturesProps) => {
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
- {/* Responsive Grid: 4 columns across all screens, but adjusted sizing/layout */}
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
-