medusa-storefront-data 2.1.0 → 2.4.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 (269) hide show
  1. package/HOMEPAGE_CONFIG.md +99 -0
  2. package/dist/segment-data/default-page-input.json +191 -0
  3. package/dist/src/config.d.ts.map +1 -0
  4. package/dist/src/cookies.d.ts.map +1 -0
  5. package/dist/src/edge.d.ts.map +1 -0
  6. package/dist/src/middleware.d.ts.map +1 -0
  7. package/dist/src/server/cart.d.ts.map +1 -0
  8. package/dist/src/server/categories.d.ts.map +1 -0
  9. package/dist/src/server/collections.d.ts.map +1 -0
  10. package/dist/src/server/config-merge.d.ts +3 -0
  11. package/dist/src/server/config-merge.d.ts.map +1 -0
  12. package/dist/src/server/config-merge.js +31 -0
  13. package/dist/src/server/contact.d.ts.map +1 -0
  14. package/dist/src/server/customer-registration.d.ts.map +1 -0
  15. package/dist/src/server/customer.d.ts.map +1 -0
  16. package/dist/src/server/dynamic-config.d.ts +240 -0
  17. package/dist/src/server/dynamic-config.d.ts.map +1 -0
  18. package/dist/src/server/dynamic-config.js +703 -0
  19. package/dist/src/server/fulfillment.d.ts.map +1 -0
  20. package/dist/src/server/guest.d.ts.map +1 -0
  21. package/dist/src/server/home-sections/about-brand.d.ts +9 -0
  22. package/dist/src/server/home-sections/about-brand.d.ts.map +1 -0
  23. package/dist/src/server/home-sections/about-brand.js +9 -0
  24. package/dist/src/server/home-sections/baptism-picks.d.ts +10 -0
  25. package/dist/src/server/home-sections/baptism-picks.d.ts.map +1 -0
  26. package/dist/src/server/home-sections/baptism-picks.js +27 -0
  27. package/dist/src/server/home-sections/baptism.d.ts +13 -0
  28. package/dist/src/server/home-sections/baptism.d.ts.map +1 -0
  29. package/dist/src/server/home-sections/baptism.js +43 -0
  30. package/dist/src/server/home-sections/blog-posts.d.ts +9 -0
  31. package/dist/src/server/home-sections/blog-posts.d.ts.map +1 -0
  32. package/dist/src/server/home-sections/blog-posts.js +9 -0
  33. package/dist/src/server/home-sections/brand-marquee.d.ts +8 -0
  34. package/dist/src/server/home-sections/brand-marquee.d.ts.map +1 -0
  35. package/dist/src/server/home-sections/brand-marquee.js +7 -0
  36. package/dist/src/server/home-sections/brand-pillars.d.ts +9 -0
  37. package/dist/src/server/home-sections/brand-pillars.d.ts.map +1 -0
  38. package/dist/src/server/home-sections/brand-pillars.js +9 -0
  39. package/dist/src/server/home-sections/category-pills.d.ts +9 -0
  40. package/dist/src/server/home-sections/category-pills.d.ts.map +1 -0
  41. package/dist/src/server/home-sections/category-pills.js +19 -0
  42. package/dist/src/server/home-sections/celebrity-trust.d.ts +12 -0
  43. package/dist/src/server/home-sections/celebrity-trust.d.ts.map +1 -0
  44. package/dist/src/server/home-sections/celebrity-trust.js +27 -0
  45. package/dist/src/server/home-sections/features.d.ts +9 -0
  46. package/dist/src/server/home-sections/features.d.ts.map +1 -0
  47. package/dist/src/server/home-sections/features.js +9 -0
  48. package/dist/src/server/home-sections/hero.d.ts +9 -0
  49. package/dist/src/server/home-sections/hero.d.ts.map +1 -0
  50. package/dist/src/server/home-sections/hero.js +9 -0
  51. package/dist/src/server/home-sections/instagram-posts.d.ts +9 -0
  52. package/dist/src/server/home-sections/instagram-posts.d.ts.map +1 -0
  53. package/dist/src/server/home-sections/instagram-posts.js +9 -0
  54. package/dist/src/server/home-sections/loved-by-moms.d.ts +12 -0
  55. package/dist/src/server/home-sections/loved-by-moms.d.ts.map +1 -0
  56. package/dist/src/server/home-sections/loved-by-moms.js +20 -0
  57. package/dist/src/server/home-sections/luxe-favourites.d.ts +11 -0
  58. package/dist/src/server/home-sections/luxe-favourites.d.ts.map +1 -0
  59. package/dist/src/server/home-sections/luxe-favourites.js +19 -0
  60. package/dist/src/server/home-sections/new-arrivals-classic.d.ts +8 -0
  61. package/dist/src/server/home-sections/new-arrivals-classic.d.ts.map +1 -0
  62. package/dist/src/server/home-sections/new-arrivals-classic.js +10 -0
  63. package/dist/src/server/home-sections/new-arrivals.d.ts +12 -0
  64. package/dist/src/server/home-sections/new-arrivals.d.ts.map +1 -0
  65. package/dist/src/server/home-sections/new-arrivals.js +28 -0
  66. package/dist/src/server/home-sections/promo-announcements.d.ts +7 -0
  67. package/dist/src/server/home-sections/promo-announcements.d.ts.map +1 -0
  68. package/dist/src/server/home-sections/promo-announcements.js +7 -0
  69. package/dist/src/server/home-sections/promo-countdown.d.ts +6 -0
  70. package/dist/src/server/home-sections/promo-countdown.d.ts.map +1 -0
  71. package/dist/src/server/home-sections/promo-countdown.js +5 -0
  72. package/dist/src/server/home-sections/shared.d.ts +10 -0
  73. package/dist/src/server/home-sections/shared.d.ts.map +1 -0
  74. package/dist/src/server/home-sections/shared.js +29 -0
  75. package/dist/src/server/home-sections/shop-by-age.d.ts +9 -0
  76. package/dist/src/server/home-sections/shop-by-age.d.ts.map +1 -0
  77. package/dist/src/server/home-sections/shop-by-age.js +17 -0
  78. package/dist/src/server/home-sections/shop-by-category.d.ts +10 -0
  79. package/dist/src/server/home-sections/shop-by-category.d.ts.map +1 -0
  80. package/dist/src/server/home-sections/shop-by-category.js +17 -0
  81. package/dist/src/server/home-sections/testimonials.d.ts +9 -0
  82. package/dist/src/server/home-sections/testimonials.d.ts.map +1 -0
  83. package/dist/src/server/home-sections/testimonials.js +9 -0
  84. package/dist/src/server/home-sections/theme-dresses.d.ts +12 -0
  85. package/dist/src/server/home-sections/theme-dresses.d.ts.map +1 -0
  86. package/dist/src/server/home-sections/theme-dresses.js +27 -0
  87. package/dist/src/server/home-sections/video-stories.d.ts +9 -0
  88. package/dist/src/server/home-sections/video-stories.d.ts.map +1 -0
  89. package/dist/src/server/home-sections/video-stories.js +9 -0
  90. package/dist/src/server/home-sections/why-choose-us.d.ts +9 -0
  91. package/dist/src/server/home-sections/why-choose-us.d.ts.map +1 -0
  92. package/dist/src/server/home-sections/why-choose-us.js +9 -0
  93. package/dist/src/server/home.d.ts +62 -0
  94. package/dist/src/server/home.d.ts.map +1 -0
  95. package/dist/src/server/home.js +122 -0
  96. package/dist/src/server/homepage-config.types.d.ts +274 -0
  97. package/dist/src/server/homepage-config.types.d.ts.map +1 -0
  98. package/dist/src/server/homepage-config.types.js +33 -0
  99. package/dist/src/server/homepage-section-defaults.d.ts +188 -0
  100. package/dist/src/server/homepage-section-defaults.d.ts.map +1 -0
  101. package/dist/src/server/homepage-section-defaults.js +188 -0
  102. package/dist/{server → src/server}/index.d.ts +2 -0
  103. package/dist/src/server/index.d.ts.map +1 -0
  104. package/dist/{server → src/server}/index.js +2 -0
  105. package/dist/src/server/locale-actions.d.ts.map +1 -0
  106. package/dist/src/server/locales.d.ts.map +1 -0
  107. package/dist/src/server/notifications.d.ts.map +1 -0
  108. package/dist/src/server/onboarding.d.ts.map +1 -0
  109. package/dist/src/server/orders.d.ts.map +1 -0
  110. package/dist/src/server/page-input.d.ts +19 -0
  111. package/dist/src/server/page-input.d.ts.map +1 -0
  112. package/dist/src/server/page-input.js +40 -0
  113. package/dist/src/server/payment-details.d.ts.map +1 -0
  114. package/dist/src/server/payment.d.ts.map +1 -0
  115. package/dist/src/server/pincode.d.ts.map +1 -0
  116. package/dist/src/server/products.d.ts.map +1 -0
  117. package/dist/src/server/regions.d.ts.map +1 -0
  118. package/dist/src/server/resolve-home-segment-data.d.ts +25 -0
  119. package/dist/src/server/resolve-home-segment-data.d.ts.map +1 -0
  120. package/dist/src/server/resolve-home-segment-data.js +20 -0
  121. package/dist/src/server/returns.d.ts.map +1 -0
  122. package/dist/src/server/shoppable-looks.d.ts +17 -0
  123. package/dist/src/server/shoppable-looks.d.ts.map +1 -0
  124. package/dist/src/server/shoppable-looks.js +136 -0
  125. package/dist/src/server/swaps.d.ts.map +1 -0
  126. package/dist/src/server/variants.d.ts.map +1 -0
  127. package/dist/src/server/wishlist.d.ts.map +1 -0
  128. package/dist/src/util/get-locale-header.d.ts.map +1 -0
  129. package/dist/src/util/medusa-error.d.ts.map +1 -0
  130. package/dist/src/util/revalidate-cart.d.ts.map +1 -0
  131. package/dist/src/util/sort-products.d.ts.map +1 -0
  132. package/dist/src/util/store-client.d.ts.map +1 -0
  133. package/homepage-config.example.json +209 -0
  134. package/homepage-sections.example.json +55 -0
  135. package/package.json +121 -2
  136. package/segment-data/default-page-input.json +191 -0
  137. package/src/server/config-merge.ts +31 -0
  138. package/src/server/dynamic-config.ts +838 -248
  139. package/src/server/home-sections/about-brand.ts +24 -0
  140. package/src/server/home-sections/baptism-picks.ts +46 -0
  141. package/src/server/home-sections/baptism.ts +64 -0
  142. package/src/server/home-sections/blog-posts.ts +23 -0
  143. package/src/server/home-sections/brand-marquee.ts +17 -0
  144. package/src/server/home-sections/brand-pillars.ts +23 -0
  145. package/src/server/home-sections/category-pills.ts +32 -0
  146. package/src/server/home-sections/celebrity-trust.ts +42 -0
  147. package/src/server/home-sections/features.ts +23 -0
  148. package/src/server/home-sections/hero.ts +24 -0
  149. package/src/server/home-sections/instagram-posts.ts +23 -0
  150. package/src/server/home-sections/loved-by-moms.ts +34 -0
  151. package/src/server/home-sections/luxe-favourites.ts +30 -0
  152. package/src/server/home-sections/new-arrivals-classic.ts +21 -0
  153. package/src/server/home-sections/new-arrivals.ts +47 -0
  154. package/src/server/home-sections/promo-announcements.ts +17 -0
  155. package/src/server/home-sections/promo-countdown.ts +15 -0
  156. package/src/server/home-sections/shared.ts +44 -0
  157. package/src/server/home-sections/shop-by-age.ts +26 -0
  158. package/src/server/home-sections/shop-by-category.ts +28 -0
  159. package/src/server/home-sections/testimonials.ts +23 -0
  160. package/src/server/home-sections/theme-dresses.ts +49 -0
  161. package/src/server/home-sections/video-stories.ts +23 -0
  162. package/src/server/home-sections/why-choose-us.ts +23 -0
  163. package/src/server/home.ts +173 -16
  164. package/src/server/homepage-config.types.ts +335 -0
  165. package/src/server/homepage-section-defaults.ts +199 -0
  166. package/src/server/index.ts +2 -0
  167. package/src/server/page-input.ts +64 -0
  168. package/src/server/resolve-home-segment-data.ts +63 -0
  169. package/src/server/shoppable-looks.ts +178 -0
  170. package/dist/config.d.ts.map +0 -1
  171. package/dist/cookies.d.ts.map +0 -1
  172. package/dist/edge.d.ts.map +0 -1
  173. package/dist/middleware.d.ts.map +0 -1
  174. package/dist/server/cart.d.ts.map +0 -1
  175. package/dist/server/categories.d.ts.map +0 -1
  176. package/dist/server/collections.d.ts.map +0 -1
  177. package/dist/server/contact.d.ts.map +0 -1
  178. package/dist/server/customer-registration.d.ts.map +0 -1
  179. package/dist/server/customer.d.ts.map +0 -1
  180. package/dist/server/dynamic-config.d.ts +0 -125
  181. package/dist/server/dynamic-config.d.ts.map +0 -1
  182. package/dist/server/dynamic-config.js +0 -289
  183. package/dist/server/fulfillment.d.ts.map +0 -1
  184. package/dist/server/guest.d.ts.map +0 -1
  185. package/dist/server/home.d.ts +0 -15
  186. package/dist/server/home.d.ts.map +0 -1
  187. package/dist/server/home.js +0 -45
  188. package/dist/server/index.d.ts.map +0 -1
  189. package/dist/server/locale-actions.d.ts.map +0 -1
  190. package/dist/server/locales.d.ts.map +0 -1
  191. package/dist/server/notifications.d.ts.map +0 -1
  192. package/dist/server/onboarding.d.ts.map +0 -1
  193. package/dist/server/orders.d.ts.map +0 -1
  194. package/dist/server/payment-details.d.ts.map +0 -1
  195. package/dist/server/payment.d.ts.map +0 -1
  196. package/dist/server/pincode.d.ts.map +0 -1
  197. package/dist/server/products.d.ts.map +0 -1
  198. package/dist/server/regions.d.ts.map +0 -1
  199. package/dist/server/returns.d.ts.map +0 -1
  200. package/dist/server/swaps.d.ts.map +0 -1
  201. package/dist/server/variants.d.ts.map +0 -1
  202. package/dist/server/wishlist.d.ts.map +0 -1
  203. package/dist/util/get-locale-header.d.ts.map +0 -1
  204. package/dist/util/medusa-error.d.ts.map +0 -1
  205. package/dist/util/revalidate-cart.d.ts.map +0 -1
  206. package/dist/util/sort-products.d.ts.map +0 -1
  207. package/dist/util/store-client.d.ts.map +0 -1
  208. /package/dist/{config.d.ts → src/config.d.ts} +0 -0
  209. /package/dist/{config.js → src/config.js} +0 -0
  210. /package/dist/{cookies.d.ts → src/cookies.d.ts} +0 -0
  211. /package/dist/{cookies.js → src/cookies.js} +0 -0
  212. /package/dist/{edge.d.ts → src/edge.d.ts} +0 -0
  213. /package/dist/{edge.js → src/edge.js} +0 -0
  214. /package/dist/{middleware.d.ts → src/middleware.d.ts} +0 -0
  215. /package/dist/{middleware.js → src/middleware.js} +0 -0
  216. /package/dist/{server → src/server}/cart.d.ts +0 -0
  217. /package/dist/{server → src/server}/cart.js +0 -0
  218. /package/dist/{server → src/server}/categories.d.ts +0 -0
  219. /package/dist/{server → src/server}/categories.js +0 -0
  220. /package/dist/{server → src/server}/collections.d.ts +0 -0
  221. /package/dist/{server → src/server}/collections.js +0 -0
  222. /package/dist/{server → src/server}/contact.d.ts +0 -0
  223. /package/dist/{server → src/server}/contact.js +0 -0
  224. /package/dist/{server → src/server}/customer-registration.d.ts +0 -0
  225. /package/dist/{server → src/server}/customer-registration.js +0 -0
  226. /package/dist/{server → src/server}/customer.d.ts +0 -0
  227. /package/dist/{server → src/server}/customer.js +0 -0
  228. /package/dist/{server → src/server}/fulfillment.d.ts +0 -0
  229. /package/dist/{server → src/server}/fulfillment.js +0 -0
  230. /package/dist/{server → src/server}/guest.d.ts +0 -0
  231. /package/dist/{server → src/server}/guest.js +0 -0
  232. /package/dist/{server → src/server}/locale-actions.d.ts +0 -0
  233. /package/dist/{server → src/server}/locale-actions.js +0 -0
  234. /package/dist/{server → src/server}/locales.d.ts +0 -0
  235. /package/dist/{server → src/server}/locales.js +0 -0
  236. /package/dist/{server → src/server}/notifications.d.ts +0 -0
  237. /package/dist/{server → src/server}/notifications.js +0 -0
  238. /package/dist/{server → src/server}/onboarding.d.ts +0 -0
  239. /package/dist/{server → src/server}/onboarding.js +0 -0
  240. /package/dist/{server → src/server}/orders.d.ts +0 -0
  241. /package/dist/{server → src/server}/orders.js +0 -0
  242. /package/dist/{server → src/server}/payment-details.d.ts +0 -0
  243. /package/dist/{server → src/server}/payment-details.js +0 -0
  244. /package/dist/{server → src/server}/payment.d.ts +0 -0
  245. /package/dist/{server → src/server}/payment.js +0 -0
  246. /package/dist/{server → src/server}/pincode.d.ts +0 -0
  247. /package/dist/{server → src/server}/pincode.js +0 -0
  248. /package/dist/{server → src/server}/products.d.ts +0 -0
  249. /package/dist/{server → src/server}/products.js +0 -0
  250. /package/dist/{server → src/server}/regions.d.ts +0 -0
  251. /package/dist/{server → src/server}/regions.js +0 -0
  252. /package/dist/{server → src/server}/returns.d.ts +0 -0
  253. /package/dist/{server → src/server}/returns.js +0 -0
  254. /package/dist/{server → src/server}/swaps.d.ts +0 -0
  255. /package/dist/{server → src/server}/swaps.js +0 -0
  256. /package/dist/{server → src/server}/variants.d.ts +0 -0
  257. /package/dist/{server → src/server}/variants.js +0 -0
  258. /package/dist/{server → src/server}/wishlist.d.ts +0 -0
  259. /package/dist/{server → src/server}/wishlist.js +0 -0
  260. /package/dist/{util → src/util}/get-locale-header.d.ts +0 -0
  261. /package/dist/{util → src/util}/get-locale-header.js +0 -0
  262. /package/dist/{util → src/util}/medusa-error.d.ts +0 -0
  263. /package/dist/{util → src/util}/medusa-error.js +0 -0
  264. /package/dist/{util → src/util}/revalidate-cart.d.ts +0 -0
  265. /package/dist/{util → src/util}/revalidate-cart.js +0 -0
  266. /package/dist/{util → src/util}/sort-products.d.ts +0 -0
  267. /package/dist/{util → src/util}/sort-products.js +0 -0
  268. /package/dist/{util → src/util}/store-client.d.ts +0 -0
  269. /package/dist/{util → src/util}/store-client.js +0 -0
