@salifbiaye/create-boutique 0.1.1

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 (316) hide show
  1. package/README.md +1 -0
  2. package/cli/dist/commands.js +382 -0
  3. package/cli/dist/create.js +194 -0
  4. package/cli/dist/docker.js +19 -0
  5. package/cli/dist/generate.js +322 -0
  6. package/cli/dist/guide.js +72 -0
  7. package/cli/dist/index.js +156 -0
  8. package/cli/dist/paths.js +29 -0
  9. package/cli/dist/ports.js +54 -0
  10. package/cli/dist/project.js +119 -0
  11. package/cli/dist/themes.js +24 -0
  12. package/docs/fiche-client.en.html +137 -0
  13. package/docs/fiche-client.html +137 -0
  14. package/docs/guide-complet.en.html +259 -0
  15. package/docs/guide-complet.fr.html +259 -0
  16. package/package.json +51 -0
  17. package/templates/backend/.dockerignore +9 -0
  18. package/templates/backend/Dockerfile +30 -0
  19. package/templates/backend/README.md +78 -0
  20. package/templates/backend/TEMPLATE_VERSION +1 -0
  21. package/templates/backend/docker/entrypoint.sh +28 -0
  22. package/templates/backend/instrumentation.ts +24 -0
  23. package/templates/backend/medusa-config.ts +114 -0
  24. package/templates/backend/package-lock.json +16222 -0
  25. package/templates/backend/package.json +39 -0
  26. package/templates/backend/src/admin/README.md +33 -0
  27. package/templates/backend/src/admin/components/image-field.tsx +61 -0
  28. package/templates/backend/src/admin/i18n/README.md +58 -0
  29. package/templates/backend/src/admin/i18n/index.ts +8 -0
  30. package/templates/backend/src/admin/i18n/json/en.json +66 -0
  31. package/templates/backend/src/admin/i18n/json/fr.json +66 -0
  32. package/templates/backend/src/admin/lib/client.ts +9 -0
  33. package/templates/backend/src/admin/routes/boutique-campaigns/campaign-drawer.tsx +286 -0
  34. package/templates/backend/src/admin/routes/boutique-campaigns/page.tsx +113 -0
  35. package/templates/backend/src/admin/tsconfig.json +24 -0
  36. package/templates/backend/src/admin/vite-env.d.ts +1 -0
  37. package/templates/backend/src/admin/widgets/category-image.tsx +56 -0
  38. package/templates/backend/src/api/README.md +135 -0
  39. package/templates/backend/src/api/admin/boutique-campaigns/[salesChannelId]/route.ts +30 -0
  40. package/templates/backend/src/api/admin/boutique-campaigns/route.ts +24 -0
  41. package/templates/backend/src/api/middlewares.ts +28 -0
  42. package/templates/backend/src/api/rate-limit.ts +30 -0
  43. package/templates/backend/src/api/route.ts +6 -0
  44. package/templates/backend/src/api/store/campaign/route.ts +25 -0
  45. package/templates/backend/src/api/store/order-lookup/route.ts +74 -0
  46. package/templates/backend/src/emails/en.json +64 -0
  47. package/templates/backend/src/emails/fr.json +64 -0
  48. package/templates/backend/src/jobs/README.md +38 -0
  49. package/templates/backend/src/lib/emails.ts +94 -0
  50. package/templates/backend/src/lib/notify.ts +148 -0
  51. package/templates/backend/src/lib/project.ts +146 -0
  52. package/templates/backend/src/links/README.md +26 -0
  53. package/templates/backend/src/links/campaign-sales-channel.ts +9 -0
  54. package/templates/backend/src/modules/README.md +117 -0
  55. package/templates/backend/src/modules/campaign/index.ts +9 -0
  56. package/templates/backend/src/modules/campaign/migrations/.snapshot-boutique-campaign.json +272 -0
  57. package/templates/backend/src/modules/campaign/migrations/Migration20260928141440.ts +16 -0
  58. package/templates/backend/src/modules/campaign/migrations/Migration20260928145243.ts +13 -0
  59. package/templates/backend/src/modules/campaign/models/campaign.ts +28 -0
  60. package/templates/backend/src/modules/campaign/service.ts +6 -0
  61. package/templates/backend/src/modules/campaign/validators.ts +94 -0
  62. package/templates/backend/src/modules/resend/index.ts +6 -0
  63. package/templates/backend/src/modules/resend/service.ts +63 -0
  64. package/templates/backend/src/scripts/README.md +63 -0
  65. package/templates/backend/src/scripts/campaign-export.ts +15 -0
  66. package/templates/backend/src/scripts/campaign-import.ts +38 -0
  67. package/templates/backend/src/scripts/price-report.ts +34 -0
  68. package/templates/backend/src/scripts/seed.ts +418 -0
  69. package/templates/backend/src/scripts/storage.ts +63 -0
  70. package/templates/backend/src/subscribers/README.md +61 -0
  71. package/templates/backend/src/subscribers/account-emails.ts +27 -0
  72. package/templates/backend/src/subscribers/fulfillment-emails.ts +42 -0
  73. package/templates/backend/src/subscribers/order-emails.ts +12 -0
  74. package/templates/backend/src/subscribers/return-refund-emails.ts +50 -0
  75. package/templates/backend/src/workflows/README.md +81 -0
  76. package/templates/backend/src/workflows/steps/upsert-campaign.ts +53 -0
  77. package/templates/backend/src/workflows/upsert-campaign.ts +30 -0
  78. package/templates/backend/tsconfig.json +36 -0
  79. package/templates/storefront/.dockerignore +7 -0
  80. package/templates/storefront/.github/ISSUE_TEMPLATE/bug-report.yml +71 -0
  81. package/templates/storefront/.prettierrc +8 -0
  82. package/templates/storefront/Dockerfile +37 -0
  83. package/templates/storefront/LICENSE +21 -0
  84. package/templates/storefront/README.md +127 -0
  85. package/templates/storefront/locales/en.json +269 -0
  86. package/templates/storefront/locales/fr.json +269 -0
  87. package/templates/storefront/next-env.d.ts +7 -0
  88. package/templates/storefront/next-sitemap.js +19 -0
  89. package/templates/storefront/next.config.js +29 -0
  90. package/templates/storefront/package-lock.json +9031 -0
  91. package/templates/storefront/package.json +49 -0
  92. package/templates/storefront/postcss.config.js +6 -0
  93. package/templates/storefront/public/favicon.ico +0 -0
  94. package/templates/storefront/scripts/sync-design.mjs +30 -0
  95. package/templates/storefront/src/app/(checkout)/checkout/page.tsx +31 -0
  96. package/templates/storefront/src/app/(checkout)/layout.tsx +29 -0
  97. package/templates/storefront/src/app/(checkout)/not-found.tsx +20 -0
  98. package/templates/storefront/src/app/(main)/account/@dashboard/addresses/page.tsx +34 -0
  99. package/templates/storefront/src/app/(main)/account/@dashboard/loading.tsx +9 -0
  100. package/templates/storefront/src/app/(main)/account/@dashboard/orders/details/[id]/page.tsx +33 -0
  101. package/templates/storefront/src/app/(main)/account/@dashboard/orders/page.tsx +37 -0
  102. package/templates/storefront/src/app/(main)/account/@dashboard/page.tsx +22 -0
  103. package/templates/storefront/src/app/(main)/account/@dashboard/profile/page.tsx +53 -0
  104. package/templates/storefront/src/app/(main)/account/@login/page.tsx +12 -0
  105. package/templates/storefront/src/app/(main)/account/layout.tsx +20 -0
  106. package/templates/storefront/src/app/(main)/account/loading.tsx +9 -0
  107. package/templates/storefront/src/app/(main)/cart/loading.tsx +5 -0
  108. package/templates/storefront/src/app/(main)/cart/not-found.tsx +21 -0
  109. package/templates/storefront/src/app/(main)/cart/page.tsx +21 -0
  110. package/templates/storefront/src/app/(main)/categories/[...category]/page.tsx +55 -0
  111. package/templates/storefront/src/app/(main)/collections/[handle]/page.tsx +55 -0
  112. package/templates/storefront/src/app/(main)/layout.tsx +43 -0
  113. package/templates/storefront/src/app/(main)/legal/[page]/page.tsx +42 -0
  114. package/templates/storefront/src/app/(main)/not-found.tsx +21 -0
  115. package/templates/storefront/src/app/(main)/order/[id]/confirmed/loading.tsx +5 -0
  116. package/templates/storefront/src/app/(main)/order/[id]/confirmed/page.tsx +23 -0
  117. package/templates/storefront/src/app/(main)/order/[id]/transfer/[token]/accept/page.tsx +42 -0
  118. package/templates/storefront/src/app/(main)/order/[id]/transfer/[token]/decline/page.tsx +42 -0
  119. package/templates/storefront/src/app/(main)/order/[id]/transfer/[token]/page.tsx +35 -0
  120. package/templates/storefront/src/app/(main)/order/track/page.tsx +32 -0
  121. package/templates/storefront/src/app/(main)/page.tsx +68 -0
  122. package/templates/storefront/src/app/(main)/products/[handle]/page.tsx +86 -0
  123. package/templates/storefront/src/app/(main)/store/page.tsx +28 -0
  124. package/templates/storefront/src/app/api/health/route.ts +13 -0
  125. package/templates/storefront/src/app/layout.tsx +42 -0
  126. package/templates/storefront/src/app/not-found.tsx +21 -0
  127. package/templates/storefront/src/app/opengraph-image.jpg +0 -0
  128. package/templates/storefront/src/app/twitter-image.jpg +0 -0
  129. package/templates/storefront/src/instrumentation-node.ts +18 -0
  130. package/templates/storefront/src/instrumentation.ts +9 -0
  131. package/templates/storefront/src/lib/boutique/campaign.ts +116 -0
  132. package/templates/storefront/src/lib/boutique/config.ts +152 -0
  133. package/templates/storefront/src/lib/boutique/fonts.ts +29 -0
  134. package/templates/storefront/src/lib/boutique/i18n-client.tsx +34 -0
  135. package/templates/storefront/src/lib/boutique/i18n.ts +43 -0
  136. package/templates/storefront/src/lib/boutique/schema.ts +131 -0
  137. package/templates/storefront/src/lib/config.ts +39 -0
  138. package/templates/storefront/src/lib/constants.tsx +75 -0
  139. package/templates/storefront/src/lib/context/modal-context.tsx +34 -0
  140. package/templates/storefront/src/lib/data/cart.ts +434 -0
  141. package/templates/storefront/src/lib/data/categories.ts +49 -0
  142. package/templates/storefront/src/lib/data/collections.ts +59 -0
  143. package/templates/storefront/src/lib/data/cookies.ts +103 -0
  144. package/templates/storefront/src/lib/data/customer.ts +261 -0
  145. package/templates/storefront/src/lib/data/fulfillment.ts +68 -0
  146. package/templates/storefront/src/lib/data/onboarding.ts +9 -0
  147. package/templates/storefront/src/lib/data/order-lookup.ts +34 -0
  148. package/templates/storefront/src/lib/data/orders.ts +112 -0
  149. package/templates/storefront/src/lib/data/payment.ts +35 -0
  150. package/templates/storefront/src/lib/data/products.ts +127 -0
  151. package/templates/storefront/src/lib/data/regions.ts +70 -0
  152. package/templates/storefront/src/lib/data/variants.ts +38 -0
  153. package/templates/storefront/src/lib/hooks/use-in-view.tsx +29 -0
  154. package/templates/storefront/src/lib/hooks/use-toggle-state.tsx +46 -0
  155. package/templates/storefront/src/lib/util/compare-addresses.ts +28 -0
  156. package/templates/storefront/src/lib/util/get-percentage-diff.ts +6 -0
  157. package/templates/storefront/src/lib/util/get-product-price.ts +79 -0
  158. package/templates/storefront/src/lib/util/isEmpty.ts +11 -0
  159. package/templates/storefront/src/lib/util/medusa-error.ts +22 -0
  160. package/templates/storefront/src/lib/util/money.ts +35 -0
  161. package/templates/storefront/src/lib/util/product.ts +5 -0
  162. package/templates/storefront/src/lib/util/repeat.ts +5 -0
  163. package/templates/storefront/src/lib/util/sort-products.ts +50 -0
  164. package/templates/storefront/src/modules/account/components/account-info/index.tsx +140 -0
  165. package/templates/storefront/src/modules/account/components/account-nav/index.tsx +198 -0
  166. package/templates/storefront/src/modules/account/components/address-book/index.tsx +28 -0
  167. package/templates/storefront/src/modules/account/components/address-card/add-address.tsx +169 -0
  168. package/templates/storefront/src/modules/account/components/address-card/edit-address-modal.tsx +241 -0
  169. package/templates/storefront/src/modules/account/components/login/index.tsx +65 -0
  170. package/templates/storefront/src/modules/account/components/order-card/index.tsx +88 -0
  171. package/templates/storefront/src/modules/account/components/order-overview/index.tsx +47 -0
  172. package/templates/storefront/src/modules/account/components/overview/index.tsx +169 -0
  173. package/templates/storefront/src/modules/account/components/profile-billing-address/index.tsx +193 -0
  174. package/templates/storefront/src/modules/account/components/profile-email/index.tsx +75 -0
  175. package/templates/storefront/src/modules/account/components/profile-name/index.tsx +79 -0
  176. package/templates/storefront/src/modules/account/components/profile-password/index.tsx +72 -0
  177. package/templates/storefront/src/modules/account/components/profile-phone/index.tsx +74 -0
  178. package/templates/storefront/src/modules/account/components/register/index.tsx +107 -0
  179. package/templates/storefront/src/modules/account/components/transfer-request-form/index.tsx +83 -0
  180. package/templates/storefront/src/modules/account/templates/account-layout.tsx +43 -0
  181. package/templates/storefront/src/modules/account/templates/login-template.tsx +27 -0
  182. package/templates/storefront/src/modules/cart/components/cart-item-select/index.tsx +75 -0
  183. package/templates/storefront/src/modules/cart/components/empty-cart-message/index.tsx +25 -0
  184. package/templates/storefront/src/modules/cart/components/item/index.tsx +144 -0
  185. package/templates/storefront/src/modules/cart/components/sign-in-prompt/index.tsx +27 -0
  186. package/templates/storefront/src/modules/cart/templates/index.tsx +51 -0
  187. package/templates/storefront/src/modules/cart/templates/items.tsx +58 -0
  188. package/templates/storefront/src/modules/cart/templates/preview.tsx +51 -0
  189. package/templates/storefront/src/modules/cart/templates/summary.tsx +50 -0
  190. package/templates/storefront/src/modules/categories/templates/index.tsx +94 -0
  191. package/templates/storefront/src/modules/checkout/components/address-select/index.tsx +117 -0
  192. package/templates/storefront/src/modules/checkout/components/addresses/index.tsx +186 -0
  193. package/templates/storefront/src/modules/checkout/components/billing_address/index.tsx +114 -0
  194. package/templates/storefront/src/modules/checkout/components/country-select/index.tsx +50 -0
  195. package/templates/storefront/src/modules/checkout/components/discount-code/index.tsx +181 -0
  196. package/templates/storefront/src/modules/checkout/components/error-message/index.tsx +13 -0
  197. package/templates/storefront/src/modules/checkout/components/payment/index.tsx +261 -0
  198. package/templates/storefront/src/modules/checkout/components/payment-button/index.tsx +195 -0
  199. package/templates/storefront/src/modules/checkout/components/payment-container/index.tsx +130 -0
  200. package/templates/storefront/src/modules/checkout/components/payment-test/index.tsx +12 -0
  201. package/templates/storefront/src/modules/checkout/components/payment-wrapper/index.tsx +42 -0
  202. package/templates/storefront/src/modules/checkout/components/payment-wrapper/stripe-wrapper.tsx +54 -0
  203. package/templates/storefront/src/modules/checkout/components/review/index.tsx +54 -0
  204. package/templates/storefront/src/modules/checkout/components/shipping/index.tsx +417 -0
  205. package/templates/storefront/src/modules/checkout/components/shipping-address/index.tsx +219 -0
  206. package/templates/storefront/src/modules/checkout/components/submit-button/index.tsx +32 -0
  207. package/templates/storefront/src/modules/checkout/templates/checkout-form/index.tsx +38 -0
  208. package/templates/storefront/src/modules/checkout/templates/checkout-summary/index.tsx +31 -0
  209. package/templates/storefront/src/modules/collections/templates/index.tsx +44 -0
  210. package/templates/storefront/src/modules/common/components/cart-totals/index.tsx +84 -0
  211. package/templates/storefront/src/modules/common/components/checkbox/index.tsx +44 -0
  212. package/templates/storefront/src/modules/common/components/delete-button/index.tsx +42 -0
  213. package/templates/storefront/src/modules/common/components/divider/index.tsx +9 -0
  214. package/templates/storefront/src/modules/common/components/filter-radio-group/index.tsx +61 -0
  215. package/templates/storefront/src/modules/common/components/input/index.tsx +79 -0
  216. package/templates/storefront/src/modules/common/components/interactive-link/index.tsx +33 -0
  217. package/templates/storefront/src/modules/common/components/line-item-options/index.tsx +26 -0
  218. package/templates/storefront/src/modules/common/components/line-item-price/index.tsx +63 -0
  219. package/templates/storefront/src/modules/common/components/line-item-unit-price/index.tsx +62 -0
  220. package/templates/storefront/src/modules/common/components/localized-client-link/index.tsx +29 -0
  221. package/templates/storefront/src/modules/common/components/modal/index.tsx +118 -0
  222. package/templates/storefront/src/modules/common/components/native-select/index.tsx +76 -0
  223. package/templates/storefront/src/modules/common/components/radio/index.tsx +27 -0
  224. package/templates/storefront/src/modules/common/icons/back.tsx +37 -0
  225. package/templates/storefront/src/modules/common/icons/bancontact.tsx +26 -0
  226. package/templates/storefront/src/modules/common/icons/chevron-down.tsx +30 -0
  227. package/templates/storefront/src/modules/common/icons/eye-off.tsx +37 -0
  228. package/templates/storefront/src/modules/common/icons/eye.tsx +37 -0
  229. package/templates/storefront/src/modules/common/icons/fast-delivery.tsx +65 -0
  230. package/templates/storefront/src/modules/common/icons/ideal.tsx +26 -0
  231. package/templates/storefront/src/modules/common/icons/map-pin.tsx +37 -0
  232. package/templates/storefront/src/modules/common/icons/package.tsx +44 -0
  233. package/templates/storefront/src/modules/common/icons/paypal.tsx +30 -0
  234. package/templates/storefront/src/modules/common/icons/placeholder-image.tsx +44 -0
  235. package/templates/storefront/src/modules/common/icons/refresh.tsx +51 -0
  236. package/templates/storefront/src/modules/common/icons/spinner.tsx +37 -0
  237. package/templates/storefront/src/modules/common/icons/trash.tsx +51 -0
  238. package/templates/storefront/src/modules/common/icons/user.tsx +37 -0
  239. package/templates/storefront/src/modules/common/icons/x.tsx +37 -0
  240. package/templates/storefront/src/modules/layout/components/cart-mismatch-banner/index.tsx +59 -0
  241. package/templates/storefront/src/modules/order/components/help/index.tsx +26 -0
  242. package/templates/storefront/src/modules/order/components/item/index.tsx +57 -0
  243. package/templates/storefront/src/modules/order/components/items/index.tsx +44 -0
  244. package/templates/storefront/src/modules/order/components/onboarding-cta/index.tsx +28 -0
  245. package/templates/storefront/src/modules/order/components/order-details/index.tsx +64 -0
  246. package/templates/storefront/src/modules/order/components/order-summary/index.tsx +61 -0
  247. package/templates/storefront/src/modules/order/components/payment-details/index.tsx +64 -0
  248. package/templates/storefront/src/modules/order/components/shipping-details/index.tsx +74 -0
  249. package/templates/storefront/src/modules/order/components/transfer-actions/index.tsx +83 -0
  250. package/templates/storefront/src/modules/order/components/transfer-image/index.tsx +275 -0
  251. package/templates/storefront/src/modules/order/templates/order-completed-template.tsx +53 -0
  252. package/templates/storefront/src/modules/order/templates/order-details-template.tsx +46 -0
  253. package/templates/storefront/src/modules/products/components/thumbnail/index.tsx +70 -0
  254. package/templates/storefront/src/modules/products/templates/index.tsx +118 -0
  255. package/templates/storefront/src/modules/shipping/components/free-shipping-price-nudge/index.tsx +285 -0
  256. package/templates/storefront/src/modules/skeletons/components/skeleton-button/index.tsx +5 -0
  257. package/templates/storefront/src/modules/skeletons/components/skeleton-card-details/index.tsx +10 -0
  258. package/templates/storefront/src/modules/skeletons/components/skeleton-cart-item/index.tsx +35 -0
  259. package/templates/storefront/src/modules/skeletons/components/skeleton-cart-totals/index.tsx +30 -0
  260. package/templates/storefront/src/modules/skeletons/components/skeleton-code-form/index.tsx +13 -0
  261. package/templates/storefront/src/modules/skeletons/components/skeleton-line-item/index.tsx +35 -0
  262. package/templates/storefront/src/modules/skeletons/components/skeleton-order-confirmed-header/index.tsx +14 -0
  263. package/templates/storefront/src/modules/skeletons/components/skeleton-order-information/index.tsx +36 -0
  264. package/templates/storefront/src/modules/skeletons/components/skeleton-order-items/index.tsx +43 -0
  265. package/templates/storefront/src/modules/skeletons/components/skeleton-order-summary/index.tsx +15 -0
  266. package/templates/storefront/src/modules/skeletons/components/skeleton-product-preview/index.tsx +15 -0
  267. package/templates/storefront/src/modules/skeletons/templates/skeleton-cart-page/index.tsx +65 -0
  268. package/templates/storefront/src/modules/skeletons/templates/skeleton-order-confirmed/index.tsx +21 -0
  269. package/templates/storefront/src/modules/skeletons/templates/skeleton-product-grid/index.tsx +23 -0
  270. package/templates/storefront/src/modules/store/components/pagination/index.tsx +114 -0
  271. package/templates/storefront/src/modules/store/components/refinement-list/index.tsx +41 -0
  272. package/templates/storefront/src/modules/store/components/refinement-list/sort-products/index.tsx +48 -0
  273. package/templates/storefront/src/modules/store/templates/index.tsx +42 -0
  274. package/templates/storefront/src/modules/store/templates/paginated-products.tsx +80 -0
  275. package/templates/storefront/src/modules/ub/button.tsx +41 -0
  276. package/templates/storefront/src/modules/ub/campaign-blocks.tsx +197 -0
  277. package/templates/storefront/src/modules/ub/campaign-ended.tsx +28 -0
  278. package/templates/storefront/src/modules/ub/category-tiles.tsx +41 -0
  279. package/templates/storefront/src/modules/ub/countdown.tsx +46 -0
  280. package/templates/storefront/src/modules/ub/footer.tsx +56 -0
  281. package/templates/storefront/src/modules/ub/icons.tsx +42 -0
  282. package/templates/storefront/src/modules/ub/nav.tsx +90 -0
  283. package/templates/storefront/src/modules/ub/pdp/buy-box.tsx +140 -0
  284. package/templates/storefront/src/modules/ub/pdp/gallery.tsx +40 -0
  285. package/templates/storefront/src/modules/ub/product-card.tsx +66 -0
  286. package/templates/storefront/src/modules/ub/top-bar.tsx +20 -0
  287. package/templates/storefront/src/modules/ub/track-form.tsx +92 -0
  288. package/templates/storefront/src/proxy.ts +89 -0
  289. package/templates/storefront/src/styles/globals.css +98 -0
  290. package/templates/storefront/src/styles/ub/bundle.css +372 -0
  291. package/templates/storefront/src/styles/ub/fonts.css +17 -0
  292. package/templates/storefront/src/styles/ub/medusa-ui.css +90 -0
  293. package/templates/storefront/src/styles/ub/overrides.css +12 -0
  294. package/templates/storefront/src/styles/ub/tokens.css +9 -0
  295. package/templates/storefront/src/types/global.ts +24 -0
  296. package/templates/storefront/src/types/icon.ts +4 -0
  297. package/templates/storefront/tailwind.config.js +164 -0
  298. package/templates/storefront/themes/black-friday/campaign.en.json +31 -0
  299. package/templates/storefront/themes/black-friday/campaign.fr.json +31 -0
  300. package/templates/storefront/themes/black-friday/theme.json +9 -0
  301. package/templates/storefront/themes/cyber-monday/campaign.en.json +31 -0
  302. package/templates/storefront/themes/cyber-monday/campaign.fr.json +31 -0
  303. package/templates/storefront/themes/cyber-monday/theme.json +9 -0
  304. package/templates/storefront/themes/halloween/campaign.en.json +31 -0
  305. package/templates/storefront/themes/halloween/campaign.fr.json +31 -0
  306. package/templates/storefront/themes/halloween/theme.json +9 -0
  307. package/templates/storefront/themes/noel/campaign.en.json +31 -0
  308. package/templates/storefront/themes/noel/campaign.fr.json +31 -0
  309. package/templates/storefront/themes/noel/theme.json +9 -0
  310. package/templates/storefront/themes/nouvel-an/campaign.en.json +31 -0
  311. package/templates/storefront/themes/nouvel-an/campaign.fr.json +31 -0
  312. package/templates/storefront/themes/nouvel-an/theme.json +9 -0
  313. package/templates/storefront/themes/thanksgiving/campaign.en.json +31 -0
  314. package/templates/storefront/themes/thanksgiving/campaign.fr.json +31 -0
  315. package/templates/storefront/themes/thanksgiving/theme.json +9 -0
  316. package/templates/storefront/tsconfig.json +53 -0
