@salifbiaye/create-boutique 0.3.0 → 0.3.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.
@@ -231,9 +231,12 @@ const day = (d, add = 0, end = false) => {
231
231
  };
232
232
  /** Événement d'un thème : « black-friday-2 » → « black-friday ». */
233
233
  const baseTheme = (theme) => theme.replace(/-\d+$/, "");
234
- /** Visuel du hero dans seed/images : hero-<slug>.*, sinon hero-<thème>.*, sinon hero.* (importé par le seed). */
235
- function heroImage(images, slug, theme) {
236
- for (const name of [`hero-${slug}`, `hero-${theme}`, "hero"]) {
234
+ /**
235
+ * Visuel de campagne dans seed/images (importé par le seed) : <prefix>-<slug>.*, sinon <prefix>-<thème>.*, sinon <prefix>.*
236
+ * prefix = "hero" (bannière du haut) ou "promo" (bandeau promo).
237
+ */
238
+ function heroImage(images, slug, theme, prefix = "hero") {
239
+ for (const name of [`${prefix}-${slug}`, `${prefix}-${theme}`, prefix]) {
237
240
  const found = images.find((f) => /\.(png|jpe?g|webp)$/i.test(f) && f.replace(/\.[^.]+$/, "").toLowerCase() === name);
238
241
  if (found)
239
242
  return found;
@@ -295,6 +298,9 @@ export function writeCampaignFiles(dir, p, language) {
295
298
  const hero = heroImage(images, b.slug, b.theme);
296
299
  if (hero)
297
300
  Object.assign(campaign, { hero: { image: hero } });
301
+ const promo = heroImage(images, b.slug, b.theme, "promo");
302
+ if (promo)
303
+ Object.assign(campaign, { promo: { image: promo } });
298
304
  fs.writeFileSync(file, JSON.stringify(campaign, null, 2) + "\n");
299
305
  }
300
306
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salifbiaye/create-boutique",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Usine à Boutiques : génère un projet complet (backend Medusa + une boutique Next.js par campagne) prêt pour docker compose",
5
5
  "type": "module",
6
6
  "repository": { "type": "git", "url": "git+https://github.com/salifbiaye/create-boutique.git" },
@@ -77,6 +77,20 @@ async function HarvestProducts({ campaign }: { campaign: Campaign }) {
77
77
  )
78
78
  }
79
79
 
80
+ /** Bandeau promo avec, à défaut d'image de campagne, la photo du produit le plus remisé. */
81
+ async function Promo({ campaign }: { campaign: Campaign }) {
82
+ const {
83
+ response: { products },
84
+ } = await listProducts({ queryParams: { limit: 12, fields: "*categories,*collection" } })
85
+ const off = (p: (typeof products)[number]) =>
86
+ Math.max(0, ...(p.variants ?? []).map((v) => {
87
+ const c = v.calculated_price
88
+ return c?.original_amount && c.calculated_amount != null ? 1 - c.calculated_amount / c.original_amount : 0
89
+ }))
90
+ const best = [...products].filter((p) => p.thumbnail).sort((a, b) => off(b) - off(a))[0]
91
+ return <PromoBanner campaign={campaign} fallbackImage={best?.thumbnail ?? undefined} />
92
+ }
93
+
80
94
  function GridFallback() {
81
95
  return <div className="ub-section" aria-busy="true" style={{ minHeight: 320 }} />
82
96
  }
@@ -114,7 +128,9 @@ export default async function Home() {
114
128
  <Suspense fallback={<GridFallback />}>
115
129
  <Showcase part="spotlight" eyebrow={campaign.hero.eyebrow} />
116
130
  </Suspense>
117
- <PromoBanner campaign={campaign} />
131
+ <Suspense fallback={<GridFallback />}>
132
+ <Promo campaign={campaign} />
133
+ </Suspense>
118
134
  <Suspense fallback={<GridFallback />}>
119
135
  <Products title={campaign.sections?.products ?? t("section.products")} />
120
136
  </Suspense>
@@ -145,9 +145,10 @@ export function TrustBar({ freeShipping }: { freeShipping?: string }) {
145
145
  )
146
146
  }
147
147
 
148
- export function PromoBanner({ campaign }: { campaign: Campaign }) {
148
+ /** Bandeau promo. Image : celle de la campagne (admin → Campagnes), sinon `fallbackImage` (photo d'un produit remisé). */
149
+ export function PromoBanner({ campaign, fallbackImage }: { campaign: Campaign; fallbackImage?: string }) {
149
150
  const { promo } = campaign
150
- const promoImage = promo.image && /^https?:\/\//.test(promo.image) ? promo.image : undefined
151
+ const promoImage = (promo.image && /^https?:\/\//.test(promo.image) ? promo.image : undefined) ?? fallbackImage
151
152
 
152
153
  return (
153
154
  <section className="ub-section" style={{ paddingTop: 0 }}>