@@ -0,0 +1,49 @@
1
+ import "server-only"
2
+
3
+ import { HttpTypes } from "@medusajs/types"
4
+ import { fetchRatings } from "medusa-reviews-logic/server"
5
+ import { getHomeSectionsCopyFromPageInput } from "../dynamic-config"
6
+ import { listCategories } from "../categories"
7
+ import { getProductsByTag } from "../products"
8
+ import {
9
+ categoryIdsMatching,
10
+ productsForCategoryIds,
11
+ requireRegion,
12
+ type SectionLoaderOptions,
13
+ } from "./shared"
14
+
15
+ export type ThemeDressesSectionData = {
16
+ region: NonNullable<Awaited<ReturnType<typeof requireRegion>>>
17
+ themeProducts: HttpTypes.StoreProduct[]
18
+ ratings: unknown[]
19
+ copy: ReturnType<typeof getHomeSectionsCopyFromPageInput>["themeDresses"]
20
+ }
21
+
22
+ export async function loadThemeDressesSectionData(
23
+ countryCode: string,
24
+ options?: SectionLoaderOptions
25
+ ): Promise<ThemeDressesSectionData | null> {
26
+ const region = await requireRegion(countryCode)
27
+ if (!region) return null
28
+ const pageInput = options?.pageInput ?? null
29
+ const [themeByTag, categoriesResult, ratings] = await Promise.all([
30
+ getProductsByTag({ tagValue: "theme", limit: 8, countryCode }).catch(() => []),
31
+ listCategories({ limit: 100 }).catch(() => []),
32
+ fetchRatings(),
33
+ ])
34
+ const categories = (categoriesResult ?? []) as HttpTypes.StoreProductCategory[]
35
+ const themeProducts =
36
+ themeByTag.length > 0
37
+ ? themeByTag
38
+ : await productsForCategoryIds(
39
+ countryCode,
40
+ categoryIdsMatching(categories, /theme/),
41
+ 8
42
+ )
43
+ return {
44
+ region,
45
+ themeProducts,
46
+ ratings: ratings ?? [],
47
+ copy: getHomeSectionsCopyFromPageInput(pageInput).themeDresses,
48
+ }
49
+ }
@@ -0,0 +1,23 @@
1
+ import "server-only"
2
+
3
+ import {
4
+ getHomeSectionsCopyFromPageInput,
5
+ getVideoStoriesFromPageInput,
6
+ } from "../dynamic-config"
7
+ import type { SectionLoaderOptions } from "./shared"
8
+
9
+ export type VideoStoriesSectionData = {
10
+ videoStories: ReturnType<typeof getVideoStoriesFromPageInput>
11
+ copy: ReturnType<typeof getHomeSectionsCopyFromPageInput>["videoStories"]
12
+ }
13
+
14
+ export async function loadVideoStoriesSectionData(
15
+ _countryCode: string,
16
+ options?: SectionLoaderOptions
17
+ ): Promise<VideoStoriesSectionData> {
18
+ const pageInput = options?.pageInput ?? null
19
+ return {
20
+ videoStories: getVideoStoriesFromPageInput(pageInput),
21
+ copy: getHomeSectionsCopyFromPageInput(pageInput).videoStories,
22
+ }
23
+ }
@@ -0,0 +1,23 @@
1
+ import "server-only"
2
+
3
+ import {
4
+ getFeaturesFromPageInput,
5
+ getHomeSectionsCopyFromPageInput,
6
+ } from "../dynamic-config"
7
+ import type { SectionLoaderOptions } from "./shared"
8
+
9
+ export type WhyChooseUsSectionData = {
10
+ whyChooseUsFeatures: ReturnType<typeof getFeaturesFromPageInput>
11
+ copy: ReturnType<typeof getHomeSectionsCopyFromPageInput>["whyChooseUs"]
12
+ }
13
+
14
+ export async function loadWhyChooseUsSectionData(
15
+ _countryCode: string,
16
+ options?: SectionLoaderOptions
17
+ ): Promise<WhyChooseUsSectionData> {
18
+ const pageInput = options?.pageInput ?? null
19
+ return {
20
+ whyChooseUsFeatures: getFeaturesFromPageInput(pageInput),
21
+ copy: getHomeSectionsCopyFromPageInput(pageInput).whyChooseUs,
22
+ }
23
+ }
@@ -1,26 +1,111 @@
1
- "use server";
1
+ import "server-only";
2
2
 
