medusa-storefront-data 2.1.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.
@@ -1,25 +1,48 @@
1
1
  "use server";
2
2
  import { listCollections } from "./collections";
3
3
  import { listCategories } from "./categories";
4
- import { listProductsWithSort, getProductsByTag } from "./products";
4
+ import { listProductsWithSort, getProductsByTag, listProducts } from "./products";
5
5
  import { getRegion } from "./regions";
6
- import { getTestimonialsFromConfig } from "./dynamic-config";
6
+ import { getTestimonialsFromConfig, getVideoStoriesFromConfig, getAnnouncementMessagesFromConfig, getAboutBrandFromConfig, getBrandPillarsFromConfig, getBlogPostsFromConfig, getPromoCountdownFromConfig, getHomeSectionsCopyFromConfig, getTrustFeaturesFromConfig, } from "./dynamic-config";
7
7
  import { fetchRatings } from "medusa-reviews-logic/server";
8
+ async function productsForCategoryIds(countryCode, categoryIds, limit) {
9
+ if (!categoryIds.length)
10
+ return [];
11
+ try {
12
+ const { response } = await listProducts({
13
+ countryCode,
14
+ queryParams: {
15
+ category_id: categoryIds,
16
+ limit,
17
+ },
18
+ });
19
+ return response.products ?? [];
20
+ }
21
+ catch {
22
+ return [];
23
+ }
24
+ }
25
+ function categoryIdsMatching(categories, pattern) {
26
+ return categories
27
+ .filter((c) => pattern.test((c.name || c.handle || "").toLowerCase()))
28
+ .map((c) => c.id)
29
+ .filter(Boolean);
30
+ }
8
31
  /**
9
- * Parallel home page data loader (replaces per-page Promise.all boilerplate).
32
+ * Parallel home page data loader for full storefront homepage sections.
10
33
  */