@@ -0,0 +1,70 @@
1
+ "use server"
2
+
3
+ import { getBoutique } from "@lib/boutique/config"
4
+ import { sdk } from "@lib/config"
5
+ import medusaError from "@lib/util/medusa-error"
6
+ import { HttpTypes } from "@medusajs/types"
7
+ import { getCacheOptions } from "./cookies"
8
+
9
+ export const listRegions = async () => {
10
+ const next = {
11
+ ...(await getCacheOptions("regions")),
12
+ }
13
+
14
+ return sdk.client
15
+ .fetch<{ regions: HttpTypes.StoreRegion[] }>(`/store/regions`, {
16
+ method: "GET",
17
+ next,
18
+ cache: "force-cache",
19
+ })
20
+ .then(({ regions }) => regions)
21
+ .catch(medusaError)
22
+ }
23
+
24
+ export const retrieveRegion = async (id: string) => {
25
+ const next = {
26
+ ...(await getCacheOptions(["regions", id].join("-"))),
27
+ }
28
+
29
+ return sdk.client
30
+ .fetch<{ region: HttpTypes.StoreRegion }>(`/store/regions/${id}`, {
31
+ method: "GET",
32
+ next,
33
+ cache: "force-cache",
34
+ })
35
+ .then(({ region }) => region)
36
+ .catch(medusaError)
37
+ }
38
+
39
+ let boutiqueRegion: HttpTypes.StoreRegion | undefined
40
+
41
+ /**
42
+ * Région de la boutique. Le seed crée une région par boutique, nommée par son slug
43
+ * (devise de la boutique + pays de shipsTo) : deux boutiques de même devise mais de
44
+ * pays différents ne se mélangent donc pas. À défaut, région avec sa devise et son pays.
45
+ * Fixée par la config de l'instance, jamais par l'URL ni par un cookie.
46
+ */
47
+ export const getRegion = async (): Promise<HttpTypes.StoreRegion> => {
48
+ if (boutiqueRegion) return boutiqueRegion
49
+
50
+ const { country, currency, slug } = getBoutique()
51
+ const regions = (await listRegions()) ?? []
52
+
53
+ const region =
54
+ regions.find((r) => r.name === slug && r.currency_code?.toLowerCase() === currency) ??
55
+ regions.find(
56
+ (r) =>
57
+ r.currency_code?.toLowerCase() === currency &&
58
+ r.countries?.some((c) => c?.iso_2?.toLowerCase() === country)
59
+ )
60
+
61
+ if (!region) {
62
+ throw new Error(
63
+ `[boutique] aucune région Medusa pour ${slug} (pays « ${country} », devise « ${currency} »). ` +
64
+ `Régions disponibles : ${regions.map((r) => `${r.name} (${r.currency_code})`).join(", ") || "aucune"}`
65
+ )
66
+ }
67
+
68
+ boutiqueRegion = region
69
+ return region
70
+ }
@@ -0,0 +1,38 @@
1
+ "use server"
2
+
3
+ import { sdk } from "@lib/config"
4
+ import { HttpTypes } from "@medusajs/types"
5
+
6
+ import { getAuthHeaders, getCacheOptions } from "./cookies"
7
+
8
+ export const retrieveVariant = async (
9
+ variant_id: string
10
+ ): Promise<HttpTypes.StoreProductVariant | null> => {
11
+ const authHeaders = await getAuthHeaders()
12
+
13
+ if (!authHeaders) return null
14
+
15
+ const headers = {
16
+ ...authHeaders,
17
+ }
18
+
19
+ const next = {
20
+ ...(await getCacheOptions("variants")),
21
+ }
22
+
23
+ return await sdk.client
24
+ .fetch<{ variant: HttpTypes.StoreProductVariant }>(
25
+ `/store/product-variants/${variant_id}`,
26
+ {
27
+ method: "GET",
28
+ query: {
29
+ fields: "*images",
30
+ },
31
+ headers,
32
+ next,
33
+ cache: "force-cache",
34
+ }
35
+ )
36
+ .then(({ variant }) => variant)
37
+ .catch(() => null)
38
+ }
@@ -0,0 +1,29 @@
1
+ import { RefObject, useEffect, useState } from "react"
2
+
3
+ export const useIntersection = (
4
+ element: RefObject<HTMLDivElement | null>,
5
+ rootMargin: string
6
+ ) => {
7
+ const [isVisible, setState] = useState(false)
8
+
9
+ useEffect(() => {
10
+ if (!element.current) {
11
+ return
12
+ }
13
+
14
+ const el = element.current
15
+
16
+ const observer = new IntersectionObserver(
17
+ ([entry]) => {
18
+ setState(entry.isIntersecting)
19
+ },
20
+ { rootMargin }
21
+ )
22
+
23
+ observer.observe(el)
24
+
25
+ return () => observer.unobserve(el)
26
+ }, [element, rootMargin])
27
+
28
+ return isVisible
29
+ }
@@ -0,0 +1,46 @@
1
+ import { useState } from "react"
2
+
3
+ export type StateType = [boolean, () => void, () => void, () => void] & {
4
+ state: boolean
5
+ open: () => void
6
+ close: () => void
7
+ toggle: () => void
8
+ }
9
+
10
+ /**
11
+ *
12
+ * @param initialState - boolean
13
+ * @returns An array like object with `state`, `open`, `close`, and `toggle` properties
14
+ * to allow both object and array destructuring
15
+ *
16
+ * ```
17
+ * const [showModal, openModal, closeModal, toggleModal] = useToggleState()
18
+ * // or
19
+ * const { state, open, close, toggle } = useToggleState()
20
+ * ```
21
+ */
22
+
23
+ const useToggleState = (initialState = false) => {
24
+ const [state, setState] = useState<boolean>(initialState)
25
+
26
+ const close = () => {
27
+ setState(false)
28
+ }
29
+
30
+ const open = () => {
31
+ setState(true)
32
+ }
33
+
34
+ const toggle = () => {
35
+ setState((state) => !state)
36
+ }
37
+
38
+ const hookData = [state, open, close, toggle] as StateType
39
+ hookData.state = state
40
+ hookData.open = open
41
+ hookData.close = close
42
+ hookData.toggle = toggle
43
+ return hookData
44
+ }
45
+
46
+ export default useToggleState
@@ -0,0 +1,28 @@
1
+ import { isEqual, pick } from "lodash"
2
+
3
+ export default function compareAddresses(address1: any, address2: any) {
4
+ return isEqual(
5
+ pick(address1, [
6
+ "first_name",
7
+ "last_name",
8
+ "address_1",
9
+ "company",
10
+ "postal_code",
11
+ "city",
12
+ "country_code",
13
+ "province",
14
+ "phone",
15
+ ]),
16
+ pick(address2, [
17
+ "first_name",
18
+ "last_name",
19
+ "address_1",
20
+ "company",
21
+ "postal_code",
22
+ "city",
23
+ "country_code",
24
+ "province",
25
+ "phone",
26
+ ])
27
+ )
28
+ }
@@ -0,0 +1,6 @@
1
+ export const getPercentageDiff = (original: number, calculated: number) => {
2
+ const diff = original - calculated
3
+ const decrease = (diff / original) * 100
4
+
5
+ return decrease.toFixed()
6
+ }
@@ -0,0 +1,79 @@
1
+ import { HttpTypes } from "@medusajs/types"
2
+ import { getPercentageDiff } from "./get-percentage-diff"
3
+ import { convertToLocale } from "./money"
4
+
5
+ export const getPricesForVariant = (variant: any) => {
6
+ if (!variant?.calculated_price?.calculated_amount) {
7
+ return null
8
+ }
9
+
10
+ return {
11
+ calculated_price_number: variant.calculated_price.calculated_amount,
12
+ calculated_price: convertToLocale({
13
+ amount: variant.calculated_price.calculated_amount,
14
+ currency_code: variant.calculated_price.currency_code,
15
+ }),
16
+ original_price_number: variant.calculated_price.original_amount,
17
+ original_price: convertToLocale({
18
+ amount: variant.calculated_price.original_amount,
19
+ currency_code: variant.calculated_price.currency_code,
20
+ }),
21
+ currency_code: variant.calculated_price.currency_code,
22
+ price_type: variant.calculated_price.calculated_price.price_list_type,
23
+ percentage_diff: getPercentageDiff(
24
+ variant.calculated_price.original_amount,
25
+ variant.calculated_price.calculated_amount
26
+ ),
27
+ }
28
+ }
29
+
30
+ export function getProductPrice({
31
+ product,
32
+ variantId,
33
+ }: {
34
+ product: HttpTypes.StoreProduct
35
+ variantId?: string
36
+ }) {
37
+ if (!product || !product.id) {
38
+ throw new Error("No product provided")
39
+ }
40
+
41
+ const cheapestPrice = () => {
42
+ if (!product || !product.variants?.length) {
43
+ return null
44
+ }
45
+
46
+ const cheapestVariant: any = product.variants
47
+ .filter((v: any) => !!v.calculated_price)
48
+ .sort((a: any, b: any) => {
49
+ return (
50
+ a.calculated_price.calculated_amount -
51
+ b.calculated_price.calculated_amount
52
+ )
53
+ })[0]
54
+
55
+ return getPricesForVariant(cheapestVariant)
56
+ }
57
+
58
+ const variantPrice = () => {
59
+ if (!product || !variantId) {
60
+ return null
61
+ }
62
+
63
+ const variant: any = product.variants?.find(
64
+ (v) => v.id === variantId || v.sku === variantId
65
+ )
66
+
67
+ if (!variant) {
68
+ return null
69
+ }
70
+
71
+ return getPricesForVariant(variant)
72
+ }
73
+
74
+ return {
75
+ product,
76
+ cheapestPrice: cheapestPrice(),
77
+ variantPrice: variantPrice(),
78
+ }
79
+ }
@@ -0,0 +1,11 @@
1
+ export const isObject = (input: any) => input instanceof Object
2
+ export const isArray = (input: any) => Array.isArray(input)
3
+ export const isEmpty = (input: any) => {
4
+ return (
5
+ input === null ||
6
+ input === undefined ||
7
+ (isObject(input) && Object.keys(input).length === 0) ||
8
+ (isArray(input) && (input as any[]).length === 0) ||
9
+ (typeof input === "string" && input.trim().length === 0)
10
+ )
11
+ }
@@ -0,0 +1,22 @@
1
+ export default function medusaError(error: any): never {
2
+ if (error.response) {
3
+ // The request was made and the server responded with a status code
4
+ // that falls out of the range of 2xx
5
+ const u = new URL(error.config.url, error.config.baseURL)
6
+ console.error("Resource:", u.toString())
7
+ console.error("Response data:", error.response.data)
8
+ console.error("Status code:", error.response.status)
9
+ console.error("Headers:", error.response.headers)
10
+
11
+ // Extracting the error message from the response data
12
+ const message = error.response.data.message || error.response.data
13
+
14
+ throw new Error(message.charAt(0).toUpperCase() + message.slice(1) + ".")
15
+ } else if (error.request) {
16
+ // The request was made but no response was received
17
+ throw new Error("No response received: " + error.request)
18
+ } else {
19
+ // Something happened in setting up the request that triggered an Error
20
+ throw new Error("Error setting up the request: " + error.message)
21
+ }
22
+ }
@@ -0,0 +1,35 @@
1
+ import { isEmpty } from "./isEmpty"
2
+
3
+ type ConvertToLocaleParams = {
4
+ amount: number
5
+ currency_code: string
6
+ minimumFractionDigits?: number
7
+ maximumFractionDigits?: number
8
+ locale?: string
9
+ }
10
+
11
+ /**
12
+ * Locale de l'instance (ex. "fr-FR") : côté serveur posée par getBoutique(),
13
+ * côté navigateur lue sur <html lang>. Serveur et client formatent donc pareil.
14
+ */
15
+ const instanceLocale = () =>
16
+ typeof document !== "undefined"
17
+ ? document.documentElement.lang || "en-US"
18
+ : process.env.BOUTIQUE_LOCALE || "en-US"
19
+
20
+ export const convertToLocale = ({
21
+ amount,
22
+ currency_code,
23
+ minimumFractionDigits,
24
+ maximumFractionDigits,
25
+ locale = instanceLocale(),
26
+ }: ConvertToLocaleParams) => {
27
+ return currency_code && !isEmpty(currency_code)
28
+ ? new Intl.NumberFormat(locale, {
29
+ style: "currency",
30
+ currency: currency_code,
31
+ minimumFractionDigits,
32
+ maximumFractionDigits,
33
+ }).format(amount)
34
+ : amount.toString()
35
+ }
@@ -0,0 +1,5 @@
1
+ import { HttpTypes } from "@medusajs/types";
2
+
3
+ export const isSimpleProduct = (product: HttpTypes.StoreProduct): boolean => {
4
+ return product.options?.length === 1 && product.options[0].values?.length === 1;
5
+ }
@@ -0,0 +1,5 @@
1
+ const repeat = (times: number) => {
2
+ return Array.from(Array(times).keys())
3
+ }
4
+
5
+ export default repeat
@@ -0,0 +1,50 @@
1
+ import { HttpTypes } from "@medusajs/types"
2
+ import { SortOptions } from "@modules/store/components/refinement-list/sort-products"
3
+
4
+ interface MinPricedProduct extends HttpTypes.StoreProduct {
5
+ _minPrice?: number
6
+ }
7
+
8
+ /**
9
+ * Helper function to sort products by price until the store API supports sorting by price
10
+ * @param products
11
+ * @param sortBy
12
+ * @returns products sorted by price
13
+ */
14
+ export function sortProducts(
15
+ products: HttpTypes.StoreProduct[],
16
+ sortBy: SortOptions
17
+ ): HttpTypes.StoreProduct[] {
18
+ let sortedProducts = products as MinPricedProduct[]
19
+
20
+ if (["price_asc", "price_desc"].includes(sortBy)) {
21
+ // Precompute the minimum price for each product
22
+ sortedProducts.forEach((product) => {
23
+ if (product.variants && product.variants.length > 0) {
24
+ product._minPrice = Math.min(
25
+ ...product.variants.map(
26
+ (variant) => variant?.calculated_price?.calculated_amount || 0
27
+ )
28
+ )
29
+ } else {
30
+ product._minPrice = Infinity
31
+ }
32
+ })
33
+
34
+ // Sort products based on the precomputed minimum prices
35
+ sortedProducts.sort((a, b) => {
36
+ const diff = a._minPrice! - b._minPrice!
37
+ return sortBy === "price_asc" ? diff : -diff
38
+ })
39
+ }
40
+
41
+ if (sortBy === "created_at") {
42
+ sortedProducts.sort((a, b) => {
43
+ return (
44
+ new Date(b.created_at!).getTime() - new Date(a.created_at!).getTime()
45
+ )
46
+ })
47
+ }
48
+
49
+ return sortedProducts
50
+ }
@@ -0,0 +1,140 @@
1
+ import { T } from "@lib/boutique/i18n-client"
2
+ import { Disclosure } from "@headlessui/react"
3
+ import { Badge, Button, clx } from "@medusajs/ui"
4
+ import { useEffect } from "react"
5
+
6
+ import useToggleState from "@lib/hooks/use-toggle-state"
7
+ import { useFormStatus } from "react-dom"
8
+
9
+ type AccountInfoProps = {
10
+ label: string
11
+ currentInfo: string | React.ReactNode
12
+ isSuccess?: boolean
13
+ isError?: boolean
14
+ errorMessage?: string
15
+ clearState: () => void
16
+ children?: React.ReactNode
17
+ 'data-testid'?: string
18
+ }
19
+
20
+ const AccountInfo = ({
21
+ label,
22
+ currentInfo,
23
+ isSuccess,
24
+ isError,
25
+ clearState,
26
+ errorMessage = "account.error",
27
+ children,
28
+ 'data-testid': dataTestid
29
+ }: AccountInfoProps) => {
30
+ const { state, close, toggle } = useToggleState()
31
+
32
+ const { pending } = useFormStatus()
33
+
34
+ const handleToggle = () => {
35
+ clearState()
36
+ setTimeout(() => toggle(), 100)
37
+ }
38
+
39
+ useEffect(() => {
40
+ if (isSuccess) {
41
+ close()
42
+ }
43
+ }, [isSuccess, close])
44
+
45
+ return (
46
+ <div className="text-small-regular" data-testid={dataTestid}>
47
+ <div className="flex items-end justify-between">
48
+ <div className="flex flex-col">
49
+ <span className="uppercase text-ui-fg-base"><T k={label} /></span>
50
+ <div className="flex items-center flex-1 basis-0 justify-end gap-x-4">
51
+ {typeof currentInfo === "string" ? (
52
+ <span className="font-semibold" data-testid="current-info">{currentInfo}</span>
53
+ ) : (
54
+ currentInfo
55
+ )}
56
+ </div>
57
+ </div>
58
+ <div>
59
+ <Button
60
+ variant="secondary"
61
+ className="w-[100px] min-h-[25px] py-1"
62
+ onClick={handleToggle}
63
+ type={state ? "reset" : "button"}
64
+ data-testid="edit-button"
65
+ data-active={state}
66
+ >
67
+ {state ? "Cancel" : "Edit"}
68
+ </Button>
69
+ </div>
70
+ </div>
71
+
72
+ {/* Success state */}
73
+ <Disclosure>
74
+ <Disclosure.Panel
75
+ static
76
+ className={clx(
77
+ "transition-[max-height,opacity] duration-300 ease-in-out overflow-hidden",
78
+ {
79
+ "max-h-[1000px] opacity-100": isSuccess,
80
+ "max-h-0 opacity-0": !isSuccess,
81
+ }
82
+ )}
83
+ data-testid="success-message"
84
+ >
85
+ <Badge className="p-2 my-4" color="green">
86
+ <span><T k="account.updated" vars={{ field: "" }} /></span>
87
+ </Badge>
88
+ </Disclosure.Panel>
89
+ </Disclosure>
90
+
91
+ {/* Error state */}
92
+ <Disclosure>
93
+ <Disclosure.Panel
94
+ static
95
+ className={clx(
96
+ "transition-[max-height,opacity] duration-300 ease-in-out overflow-hidden",
97
+ {
98
+ "max-h-[1000px] opacity-100": isError,
99
+ "max-h-0 opacity-0": !isError,
100
+ }
101
+ )}
102
+ data-testid="error-message"
103
+ >
104
+ <Badge className="p-2 my-4" color="red">
105
+ <span><T k={errorMessage} /></span>
106
+ </Badge>
107
+ </Disclosure.Panel>
108
+ </Disclosure>
109
+
110
+ <Disclosure>
111
+ <Disclosure.Panel
112
+ static
113
+ className={clx(
114
+ "transition-[max-height,opacity] duration-300 ease-in-out overflow-visible",
115
+ {
116
+ "max-h-[1000px] opacity-100": state,
117
+ "max-h-0 opacity-0": !state,
118
+ }
119
+ )}
120
+ >
121
+ <div className="flex flex-col gap-y-2 py-4">
122
+ <div>{children}</div>
123
+ <div className="flex items-center justify-end mt-2">
124
+ <Button
125
+ isLoading={pending}
126
+ className="w-full small:max-w-[140px]"
127
+ type="submit"
128
+ data-testid="save-button"
129
+ >
130
+ <T k="form.saveChanges" />
131
+ </Button>
132
+ </div>
133
+ </div>
134
+ </Disclosure.Panel>
135
+ </Disclosure>
136
+ </div>
137
+ )
138
+ }
139
+
140
+ export default AccountInfo