3
+ import { HttpTypes } from "@medusajs/types";
3
4
  import { listCollections } from "./collections";
4
5
  import { listCategories } from "./categories";
5
- import { listProductsWithSort, getProductsByTag } from "./products";
6
+ import { listProductsWithSort, getProductsByTag, listProducts } from "./products";
6
7
  import { getRegion } from "./regions";
7
- import { getTestimonialsFromConfig } from "./dynamic-config";
8
+ import type { HomepageConfig } from "./homepage-config.types";
9
+ import type {
10
+ InstagramPostsData,
11
+ AboutBrandConfig,
12
+ BrandPillarConfig,
13
+ BlogPostConfig,
14
+ HomeSectionsCopy,
15
+ TrustFeaturesConfig,
16
+ HomepageBanner,
17
+ } from "./dynamic-config";
8
18
  import { fetchRatings } from "medusa-reviews-logic/server";
19
+ import {
20
+ getBaptismPicksLooks,
21
+ type ShoppableLook,
22
+ } from "./shoppable-looks";
23
+ import { resolveHomeSegmentConfigData } from "./resolve-home-segment-data";
24
+
25
+ async function productsForCategoryIds(
26
+ countryCode: string,
27
+ categoryIds: string[],
28
+ limit: number
29
+ ): Promise<HttpTypes.StoreProduct[]> {
30
+ if (!categoryIds.length) return [];
31
+ try {
32
+ const { response } = await listProducts({
33
+ countryCode,
34
+ queryParams: {
35
+ category_id: categoryIds,
36
+ limit,
37
+ } as HttpTypes.FindParams & HttpTypes.StoreProductListParams,
38
+ });
39
+ return response.products ?? [];
40
+ } catch {
41
+ return [];
42
+ }
43
+ }
44
+
45
+ function categoryIdsMatching(
46
+ categories: HttpTypes.StoreProductCategory[],
47
+ pattern: RegExp
48
+ ): string[] {
49
+ return categories
50
+ .filter((c) => pattern.test((c.name || c.handle || "").toLowerCase()))
51
+ .map((c) => c.id)
52
+ .filter(Boolean) as string[];
53
+ }
9
54
 