11
34
  export async function loadHomePageData(countryCode) {
12
35
  const region = await getRegion(countryCode);
13
36
  if (!region)
14
37
  return null;
15
- const [collectionsResult, categoriesResult, newArrivalsResult, bestsellersResult, testimonialsData, allRatings,] = await Promise.all([
38
+ const [collectionsResult, categoriesResult, newArrivalsResult, bestsellersResult, testimonialsData, allRatings, videoStoriesData, announcementMessages, promoCountdown, aboutBrand, brandPillars, blogPosts, themeByTag, baptismByTag, sectionsCopy, trustFeatures,] = await Promise.all([
16
39
  listCollections({ fields: "id, handle, title, metadata" }).catch(() => ({
17
40
  collections: [],
18
41
  })),
19
- listCategories({ limit: 50 }).catch(() => []),
42
+ listCategories({ limit: 100 }).catch(() => []),
20
43
  listProductsWithSort({
21
44
  page: 1,
22
- queryParams: { limit: 8 },
45
+ queryParams: { limit: 12 },
23
46
  sortBy: "created_at",
24
47
  countryCode,
25
48
  }).catch(() => ({ response: { products: [] } })),
@@ -28,18 +51,74 @@ export async function loadHomePageData(countryCode) {
28
51
  limit: 10,
29
52
  countryCode,
30
53
  }).catch(() => []),
31
- getTestimonialsFromConfig().catch(() => null),
54
+ getTestimonialsFromConfig().catch(() => ({
55
+ title: "",
56
+ testimonials: [],
57
+ })),
32
58
  fetchRatings(),
59
+ getVideoStoriesFromConfig().catch(() => ({
60
+ title: "",
61
+ videoUrls: [],
62
+ })),
63
+ getAnnouncementMessagesFromConfig(),
64
+ getPromoCountdownFromConfig(),
65
+ getAboutBrandFromConfig(),
66
+ getBrandPillarsFromConfig(),
67
+ getBlogPostsFromConfig(),
68
+ getProductsByTag({ tagValue: "theme", limit: 8, countryCode }).catch(() => []),
69
+ getProductsByTag({ tagValue: "baptism", limit: 12, countryCode }).catch(() => []),
70
+ getHomeSectionsCopyFromConfig(),
71
+ getTrustFeaturesFromConfig(),
33
72
  ]);
73
+ const categories = (categoriesResult ?? []);
34
74
  const collections = collectionsResult.collections ?? [];
35
- const newArrivals = newArrivalsResult.response?.products ?? [];
75
+ const newArrivals = newArrivalsResult
76
+ .response?.products ?? [];
77
+ const bestsellers = (bestsellersResult ?? []);
78
+ const girlIds = categoryIdsMatching(categories, /baptism.*girl|girl.*baptism|^girl$/);
79
+ const boyIds = categoryIdsMatching(categories, /baptism.*boy|boy.*baptism|^boy$/);
80
+ const baptismIds = categoryIdsMatching(categories, /baptism/);
81
+ const [baptismGirl, baptismBoy] = await Promise.all([
82
+ girlIds.length
83
+ ? productsForCategoryIds(countryCode, girlIds, 8)
84
+ : baptismByTag.filter((p) => /girl/i.test(p.title || "")).slice(0, 8),
85
+ boyIds.length
86
+ ? productsForCategoryIds(countryCode, boyIds, 8)
87
+ : baptismByTag.filter((p) => /boy/i.test(p.title || "")).slice(0, 8),
88
+ ]);
89
+ let baptismFallback = [];
90
+ if (baptismGirl.length === 0 && baptismBoy.length === 0) {
91
+ baptismFallback =
92
+ baptismIds.length > 0
93
+ ? await productsForCategoryIds(countryCode, baptismIds, 12)
94
+ : baptismByTag;
95
+ }
96
+ const resolvedBaptismGirl = baptismGirl.length > 0 ? baptismGirl : baptismFallback.slice(0, 8);
97
+ const resolvedBaptismBoy = baptismBoy.length > 0 ? baptismBoy : baptismFallback.slice(0, 8);
98
+ const themeProducts = themeByTag.length > 0
99
+ ? themeByTag
100
+ : await productsForCategoryIds(countryCode, categoryIdsMatching(categories, /theme/), 8);
36
101
  return {
37
102
  region,
38
103
  collections: collections,
39
- categories: categoriesResult ?? [],
104
+ categories,
40
105
  newArrivals,
41
- bestsellers: bestsellersResult ?? [],
106
+ bestsellers,
42
107
  testimonials: testimonialsData,
43
108
  ratings: allRatings ?? [],
109
+ videoStories: videoStoriesData,
110
+ announcementMessages,
111
+ promoCountdown,
112
+ luxeFavourites: bestsellers.slice(0, 4),
113
+ celebrityProduct: bestsellers[0] ?? newArrivals[0] ?? null,
114
+ baptismGirl: resolvedBaptismGirl,
115
+ baptismBoy: resolvedBaptismBoy,
116
+ baptismPicks: (baptismByTag.length ? baptismByTag : baptismFallback).slice(0, 5),
117
+ themeProducts,
118
+ aboutBrand,
119
+ brandPillars,
120
+ blogPosts,
121
+ sectionsCopy,
122
+ trustFeatures,
44
123
  };
45
124
  }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Default `homepage-config.sections` blocks.
3
+ * Dynamic config overrides any field; unset fields keep these defaults.
4
+ */
5
+ export declare const DEFAULT_HOMEPAGE_SECTIONS: {
6
+ readonly promoAnnouncements: {
7
+ readonly messages: readonly ["Free shipping on orders over a threshold", "Worldwide shipping available", "Selected products available with customisation", "Need help choosing? Book a video call consultation"];
8
+ };
9
+ readonly hero: {};
10
+ readonly categoryPills: {};
11
+ readonly newArrivals: {
12
+ readonly title: "New Arrivals";
13
+ readonly subtitle: "Fresh styles just landed";
14
+ };
15
+ readonly promoCountdown: {
16
+ readonly text: "Limited-time offer — shop your favourites before they're gone!";
17
+ readonly code: "SAVE20";
18
+ readonly active: false;
19
+ };
20
+ readonly celebrityTrust: {
21
+ readonly title: "Trusted by Celebrities";
22
+ readonly text: "When the stars dress their little ones in our collections.";
23
+ };
24
+ readonly luxeFavourites: {
25
+ readonly title: "Luxe Favourites";
26
+ };
27
+ readonly shopByCategory: {
28
+ readonly title: "Shop By Categories";
29
+ readonly description: "Browse by style and occasion";
30
+ };
31
+ readonly baptism: {
32
+ readonly title: "Baptism";
33
+ };
34
+ readonly baptismPicks: {
35
+ readonly title: "Baptism Picks";
36
+ };
37
+ readonly themeDresses: {
38
+ readonly title: "Theme";
39
+ readonly description: "Dress her in a dreamy theme outfit for her birthday celebration";
40
+ };
41
+ readonly brandMarquee: {
42
+ readonly text: "est. 2012";
43
+ };
44
+ readonly aboutBrand: {
45
+ readonly eyebrow: "Who We Are";
46
+ readonly title: "Discover what makes us different";
47
+ readonly description: "We offer stylish, comfortable, and high-quality clothing for children, crafted for everyday wear and special occasions.";
48
+ readonly readMoreHref: "/about";
49
+ readonly stats: readonly [{
50
+ readonly label: "Visit us in store";
51
+ readonly value: "Find a location";
52
+ }, {
53
+ readonly label: "Happy customers";
54
+ readonly value: "150k+";
55
+ }, {
56
+ readonly label: "Products";
57
+ readonly value: "3000+";
58
+ }];
59
+ };
60
+ readonly testimonials: {
61
+ readonly title: "What Clients Talk About Us";
62
+ readonly description: "The Trust We've Earned";
63
+ };
64
+ readonly brandPillars: {
65
+ readonly title: "This approach resulted in the beautiful structure";
66
+ readonly pillars: readonly [{
67
+ readonly title: "Your favorite kids destination";
68
+ readonly description: "Trendy, high-quality outfits for little ones—pieces designed for comfort, celebration, and the memories you make together.";
69
+ readonly image: "/features/store.jpg";
70
+ }, {
71
+ readonly title: "Video call consultation";
72
+ readonly description: "Shop from home with our video call service. Our team shows styles in real-time and helps you pick the perfect outfits.";
73
+ readonly image: "/features/video.jpg";
74
+ }, {
75
+ readonly title: "Design Your Dream Outfit";
76
+ readonly description: "We customize colors, themes, and handwork to create a perfectly fitted outfit for your little one's special moments.";
77
+ readonly image: "/features/design.jpg";
78
+ }, {
79
+ readonly title: "Luxury Fabrics, Made for Kids";
80
+ readonly description: "We use premium, breathable fabrics so each outfit feels luxurious and comfortable all day.";
81
+ readonly image: "/features/fabric.jpg";
82
+ }];
83
+ };
84
+ readonly features: {
85
+ readonly title: "Why shop with us";
86
+ readonly description: "Secure checkout, fast delivery, and easy exchanges";
87
+ readonly features: readonly [{
88
+ readonly name: "100% Secure";
89
+ readonly icon: "/secure.svg";
90
+ }, {
91
+ readonly name: "Easy Exchanges";
92
+ readonly icon: "/exchnage.svg";
93
+ }, {
94
+ readonly name: "Fast Delivery";
95
+ readonly icon: "/fast.svg";
96
+ }, {
97
+ readonly name: "Cash On Delivery";
98
+ readonly icon: "/cash.svg";
99
+ }];
100
+ };
101
+ readonly whyChooseUs: {
102
+ readonly title: "Why Choose Us?";
103
+ readonly features: readonly [{
104
+ readonly "feature-name": "Premium Quality";
105
+ readonly "feature-icon": "/icons/quality.svg";
106
+ }, {
107
+ readonly "feature-name": "Easy Returns";
108
+ readonly "feature-icon": "/icons/returns.svg";
109
+ }, {
110
+ readonly "feature-name": "Fast Delivery";
111
+ readonly "feature-icon": "/icons/delivery.svg";
112
+ }, {
113
+ readonly "feature-name": "Secure Checkout";
114
+ readonly "feature-icon": "/icons/secure.svg";
115
+ }];
116
+ };
117
+ readonly lovedByMoms: {
118
+ readonly title: "Loved by Moms";
119
+ readonly description: "Top-rated styles loved by parents and kids alike.";
120
+ };
121
+ readonly shopByAge: {
122
+ readonly title: "Shop By Age";
123
+ };
124
+ readonly videoStories: {
125
+ readonly title: "Watch Stories & Reviews";
126
+ readonly "video-urls": readonly [];
127
+ };
128
+ readonly blogPosts: {
129
+ readonly title: "Blog posts";
130
+ readonly posts: readonly [{
131
+ readonly title: "Which Theme Dress Matches Her Vibe?";
132
+ readonly href: "/blog/theme-dress-vibe";
133
+ readonly category: "Theme Dress";
134
+ readonly date: "May 24, 2026";
135
+ }, {
136
+ readonly title: "How to Choose a Birthday Frock That Matches Your Party Theme";
137
+ readonly href: "/blog/birthday-frock-party-theme";
138
+ readonly category: "Birthday Dress";
139
+ readonly date: "May 14, 2026";
140
+ }, {
141
+ readonly title: "Beyond Pink: Trending Colors in Kid's Party Wear";
142
+ readonly href: "/blog/trending-party-wear-colors";
143
+ readonly category: "Girls Party Dresses";
144
+ readonly date: "Apr 30, 2026";
145
+ }, {
146
+ readonly title: "Wearable Wonders: 3D Hand-Embroidered Theme Dresses";
147
+ readonly href: "/blog/3d-hand-embroidered-dresses";
148
+ readonly category: "Birthday dress for Baby Girl";
149
+ readonly date: "Apr 25, 2026";
150
+ }];
151
+ };
152
+ };
153
+ export type DefaultHomeSectionId = keyof typeof DEFAULT_HOMEPAGE_SECTIONS;
154
+ export declare const HOME_SECTION_COPY_ID_LIST: DefaultHomeSectionId[];
155
+ //# sourceMappingURL=homepage-section-defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homepage-section-defaults.d.ts","sourceRoot":"","sources":["../../src/server/homepage-section-defaults.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqJsB,CAAA;AAE5D,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,yBAAyB,CAAA;AAEzE,eAAO,MAAM,yBAAyB,EAEjC,oBAAoB,EAAE,CAAA"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Default `homepage-config.sections` blocks.
3
+ * Dynamic config overrides any field; unset fields keep these defaults.
4
+ */
5
+ export const DEFAULT_HOMEPAGE_SECTIONS = {
6
+ promoAnnouncements: {
7
+ messages: [
8
+ "Free shipping on orders over a threshold",
9
+ "Worldwide shipping available",
10
+ "Selected products available with customisation",
11
+ "Need help choosing? Book a video call consultation",
12
+ ],
13
+ },
14
+ hero: {},
15
+ categoryPills: {},
16
+ newArrivals: {
17
+ title: "New Arrivals",
18
+ subtitle: "Fresh styles just landed",
19
+ },
20
+ promoCountdown: {
21
+ text: "Limited-time offer — shop your favourites before they're gone!",
22
+ code: "SAVE20",
23
+ active: false,
24
+ },
25
+ celebrityTrust: {
26
+ title: "Trusted by Celebrities",
27
+ text: "When the stars dress their little ones in our collections.",
28
+ },
29
+ luxeFavourites: {
30
+ title: "Luxe Favourites",
31
+ },
32
+ shopByCategory: {
33
+ title: "Shop By Categories",
34
+ description: "Browse by style and occasion",
35
+ },
36
+ baptism: {
37
+ title: "Baptism",
38
+ },
39
+ baptismPicks: {
40
+ title: "Baptism Picks",
41
+ },
42
+ themeDresses: {
43
+ title: "Theme",
44
+ description: "Dress her in a dreamy theme outfit for her birthday celebration",
45
+ },
46
+ brandMarquee: {
47
+ text: "est. 2012",
48
+ },
49
+ aboutBrand: {
50
+ eyebrow: "Who We Are",
51
+ title: "Discover what makes us different",
52
+ description: "We offer stylish, comfortable, and high-quality clothing for children, crafted for everyday wear and special occasions.",
53
+ readMoreHref: "/about",
54
+ stats: [
55
+ { label: "Visit us in store", value: "Find a location" },
56
+ { label: "Happy customers", value: "150k+" },
57
+ { label: "Products", value: "3000+" },
58
+ ],
59
+ },
60
+ testimonials: {
61
+ title: "What Clients Talk About Us",
62
+ description: "The Trust We've Earned",
63
+ },
64
+ brandPillars: {
65
+ title: "This approach resulted in the beautiful structure",
66
+ pillars: [
67
+ {
68
+ title: "Your favorite kids destination",
69
+ description: "Trendy, high-quality outfits for little ones—pieces designed for comfort, celebration, and the memories you make together.",
70
+ image: "/features/store.jpg",
71
+ },
72
+ {
73
+ title: "Video call consultation",
74
+ description: "Shop from home with our video call service. Our team shows styles in real-time and helps you pick the perfect outfits.",
75
+ image: "/features/video.jpg",
76
+ },
77
+ {
78
+ title: "Design Your Dream Outfit",
79
+ description: "We customize colors, themes, and handwork to create a perfectly fitted outfit for your little one's special moments.",
80
+ image: "/features/design.jpg",
81
+ },
82
+ {
83
+ title: "Luxury Fabrics, Made for Kids",
84
+ description: "We use premium, breathable fabrics so each outfit feels luxurious and comfortable all day.",
85
+ image: "/features/fabric.jpg",
86
+ },
87
+ ],
88
+ },
89
+ features: {
90
+ title: "Why shop with us",
91
+ description: "Secure checkout, fast delivery, and easy exchanges",
92
+ features: [
93
+ { name: "100% Secure", icon: "/secure.svg" },
94
+ { name: "Easy Exchanges", icon: "/exchnage.svg" },
95
+ { name: "Fast Delivery", icon: "/fast.svg" },
96
+ { name: "Cash On Delivery", icon: "/cash.svg" },
97
+ ],
98
+ },
99
+ whyChooseUs: {
100
+ title: "Why Choose Us?",
101
+ features: [
102
+ { "feature-name": "Premium Quality", "feature-icon": "/icons/quality.svg" },
103
+ { "feature-name": "Easy Returns", "feature-icon": "/icons/returns.svg" },
104
+ { "feature-name": "Fast Delivery", "feature-icon": "/icons/delivery.svg" },
105
+ { "feature-name": "Secure Checkout", "feature-icon": "/icons/secure.svg" },
106
+ ],
107
+ },
108
+ lovedByMoms: {
109
+ title: "Loved by Moms",
110
+ description: "Top-rated styles loved by parents and kids alike.",
111
+ },
112
+ shopByAge: {
113
+ title: "Shop By Age",
114
+ },
115
+ videoStories: {
116
+ title: "Watch Stories & Reviews",
117
+ "video-urls": [],
118
+ },
119
+ blogPosts: {
120
+ title: "Blog posts",
121
+ posts: [
122
+ {
123
+ title: "Which Theme Dress Matches Her Vibe?",
124
+ href: "/blog/theme-dress-vibe",
125
+ category: "Theme Dress",
126
+ date: "May 24, 2026",
127
+ },
128
+ {
129
+ title: "How to Choose a Birthday Frock That Matches Your Party Theme",
130
+ href: "/blog/birthday-frock-party-theme",
131
+ category: "Birthday Dress",
132
+ date: "May 14, 2026",
133
+ },
134
+ {
135
+ title: "Beyond Pink: Trending Colors in Kid's Party Wear",
136
+ href: "/blog/trending-party-wear-colors",
137
+ category: "Girls Party Dresses",
138
+ date: "Apr 30, 2026",
139
+ },
140
+ {
141
+ title: "Wearable Wonders: 3D Hand-Embroidered Theme Dresses",
142
+ href: "/blog/3d-hand-embroidered-dresses",
143
+ category: "Birthday dress for Baby Girl",
144
+ date: "Apr 25, 2026",
145
+ },
146
+ ],
147
+ },
148
+ };
149
+ export const HOME_SECTION_COPY_ID_LIST = Object.keys(DEFAULT_HOMEPAGE_SECTIONS);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "medusa-storefront-data",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "type": "module",
5
5
  "description": "Medusa storefront server data layer extracted from Next.js storefront",
6
6
  "license": "MIT",