10
55
  export type HomePageData = {
11
56
  region: Awaited<ReturnType<typeof getRegion>>;
12
57
  collections: Array<Record<string, unknown>>;
13
- categories: unknown[];
14
- newArrivals: unknown[];
15
- bestsellers: unknown[];
58
+ categories: HttpTypes.StoreProductCategory[];
59
+ newArrivals: HttpTypes.StoreProduct[];
60
+ bestsellers: HttpTypes.StoreProduct[];
16
61
  testimonials: unknown | null;
17
62
  ratings: unknown[];
63
+ videoStories: { title: string; videoUrls: string[] } | null;
64
+ announcementMessages: string[];
65
+ promoCountdown: {
66
+ code: string | null;
67
+ message: string | null;
68
+ endAt: string | null;
69
+ active: boolean;
70
+ ctaLabel: string | null;
71
+ ctaHref: string | null;
72
+ };
73
+ luxeFavourites: HttpTypes.StoreProduct[];
74
+ celebrityProduct: HttpTypes.StoreProduct | null;
75
+ baptismGirl: HttpTypes.StoreProduct[];
76
+ baptismBoy: HttpTypes.StoreProduct[];
77
+ baptismPicks: ShoppableLook[];
78
+ themeProducts: HttpTypes.StoreProduct[];
79
+ aboutBrand: AboutBrandConfig;
80
+ brandPillars: { sectionTitle: string; pillars: BrandPillarConfig[] };
81
+ blogPosts: BlogPostConfig[];
82
+ instagramPosts: InstagramPostsData;
83
+ sectionsCopy: HomeSectionsCopy;
84
+ trustFeatures: TrustFeaturesConfig;
85
+ homeBanners: HomepageBanner[] | null;
86
+ appBanners: HomepageBanner[] | null;
87
+ whyChooseUsFeatures: {
88
+ title: string;
89
+ features: Array<{ name: string; icon: string }>;
90
+ };
91
+ };
92
+
93
+ export type LoadHomePageDataOptions = {
94
+ /** Merged `homepage-config` from main project (fetched via dynamic-config API there). */
95
+ pageInput?: HomepageConfig | null;
18
96
  };
19
97
 
20
98
  /**
21
- * Parallel home page data loader (replaces per-page Promise.all boilerplate).
99
+ * Parallel home page data loader. Catalog APIs run here; segment copy comes from
100
+ * `pageInput` (or package JSON defaults) — no dynamic-config fetch in this package.
22
101
  */
23
- export async function loadHomePageData(countryCode: string): Promise<HomePageData | null> {
102
+ export async function loadHomePageData(
103
+ countryCode: string,
104
+ options?: LoadHomePageDataOptions
105
+ ): Promise<HomePageData | null> {
106
+ const pageInput = options?.pageInput ?? null;
107
+ const segmentConfig = resolveHomeSegmentConfigData(pageInput);
108
+
24
109
  const region = await getRegion(countryCode);
25
110
  if (!region) return null;
26
111
 
@@ -29,16 +114,21 @@ export async function loadHomePageData(countryCode: string): Promise<HomePageDat
29
114
  categoriesResult,
30
115
  newArrivalsResult,
31
116
  bestsellersResult,
32
- testimonialsData,
33
117
  allRatings,
118
+ themeByTag,
119
+ baptismByTag,
34
120
  ] = await Promise.all([
35
121
  listCollections({ fields: "id, handle, title, metadata" }).catch(() => ({
36
122
  collections: [],
37
123
  })),
38
- listCategories({ limit: 50 }).catch(() => []),
124
+ listCategories({
125
+ limit: 100,
126
+ fields:
127
+ "id,handle,name,metadata,parent_category_id,*parent_category,*parent_category.parent_category",
128
+ }).catch(() => []),
39
129
  listProductsWithSort({
40
130
  page: 1,
41
- queryParams: { limit: 8 },
131
+ queryParams: { limit: 12 },
42
132
  sortBy: "created_at",
43
133
  countryCode,
44
134
  }).catch(() => ({ response: { products: [] } })),
@@ -47,22 +137,89 @@ export async function loadHomePageData(countryCode: string): Promise<HomePageDat
47
137
  limit: 10,
48
138
  countryCode,
49
139
  }).catch(() => []),
50
- getTestimonialsFromConfig().catch(() => null),
51
140
  fetchRatings(),
141
+ getProductsByTag({ tagValue: "theme", limit: 8, countryCode }).catch(() => []),
142
+ getProductsByTag({ tagValue: "baptism", limit: 12, countryCode }).catch(() => []),
52
143
  ]);
53
144
 
145
+ const categories = (categoriesResult ?? []) as HttpTypes.StoreProductCategory[];
54
146
  const collections =
55
147
  (collectionsResult as { collections?: unknown[] }).collections ?? [];
56
148
  const newArrivals =
57
- (newArrivalsResult as { response?: { products?: unknown[] } }).response?.products ?? [];
149
+ (newArrivalsResult as { response?: { products?: HttpTypes.StoreProduct[] } })
150
+ .response?.products ?? [];
151
+ const bestsellers = (bestsellersResult ?? []) as HttpTypes.StoreProduct[];
152
+
153
+ const girlIds = categoryIdsMatching(categories, /baptism.*girl|girl.*baptism|^girl$/);
154
+ const boyIds = categoryIdsMatching(categories, /baptism.*boy|boy.*baptism|^boy$/);
155
+ const baptismIds = categoryIdsMatching(categories, /baptism/);
156
+
157
+ const [baptismGirl, baptismBoy] = await Promise.all([
158
+ girlIds.length
159
+ ? productsForCategoryIds(countryCode, girlIds, 8)
160
+ : baptismByTag.filter((p) => /girl/i.test(p.title || "")).slice(0, 8),
161
+ boyIds.length
162
+ ? productsForCategoryIds(countryCode, boyIds, 8)
163
+ : baptismByTag.filter((p) => /boy/i.test(p.title || "")).slice(0, 8),
164
+ ]);
165
+
166
+ let baptismFallback: HttpTypes.StoreProduct[] = [];
167
+ if (baptismGirl.length === 0 && baptismBoy.length === 0) {
168
+ baptismFallback =
169
+ baptismIds.length > 0
170
+ ? await productsForCategoryIds(countryCode, baptismIds, 12)
171
+ : baptismByTag;
172
+ }
173
+
174
+ const resolvedBaptismGirl =
175
+ baptismGirl.length > 0 ? baptismGirl : baptismFallback.slice(0, 8);
176
+ const resolvedBaptismBoy =
177
+ baptismBoy.length > 0 ? baptismBoy : baptismFallback.slice(0, 8);
178
+
179
+ const themeProducts =
180
+ themeByTag.length > 0
181
+ ? themeByTag
182
+ : await productsForCategoryIds(
183
+ countryCode,
184
+ categoryIdsMatching(categories, /theme/),
185
+ 8
186
+ );
187
+
188
+ const baptismPicksFallback = (baptismByTag.length ? baptismByTag : baptismFallback).slice(
189
+ 0,
190
+ 8
191
+ );
192
+ const baptismPicks = await getBaptismPicksLooks(
193
+ countryCode,
194
+ baptismPicksFallback,
195
+ pageInput
196
+ );
58
197
 
59
198
  return {
60
199
  region,
61
200
  collections: collections as Array<Record<string, unknown>>,
62
- categories: categoriesResult ?? [],
201
+ categories,
63
202
  newArrivals,
64
- bestsellers: bestsellersResult ?? [],
65
- testimonials: testimonialsData,
203
+ bestsellers,
204
+ testimonials: segmentConfig.testimonials,
66
205
  ratings: allRatings ?? [],
206
+ videoStories: segmentConfig.videoStories,
207
+ announcementMessages: segmentConfig.announcementMessages,
208
+ promoCountdown: segmentConfig.promoCountdown,
209
+ luxeFavourites: bestsellers.slice(0, 4),
210
+ celebrityProduct: bestsellers[0] ?? newArrivals[0] ?? null,
211
+ baptismGirl: resolvedBaptismGirl,
212
+ baptismBoy: resolvedBaptismBoy,
213
+ baptismPicks,
214
+ themeProducts,
215
+ aboutBrand: segmentConfig.aboutBrand,
216
+ brandPillars: segmentConfig.brandPillars,
217
+ blogPosts: segmentConfig.blogPosts,
218
+ instagramPosts: segmentConfig.instagramPosts,
219
+ sectionsCopy: segmentConfig.sectionsCopy,
220
+ trustFeatures: segmentConfig.trustFeatures,
221
+ homeBanners: segmentConfig.homeBanners,
222
+ appBanners: segmentConfig.appBanners,
223
+ whyChooseUsFeatures: segmentConfig.whyChooseUsFeatures,
67
224
  };
68
225
  }
@@ -0,0 +1,335 @@
1
+ /**
2
+ * Medusa `GET /store/dynamic-config` → `homepage-config` shape.
3
+ * Used by the storefront and as the reference for backend admin JSON.
4
+ *
5
+ * Section keys: camelCase (preferred) or kebab-case (`new-arrivals` → `newArrivals`).
6
+ * Only fields you set override package defaults in `homepage-section-defaults.ts`.
7
+ */
8
+
9
+ /** Shared copy fields merged into every section block. */
10
+ export type HomepageSectionCopyFields = {
11
+ title?: string
12
+ name?: string
13
+ subtitle?: string
14
+ text?: string
15
+ description?: string
16
+ eyebrow?: string
17
+ /** Layout variant, e.g. new arrivals: `grid` | `classic` */
18
+ variant?: string
19
+ layout?: string
20
+ }
21
+
22
+ export type HomepagePromoAnnouncementsSection = HomepageSectionCopyFields & {
23
+ messages?: string[]
24
+ items?: Array<string | { message?: string; text?: string }>
25
+ "announcement-messages"?: string[]
26
+ }
27
+
28
+ export type HomepagePromoCountdownSection = HomepageSectionCopyFields & {
29
+ code?: string
30
+ "coupon-code"?: string
31
+ text?: string
32
+ message?: string
33
+ "promo-text"?: string
34
+ "end-at"?: string
35
+ endAt?: string
36
+ active?: boolean
37
+ "promo-active"?: boolean
38
+ "cta-label"?: string
39
+ "cta-href"?: string
40
+ }
41
+
42
+ export type HomepageNewArrivalsSection = HomepageSectionCopyFields & {
43
+ /** `grid` (ProductCardView) or `classic` (ProductPreview + pink background) */
44
+ variant?: "grid" | "classic" | "legacy" | "preview"
45
+ }
46
+
47
+ export type HomepageTestimonialItem = {
48
+ text?: string
49
+ name?: string
50
+ rating?: number
51
+ avatar?: string
52
+ id?: string
53
+ testimonial?: HomepageTestimonialItem
54
+ review?: HomepageTestimonialItem
55
+ }
56
+
57
+ export type HomepageTestimonialsSection = HomepageSectionCopyFields & {
58
+ items?: HomepageTestimonialItem[]
59
+ testimonials?: HomepageTestimonialItem[]
60
+ reviews?: HomepageTestimonialItem[]
61
+ }
62
+
63
+ export type HomepageRatingItem = HomepageTestimonialItem
64
+
65
+ export type HomepageShoppableLook = {
66
+ image?: string
67
+ "look-image"?: string
68
+ "product-ids"?: string[]
69
+ productIds?: string[]
70
+ "product-handles"?: string[]
71
+ productHandles?: string[]
72
+ look?: HomepageShoppableLook
73
+ }
74
+
75
+ export type HomepageBaptismPicksSection = HomepageSectionCopyFields & {
76
+ looks?: HomepageShoppableLook[]
77
+ "shop-looks"?: HomepageShoppableLook[]
78
+ }
79
+
80
+ export type HomepageAboutBrandStat = {
81
+ label?: string
82
+ name?: string
83
+ value?: string
84
+ }
85
+
86
+ export type HomepageAboutBrandSection = HomepageSectionCopyFields & {
87
+ readMoreHref?: string
88
+ "read-more-link"?: string
89
+ stats?: HomepageAboutBrandStat[]
90
+ "about-stats"?: HomepageAboutBrandStat[]
91
+ }
92
+
93
+ export type HomepageBrandPillar = {
94
+ title?: string
95
+ name?: string
96
+ description?: string
97
+ text?: string
98
+ image?: string
99
+ icon?: string
100
+ pillar?: HomepageBrandPillar
101
+ }
102
+
103
+ export type HomepageBrandPillarsSection = HomepageSectionCopyFields & {
104
+ pillars?: HomepageBrandPillar[]
105
+ "brand-pillars"?: HomepageBrandPillar[]
106
+ }
107
+
108
+ export type HomepageFeatureItem = {
109
+ name?: string
110
+ icon?: string
111
+ "feature-name"?: string
112
+ "feature-icon"?: string
113
+ feature?: HomepageFeatureItem
114
+ }
115
+
116
+ export type HomepageFeaturesSection = HomepageSectionCopyFields & {
117
+ features?: HomepageFeatureItem[]
118
+ items?: HomepageFeatureItem[]
119
+ }
120
+
121
+ export type HomepageBlogPost = {
122
+ title?: string
123
+ href?: string
124
+ link?: string
125
+ url?: string
126
+ excerpt?: string
127
+ description?: string
128
+ category?: string
129
+ tag?: string
130
+ date?: string
131
+ image?: string
132
+ thumbnail?: string
133
+ blog?: HomepageBlogPost
134
+ }
135
+
136
+ export type HomepageBlogPostsSection = HomepageSectionCopyFields & {
137
+ posts?: HomepageBlogPost[]
138
+ "blog-posts"?: HomepageBlogPost[]
139
+ }
140
+
141
+ export type HomepageInstagramPost = {
142
+ id?: string
143
+ image?: string
144
+ "post-image"?: string
145
+ url?: string
146
+ link?: string
147
+ post?: HomepageInstagramPost
148
+ instagram?: HomepageInstagramPost
149
+ }
150
+
151
+ export type HomepageInstagramPostsSection = HomepageSectionCopyFields & {
152
+ handle?: string
153
+ username?: string
154
+ "instagram-handle"?: string
155
+ "profile-url"?: string
156
+ profileUrl?: string
157
+ "profile-image"?: string
158
+ profileImage?: string
159
+ posts?: HomepageInstagramPost[]
160
+ items?: HomepageInstagramPost[]
161
+ }
162
+
163
+ export type HomepageVideoStoryEntry =
164
+ | string
165
+ | {
166
+ "video-url"?: string
167
+ video_url?: string
168
+ url?: string
169
+ src?: string
170
+ video?: string
171
+ "video-story"?: Record<string, unknown>
172
+ }
173
+
174
+ export type HomepageVideoStoriesSection = HomepageSectionCopyFields & {
175
+ "video-urls"?: HomepageVideoStoryEntry[]
176
+ videoUrls?: HomepageVideoStoryEntry[]
177
+ videos?: HomepageVideoStoryEntry[]
178
+ "video-stories"?: HomepageVideoStoryEntry[]
179
+ }
180
+
181
+ /** Per-section blocks under `homepage-config.sections`. */
182
+ export type HomepageSectionsConfig = {
183
+ promoAnnouncements?: HomepagePromoAnnouncementsSection
184
+ hero?: HomepageSectionCopyFields
185
+ categoryPills?: HomepageSectionCopyFields
186
+ newArrivals?: HomepageNewArrivalsSection
187
+ newArrivalsClassic?: HomepageNewArrivalsSection
188
+ promoCountdown?: HomepagePromoCountdownSection
189
+ celebrityTrust?: HomepageSectionCopyFields
190
+ luxeFavourites?: HomepageSectionCopyFields
191
+ shopByCategory?: HomepageSectionCopyFields
192
+ baptism?: HomepageSectionCopyFields
193
+ baptismPicks?: HomepageBaptismPicksSection
194
+ themeDresses?: HomepageSectionCopyFields
195
+ brandMarquee?: HomepageSectionCopyFields
196
+ aboutBrand?: HomepageAboutBrandSection
197
+ testimonials?: HomepageTestimonialsSection
198
+ brandPillars?: HomepageBrandPillarsSection
199
+ instagramPosts?: HomepageInstagramPostsSection
200
+ features?: HomepageFeaturesSection
201
+ shopByAge?: HomepageSectionCopyFields
202
+ whyChooseUs?: HomepageFeaturesSection
203
+ lovedByMoms?: HomepageSectionCopyFields
204
+ videoStories?: HomepageVideoStoriesSection
205
+ blogPosts?: HomepageBlogPostsSection
206
+ /** Allow kebab-case keys from legacy CMS */
207
+ [sectionKey: string]: HomepageSectionCopyFields | undefined
208
+ }
209
+
210
+ export type HomepageBanner = {
211
+ "homepage-banner-image"?: string
212
+ "homepage-banner-title"?: string
213
+ "homepage-banner-subtitle"?: string
214
+ "homepage-banner-description"?: string
215
+ "homepage-banner-button-name"?: string
216
+ "homepage-banner-button-link"?: string
217
+ }
218
+
219
+ export type HomepageBannerEntry = {
220
+ "homepage-banner"?: HomepageBanner
221
+ }
222
+
223
+ export type HomepageAppBanner = {
224
+ "app-banner-image"?: string
225
+ "app-banner-title"?: string
226
+ "app-banner-subtitle"?: string
227
+ "app-banner-description"?: string
228
+ "app-banner-button-name"?: string
229
+ "app-banner-button-link"?: string
230
+ "app-banner-link"?: string
231
+ }
232
+
233
+ export type HomepageAppBannerEntry = {
234
+ "app-banner"?: HomepageAppBanner
235
+ }
236
+
237
+ export type HomepageContactConfig = {
238
+ "contact-phone"?: string
239
+ "contact-email"?: string
240
+ "contact-address"?: string
241
+ }
242
+
243
+ export type HomepageSocialLink = {
244
+ "social-platform-name"?: string
245
+ "social-platform-icon"?: string
246
+ "social-platform-url"?: string
247
+ "social-link"?: HomepageSocialLink
248
+ }
249
+
250
+ export type HomepagePromoBarConfig = {
251
+ "promo-text"?: string
252
+ "promo-code"?: string
253
+ "promo-value"?: string
254
+ "promo-active"?: boolean
255
+ }
256
+
257
+ export type HomepageFaqEntry = {
258
+ "faq-category"?: string
259
+ "faq-question"?: string
260
+ "faq-answer"?: string
261
+ faq?: HomepageFaqEntry
262
+ }
263
+
264
+ /**
265
+ * Root `homepage-config` object stored in Medusa dynamic config.
266
+ */
267
+ export type HomepageConfig = {
268
+ logo?: string
269
+ "website-description"?: string
270
+ website_description?: string
271
+
272
+ /** Hero carousel — used by `hero` section (not `sections.hero`). */
273
+ "homepage-banner-array"?: HomepageBannerEntry[]
274
+
275
+ "app-banner-array"?: HomepageAppBannerEntry[]
276
+
277
+ /** Option 1 — central section copy + structured data (preferred). */
278
+ sections?: HomepageSectionsConfig
279
+
280
+ /**
281
+ * Product reviews for testimonials carousel.
282
+ * Fallback when `sections.testimonials.items` is empty.
283
+ */
284
+ ratings?: HomepageRatingItem[]
285
+
286
+ /** Legacy top-level keys (still read; prefer `sections.*`). */
287
+ "promo-bar"?: HomepagePromoBarConfig
288
+ "contact-us"?: HomepageContactConfig
289
+ "social-links"?: HomepageSocialLink[]
290
+ "faq-array"?: HomepageFaqEntry[]
291
+ "why-choose-us-title"?: string
292
+ "why-choose-us-features"?: HomepageFeatureItem[]
293
+ "about-brand"?: HomepageAboutBrandSection
294
+ "who-we-are"?: HomepageAboutBrandSection
295
+ "video-stories"?: HomepageVideoStoryEntry[]
296
+ "video-stories-array"?: HomepageVideoStoryEntry[]
297
+ video_stories?: HomepageVideoStoryEntry[]
298
+ "announcement-messages"?: string[]
299
+ "announcement-bar"?: string[]
300
+ }
301
+
302
+ /** Full dynamic config API response. */
303
+ export type DynamicConfigResponse = {
304
+ "homepage-config"?: HomepageConfig
305
+ [key: string]: unknown
306
+ }
307
+
308
+ /** All section ids with package defaults. */
309
+ export const HOMEPAGE_SECTION_IDS = [
310
+ "promoAnnouncements",
311
+ "hero",
312
+ "categoryPills",
313
+ "newArrivals",
314
+ "newArrivalsClassic",
315
+ "promoCountdown",
316
+ "celebrityTrust",
317
+ "luxeFavourites",
318
+ "shopByCategory",
319
+ "baptism",
320
+ "baptismPicks",
321
+ "themeDresses",
322
+ "brandMarquee",
323
+ "aboutBrand",
324
+ "testimonials",
325
+ "brandPillars",
326
+ "instagramPosts",
327
+ "features",
328
+ "shopByAge",
329
+ "whyChooseUs",
330
+ "lovedByMoms",
331
+ "videoStories",
332
+ "blogPosts",
333
+ ] as const
334
+
335
+ export type HomepageSectionId = (typeof HOMEPAGE_SECTION_IDS)[number]