@salifbiaye/create-boutique 0.6.0 → 0.7.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 (182) hide show
  1. package/cli/dist/code.js +110 -0
  2. package/cli/dist/commands.js +145 -11
  3. package/cli/dist/create.js +54 -7
  4. package/cli/dist/generate.js +65 -4
  5. package/cli/dist/guide.js +10 -2
  6. package/cli/dist/index.js +20 -7
  7. package/cli/dist/payments.js +150 -0
  8. package/docs/fiche-client.en.html +2 -2
  9. package/docs/fiche-client.html +2 -2
  10. package/docs/guide-complet.fr.html +681 -256
  11. package/docs/guide-images/categories.jpg +0 -0
  12. package/docs/guide-images/clients.jpg +0 -0
  13. package/docs/guide-images/collections.jpg +0 -0
  14. package/docs/guide-images/commande-detail.jpg +0 -0
  15. package/docs/guide-images/commande-expedier.jpg +0 -0
  16. package/docs/guide-images/commande-menu.jpg +0 -0
  17. package/docs/guide-images/commande-paiement.jpg +0 -0
  18. package/docs/guide-images/commandes-liste.jpg +0 -0
  19. package/docs/guide-images/connexion.jpg +0 -0
  20. package/docs/guide-images/groupes-clients.jpg +0 -0
  21. package/docs/guide-images/inventaire.jpg +0 -0
  22. package/docs/guide-images/listes-prix.jpg +0 -0
  23. package/docs/guide-images/menu.jpg +0 -0
  24. package/docs/guide-images/mes-boutiques-fermer.jpg +0 -0
  25. package/docs/guide-images/mes-boutiques-panneau.jpg +0 -0
  26. package/docs/guide-images/mes-boutiques.jpg +0 -0
  27. package/docs/guide-images/parametres-boutique.jpg +0 -0
  28. package/docs/guide-images/parametres-canaux.jpg +0 -0
  29. package/docs/guide-images/parametres-emplacements.jpg +0 -0
  30. package/docs/guide-images/parametres-profil.jpg +0 -0
  31. package/docs/guide-images/parametres-regions.jpg +0 -0
  32. package/docs/guide-images/parametres-utilisateurs.jpg +0 -0
  33. package/docs/guide-images/produit-creer-details.jpg +0 -0
  34. package/docs/guide-images/produit-detail.jpg +0 -0
  35. package/docs/guide-images/produit-variantes-menu.jpg +0 -0
  36. package/docs/guide-images/produit-variantes.jpg +0 -0
  37. package/docs/guide-images/produits-liste.jpg +0 -0
  38. package/docs/guide-images/promotion-details.jpg +0 -0
  39. package/docs/guide-images/promotion-type.jpg +0 -0
  40. package/docs/guide-images/promotions-liste.jpg +0 -0
  41. package/docs/guide-images/site-boutique.jpg +0 -0
  42. package/docs/guide-images/site-fermee.png +0 -0
  43. package/docs/guide-images/vue-ensemble.jpg +0 -0
  44. package/package.json +3 -2
  45. package/templates/backend/medusa-config.ts +31 -25
  46. package/templates/backend/package-lock.json +21 -0
  47. package/templates/backend/package.json +2 -0
  48. package/templates/backend/src/admin/i18n/json/en.json +51 -3
  49. package/templates/backend/src/admin/i18n/json/fr.json +51 -3
  50. package/templates/backend/src/admin/routes/boutique-campaigns/campaign-drawer.tsx +2 -0
  51. package/templates/backend/src/admin/routes/boutique-campaigns/page.tsx +36 -1
  52. package/templates/backend/src/admin/routes/reviews/page.tsx +259 -0
  53. package/templates/backend/src/api/admin/boutique-campaigns/route.ts +3 -1
  54. package/templates/backend/src/api/admin/boutique-online/[salesChannelId]/route.ts +22 -0
  55. package/templates/backend/src/api/admin/reviews/[id]/route.ts +15 -0
  56. package/templates/backend/src/api/admin/reviews/route.ts +47 -0
  57. package/templates/backend/src/api/middlewares.ts +7 -0
  58. package/templates/backend/src/api/store/campaign/route.ts +5 -1
  59. package/templates/backend/src/api/store/reviews/latest/route.ts +39 -0
  60. package/templates/backend/src/api/store/reviews/order/route.ts +36 -0
  61. package/templates/backend/src/api/store/reviews/route.ts +43 -0
  62. package/templates/backend/src/api/store/reviews/summary/route.ts +28 -0
  63. package/templates/backend/src/emails/en.json +3 -1
  64. package/templates/backend/src/emails/fr.json +3 -1
  65. package/templates/backend/src/lib/email-config.ts +14 -0
  66. package/templates/backend/src/lib/emails.ts +9 -0
  67. package/templates/backend/src/lib/notify.ts +14 -8
  68. package/templates/backend/src/lib/payment-config.ts +66 -0
  69. package/templates/backend/src/lib/project.ts +12 -0
  70. package/templates/backend/src/lib/redirect-payment.ts +264 -0
  71. package/templates/backend/src/lib/review-token.ts +40 -0
  72. package/templates/backend/src/lib/reviews.ts +25 -0
  73. package/templates/backend/src/modules/naboopay/index.ts +6 -0
  74. package/templates/backend/src/modules/naboopay/service.ts +92 -0
  75. package/templates/backend/src/modules/paydunya/index.ts +6 -0
  76. package/templates/backend/src/modules/paydunya/service.ts +89 -0
  77. package/templates/backend/src/modules/paytech/index.ts +6 -0
  78. package/templates/backend/src/modules/paytech/service.ts +114 -0
  79. package/templates/backend/src/modules/product-review/index.ts +8 -0
  80. package/templates/backend/src/modules/product-review/migrations/Migration20261001090000.ts +18 -0
  81. package/templates/backend/src/modules/product-review/models/review.ts +25 -0
  82. package/templates/backend/src/modules/product-review/service.ts +6 -0
  83. package/templates/backend/src/modules/product-review/validators.ts +27 -0
  84. package/templates/backend/src/modules/smtp/index.ts +6 -0
  85. package/templates/backend/src/modules/smtp/service.ts +73 -0
  86. package/templates/backend/src/scripts/seed.ts +52 -36
  87. package/templates/backend/src/subscribers/fulfillment-emails.ts +1 -1
  88. package/templates/backend/src/workflows/reviews.ts +21 -0
  89. package/templates/backend/src/workflows/steps/create-review.ts +52 -0
  90. package/templates/backend/src/workflows/steps/update-review.ts +36 -0
  91. package/templates/storefront/PERSONNALISER.md +134 -0
  92. package/templates/storefront/locales/en.json +198 -11
  93. package/templates/storefront/locales/fr.json +198 -11
  94. package/templates/storefront/public/custom/LISEZMOI.txt +2 -0
  95. package/templates/storefront/src/app/(checkout)/layout.tsx +1 -1
  96. package/templates/storefront/src/app/(main)/layout.tsx +8 -1
  97. package/templates/storefront/src/app/(main)/legal/[page]/page.tsx +42 -7
  98. package/templates/storefront/src/app/(main)/page.tsx +149 -4
  99. package/templates/storefront/src/app/(main)/review/page.tsx +60 -0
  100. package/templates/storefront/src/app/(main)/store/page.tsx +3 -1
  101. package/templates/storefront/src/app/(main)/template.tsx +17 -2
  102. package/templates/storefront/src/app/layout.tsx +13 -1
  103. package/templates/storefront/src/lib/boutique/campaign.ts +5 -1
  104. package/templates/storefront/src/lib/boutique/page-name.ts +25 -0
  105. package/templates/storefront/src/lib/boutique/schema.ts +3 -1
  106. package/templates/storefront/src/lib/constants.tsx +17 -0
  107. package/templates/storefront/src/lib/data/review-actions.ts +28 -0
  108. package/templates/storefront/src/lib/data/reviews.ts +64 -0
  109. package/templates/storefront/src/modules/checkout/components/payment/index.tsx +26 -4
  110. package/templates/storefront/src/modules/checkout/components/payment-button/index.tsx +85 -2
  111. package/templates/storefront/src/modules/products/templates/index.tsx +5 -0
  112. package/templates/storefront/src/modules/store/components/shop-search.tsx +85 -0
  113. package/templates/storefront/src/modules/store/templates/index.tsx +16 -8
  114. package/templates/storefront/src/modules/store/templates/paginated-products.tsx +15 -3
  115. package/templates/storefront/src/modules/ub/album.tsx +2 -0
  116. package/templates/storefront/src/modules/ub/arena.tsx +182 -0
  117. package/templates/storefront/src/modules/ub/campaign-blocks.tsx +3 -2
  118. package/templates/storefront/src/modules/ub/campaign-ended.tsx +27 -13
  119. package/templates/storefront/src/modules/ub/cinema.tsx +2 -0
  120. package/templates/storefront/src/modules/ub/cookie-banner.tsx +62 -0
  121. package/templates/storefront/src/modules/ub/gala.tsx +2 -0
  122. package/templates/storefront/src/modules/ub/grimoire.tsx +35 -8
  123. package/templates/storefront/src/modules/ub/harvest.tsx +2 -1
  124. package/templates/storefront/src/modules/ub/hero-image.tsx +14 -0
  125. package/templates/storefront/src/modules/ub/home-kit.ts +45 -0
  126. package/templates/storefront/src/modules/ub/lantern.tsx +244 -0
  127. package/templates/storefront/src/modules/ub/locker.tsx +180 -0
  128. package/templates/storefront/src/modules/ub/mosaic.tsx +189 -0
  129. package/templates/storefront/src/modules/ub/nav.tsx +8 -5
  130. package/templates/storefront/src/modules/ub/review-form.tsx +62 -0
  131. package/templates/storefront/src/modules/ub/reviews.tsx +131 -0
  132. package/templates/storefront/src/modules/ub/riviera.tsx +175 -0
  133. package/templates/storefront/src/modules/ub/souk.tsx +214 -0
  134. package/templates/storefront/src/modules/ub/sunset-checklist.tsx +95 -0
  135. package/templates/storefront/src/modules/ub/sunset.tsx +164 -0
  136. package/templates/storefront/src/modules/ub/tech.tsx +2 -2
  137. package/templates/storefront/src/modules/ub/training.tsx +196 -0
  138. package/templates/storefront/src/proxy.ts +2 -0
  139. package/templates/storefront/src/styles/custom.css +26 -0
  140. package/templates/storefront/src/styles/ub/album.css +1 -1
  141. package/templates/storefront/src/styles/ub/arena.css +120 -0
  142. package/templates/storefront/src/styles/ub/cinema.css +1 -1
  143. package/templates/storefront/src/styles/ub/gala.css +1 -1
  144. package/templates/storefront/src/styles/ub/grimoire.css +23 -7
  145. package/templates/storefront/src/styles/ub/harvest.css +7 -6
  146. package/templates/storefront/src/styles/ub/lantern.css +157 -0
  147. package/templates/storefront/src/styles/ub/locker.css +115 -0
  148. package/templates/storefront/src/styles/ub/media.css +10 -0
  149. package/templates/storefront/src/styles/ub/mosaic.css +118 -0
  150. package/templates/storefront/src/styles/ub/motion.css +0 -3
  151. package/templates/storefront/src/styles/ub/overrides.css +10 -0
  152. package/templates/storefront/src/styles/ub/pages.css +176 -0
  153. package/templates/storefront/src/styles/ub/reviews.css +85 -0
  154. package/templates/storefront/src/styles/ub/riviera.css +103 -0
  155. package/templates/storefront/src/styles/ub/souk.css +144 -0
  156. package/templates/storefront/src/styles/ub/sunset.css +123 -0
  157. package/templates/storefront/src/styles/ub/tech.css +2 -5
  158. package/templates/storefront/src/styles/ub/training.css +120 -0
  159. package/templates/storefront/themes/jeux/campaign.en.json +31 -0
  160. package/templates/storefront/themes/jeux/campaign.fr.json +31 -0
  161. package/templates/storefront/themes/jeux/theme.json +11 -0
  162. package/templates/storefront/themes/jeux-2/campaign.en.json +31 -0
  163. package/templates/storefront/themes/jeux-2/campaign.fr.json +31 -0
  164. package/templates/storefront/themes/jeux-2/theme.json +11 -0
  165. package/templates/storefront/themes/mubarak/campaign.en.json +31 -0
  166. package/templates/storefront/themes/mubarak/campaign.fr.json +31 -0
  167. package/templates/storefront/themes/mubarak/theme.json +11 -0
  168. package/templates/storefront/themes/mubarak-2/campaign.en.json +31 -0
  169. package/templates/storefront/themes/mubarak-2/campaign.fr.json +31 -0
  170. package/templates/storefront/themes/mubarak-2/theme.json +11 -0
  171. package/templates/storefront/themes/sport/campaign.en.json +31 -0
  172. package/templates/storefront/themes/sport/campaign.fr.json +31 -0
  173. package/templates/storefront/themes/sport/theme.json +11 -0
  174. package/templates/storefront/themes/sport-2/campaign.en.json +31 -0
  175. package/templates/storefront/themes/sport-2/campaign.fr.json +31 -0
  176. package/templates/storefront/themes/sport-2/theme.json +11 -0
  177. package/templates/storefront/themes/summer/campaign.en.json +31 -0
  178. package/templates/storefront/themes/summer/campaign.fr.json +31 -0
  179. package/templates/storefront/themes/summer/theme.json +11 -0
  180. package/templates/storefront/themes/summer-2/campaign.en.json +31 -0
  181. package/templates/storefront/themes/summer-2/campaign.fr.json +31 -0
  182. package/templates/storefront/themes/summer-2/theme.json +11 -0
package/cli/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import fs from "node:fs";
3
3
  import { parseArgs } from "node:util";
4
4
  import pc from "picocolors";
5
5
  import { z } from "zod";
6
- import { add, admin, apply, urls, backup, campaignExport, campaignImport, check, guide, remove, set, stripeWebhook, themes, update } from "./commands.js";
6
+ import { add, admin, apply, urls, backup, restore, campaignExport, campaignImport, check, guide, remove, set, stripeWebhook, themes, update } from "./commands.js";
7
7
  import { answersSchema, create } from "./create.js";
8
8
  import { ProjectError } from "./project.js";
9
9
  const HELP = `create-boutique — Usine à Boutiques
@@ -18,7 +18,9 @@ const HELP = `create-boutique — Usine à Boutiques
18
18
  --site-url <domaine> mode single : domaine du site
19
19
  --mode multi|single multi (défaut) : un site par boutique ; single : un seul site, boutique active choisie dans l'admin
20
20
  --stripe-pk / --stripe-sk / --stripe-whsec clés Stripe (optionnelles)
21
- --resend-key / --email-from e-mails (optionnels)
21
+ --resend-key / --email-from e-mails par Resend (optionnels)
22
+ --smtp-host smtp.gmail.com --smtp-user <adresse> e-mails par SMTP (mot de passe : SMTP_PASS dans .env)
23
+ --mobile paytech,paydunya,naboopay paiement Wave / Orange Money (clés dans .env ; détails : create-boutique sans --yes)
22
24
  --products <csv> --images <dossier> catalogue (optionnel)
23
25
  --store-port 8001 --api-port 9000 --storage-port 9100 premiers ports essayés
24
26
  --config <fichier.json> toutes les options dans un fichier (mêmes clés, en camelCase)
@@ -32,16 +34,18 @@ const HELP = `create-boutique — Usine à Boutiques
32
34
  create-boutique set <dossier> [--mode multi|single] change de mode (un site par boutique ↔ un seul site)
33
35
  create-boutique set <dossier> [--boutique <slug>] [--name …] [--theme …] [--currency …] [--country …] [--language …]
34
36
  create-boutique themes liste les thèmes
35
- create-boutique update <dossier> [--no-up] met à jour le code (config, .env, données conservés)
37
+ create-boutique update <dossier> [--no-up] [--yes] met à jour le code (config, .env, données, custom.css conservés ;
38
+ code modifié à la main : listé, copié dans backups/, confirmation)
36
39
  create-boutique check <dossier> commande test dans chaque boutique
37
- create-boutique backup <dossier> base + images dans backups/<date>/
40
+ create-boutique backup <dossier> [--keep 7] base + images dans backups/<date>/ (--keep : garde les N plus récentes)
41
+ create-boutique restore <dossier> [--from <nom>] [--yes] remet une sauvegarde (la plus récente par défaut)
38
42
  create-boutique admin <dossier> adresse, e-mail et mot de passe de l'admin
39
43
  create-boutique guide <dossier> régénère GUIDE-CLIENT.pdf et GUIDE-COMPLET.pdf
40
44
  create-boutique stripe-webhook <dossier> --url https://api.marque.com crée le webhook Stripe, écrit STRIPE_WEBHOOK_SECRET
41
45
  create-boutique campaign export <dossier> campagnes en base → config/campaigns/*.json
42
46
  create-boutique campaign import <dossier> [--boutique <slug>] [--yes] JSON → base (écrase)
43
47
  `;
44
- const COMMANDS = new Set(["urls", "apply", "add", "remove", "set", "themes", "update", "check", "backup", "campaign", "admin", "guide", "stripe-webhook", "help"]);
48
+ const COMMANDS = new Set(["restore", "urls", "apply", "add", "remove", "set", "themes", "update", "check", "backup", "campaign", "admin", "guide", "stripe-webhook", "help"]);
45
49
  async function main() {
46
50
  const { values, positionals } = parseArgs({
47
51
  allowPositionals: true,
@@ -59,11 +63,15 @@ async function main() {
59
63
  "files-url": { type: "string" },
60
64
  "site-url": { type: "string" },
61
65
  from: { type: "string" },
66
+ keep: { type: "string" },
62
67
  "stripe-pk": { type: "string" },
63
68
  "stripe-sk": { type: "string" },
64
69
  "stripe-whsec": { type: "string" },
65
70
  "resend-key": { type: "string" },
66
71
  "email-from": { type: "string" },
72
+ "smtp-host": { type: "string" },
73
+ "smtp-user": { type: "string" },
74
+ mobile: { type: "string" },
67
75
  products: { type: "string" },
68
76
  images: { type: "string" },
69
77
  "store-port": { type: "string" },
@@ -108,6 +116,9 @@ async function main() {
108
116
  stripeWhsec: values["stripe-whsec"],
109
117
  resendKey: values["resend-key"],
110
118
  emailFrom: values["email-from"],
119
+ smtpHost: values["smtp-host"],
120
+ smtpUser: values["smtp-user"],
121
+ mobile: values.mobile ? values.mobile.split(",").map((m) => m.trim()).filter(Boolean) : undefined,
111
122
  products: values.products,
112
123
  images: values.images,
113
124
  storePort: port(values["store-port"]),
@@ -140,11 +151,13 @@ async function main() {
140
151
  case "themes":
141
152
  return themes();
142
153
  case "update":
143
- return update(dirArg(), up);
154
+ return update(dirArg(), { ...up, yes: !!values.yes });
144
155
  case "check":
145
156
  return check(dirArg());
146
157
  case "backup":
147
- return backup(dirArg());
158
+ return void backup(dirArg(), { keep: values.keep ? Number(values.keep) : undefined });
159
+ case "restore":
160
+ return restore(dirArg(), { from: values.from, yes: !!values.yes });
148
161
  case "admin":
149
162
  return admin(dirArg());
150
163
  case "guide":
@@ -0,0 +1,150 @@
1
+ import * as p from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ export const MOBILE = {
4
+ paytech: {
5
+ name: "PayTech",
6
+ methods: "Wave, Orange Money, Free Money, Wizall, carte bancaire (Visa, Mastercard)",
7
+ currencies: ["xof", "eur", "usd", "cad", "gbp", "mad"],
8
+ test: "pas de simulation : chaque paiement test débite vraiment 100 à 150 FCFA (quel que soit le panier) sur le Wave / Orange Money du testeur ; l'argent arrive sur le solde PayTech de la boutique (moins les frais)",
9
+ needs: "compte PayTech + 2 clés (API_KEY, API_SECRET) ; mode prod après activation du compte",
10
+ refund: "bouton Remboursement de l'admin : l'argent repart vers le client (remboursement total uniquement)",
11
+ site: "paytech.sn",
12
+ },
13
+ paydunya: {
14
+ name: "PayDunya",
15
+ methods: "Wave, Orange Money, Free Money, carte bancaire…",
16
+ currencies: ["xof"],
17
+ test: "sandbox gratuite (aucun vrai paiement), clés de test séparées",
18
+ needs: "compte PayDunya + application + 3 clés (master key, private key, token)",
19
+ refund: "dans le tableau de bord PayDunya (pas d'API), puis enregistré dans l'admin",
20
+ site: "paydunya.com",
21
+ },
22
+ naboopay: {
23
+ name: "NabooPay",
24
+ methods: "Wave, Orange Money (carte annoncée)",
25
+ currencies: ["xof"],
26
+ test: "pas de mode test documenté : tester avec de petits montants réels",
27
+ needs: "compte NabooPay + 1 clé API + secret du webhook (tableau de bord → Paramètres → Intégration)",
28
+ refund: "bouton Remboursement de l'admin : l'argent repart vers le client (total uniquement, Wave et Orange Money)",
29
+ site: "naboopay.com",
30
+ },
31
+ };
32
+ const stop = (v) => {
33
+ if (p.isCancel(v)) {
34
+ p.cancel("Création annulée.");
35
+ process.exit(1);
36
+ }
37
+ return v;
38
+ };
39
+ /** Devises des boutiques pour lesquelles l'agrégateur ne s'affichera pas */
40
+ const unsupported = (id, currencies) => currencies.filter((c) => !MOBILE[id].currencies.includes(c));
41
+ /** Explication affichée avant le choix : critères de chaque agrégateur */
42
+ export function mobileNote(currencies) {
43
+ const lines = Object.keys(MOBILE).flatMap((id) => {
44
+ const m = MOBILE[id];
45
+ const off = unsupported(id, currencies);
46
+ return [
47
+ pc.bold(`${m.name}`) + pc.dim(` (${m.site})`),
48
+ ` Moyens : ${m.methods}`,
49
+ ` Devises : ${m.currencies.map((c) => c.toUpperCase()).join(", ")}` + (off.length ? pc.yellow(` → pas pour vos boutiques en ${off.join(", ").toUpperCase()}`) : ""),
50
+ ` Test : ${m.test}`,
51
+ ` Il faut : ${m.needs}`,
52
+ ` Rembourser : ${m.refund}`,
53
+ "",
54
+ ];
55
+ });
56
+ return [
57
+ "Le client choisit « Wave, Orange Money… », paie sur la page sécurisée de l'agrégateur,",
58
+ "puis revient sur la boutique ; le montant est toujours calculé par le backend.",
59
+ "Confirmation automatique (même si le client ferme la page) : backend en ligne en HTTPS.",
60
+ "",
61
+ ...lines,
62
+ "Plusieurs possibles (le client voit un choix par agrégateur). Clés : maintenant (saisie masquée)",
63
+ "ou plus tard dans .env, puis « docker compose restart backend ».",
64
+ ].join("\n");
65
+ }
66
+ /** Questions : agrégateurs, clés (facultatives maintenant), mode test / prod */
67
+ export async function askMobile(a, currencies) {
68
+ if (a.mobile)
69
+ return a;
70
+ p.note(mobileNote(currencies), "Paiement mobile : Wave, Orange Money, carte (Afrique de l'Ouest)");
71
+ const mobile = stop(await p.multiselect({
72
+ message: "Agrégateurs de paiement mobile (espace = cocher, Entrée sans rien = aucun)",
73
+ options: Object.keys(MOBILE).map((id) => {
74
+ const off = unsupported(id, currencies);
75
+ return {
76
+ value: id,
77
+ label: MOBILE[id].name,
78
+ hint: `${MOBILE[id].currencies.length > 1 ? "XOF, EUR, USD…" : "XOF uniquement"}${off.length ? ` · inactif en ${off.join(", ").toUpperCase()}` : ""}`,
79
+ };
80
+ }),
81
+ required: false,
82
+ }));
83
+ for (const id of mobile) {
84
+ const off = unsupported(id, currencies);
85
+ if (off.length === currencies.length) {
86
+ p.log.warn(`${MOBILE[id].name} n'acceptera aucune de vos boutiques (${currencies.join(", ").toUpperCase()}) : il faut une boutique en XOF.`);
87
+ }
88
+ }
89
+ const out = { mobile };
90
+ const secret = async (message) => stop(await p.password({ message: `${message} (vide = plus tard dans .env)` })) || undefined;
91
+ const env = async (name, test) => stop(await p.select({
92
+ message: `${name} : mode`,
93
+ options: [
94
+ { value: "test", label: "test", hint: test },
95
+ { value: "prod", label: "production", hint: "vrais paiements, compte activé" },
96
+ ],
97
+ initialValue: "test",
98
+ }));
99
+ if (mobile.includes("paytech")) {
100
+ out.paytechKey = await secret("PayTech : API_KEY");
101
+ out.paytechSecret = out.paytechKey ? await secret("PayTech : API_SECRET") : undefined;
102
+ out.paytechEnv = await env("PayTech", "vrais débits de 100 à 150 FCFA par paiement test, reçus sur le solde PayTech");
103
+ }
104
+ if (mobile.includes("paydunya")) {
105
+ out.paydunyaMaster = await secret("PayDunya : clé principale (master key)");
106
+ out.paydunyaPrivate = out.paydunyaMaster ? await secret("PayDunya : clé privée") : undefined;
107
+ out.paydunyaToken = out.paydunyaMaster ? await secret("PayDunya : token") : undefined;
108
+ out.paydunyaEnv = await env("PayDunya", "sandbox, aucun vrai paiement");
109
+ }
110
+ if (mobile.includes("naboopay")) {
111
+ out.naboopayKey = await secret("NabooPay : clé API");
112
+ p.log.info("NabooPay : le secret du webhook se crée après la mise en ligne (tableau de bord → Paramètres → Intégration) ; à mettre dans NABOOPAY_WEBHOOK_SECRET.");
113
+ }
114
+ return out;
115
+ }
116
+ /** Ligne du récapitulatif */
117
+ export function mobileSummary(a) {
118
+ if (!a.mobile?.length)
119
+ return "Wave / Orange Money : non (possible plus tard : clés dans .env, voir README)";
120
+ const one = (id) => {
121
+ const keys = id === "paytech" ? a.paytechKey && a.paytechSecret : id === "paydunya" ? a.paydunyaMaster && a.paydunyaPrivate && a.paydunyaToken : a.naboopayKey;
122
+ const mode = id === "paytech" ? a.paytechEnv : id === "paydunya" ? a.paydunyaEnv : undefined;
123
+ return `${MOBILE[id].name}${mode ? ` (${mode})` : ""}${keys ? "" : " — clés à mettre dans .env"}`;
124
+ };
125
+ return `Wave / Orange Money : ${a.mobile.map(one).join(", ")}`;
126
+ }
127
+ /** E-mails : Resend, SMTP (Gmail…) ou aucun */
128
+ export async function askMail() {
129
+ const kind = stop(await p.select({
130
+ message: "E-mails aux clients (confirmation, expédition, mot de passe oublié…)",
131
+ options: [
132
+ { value: "smtp", label: "SMTP (Gmail, OVH, Brevo…)", hint: "Gmail : mot de passe d'application, ~500 e-mails/jour" },
133
+ { value: "resend", label: "Resend", hint: "clé re_…, domaine vérifié ; 3 000 e-mails/mois gratuits" },
134
+ { value: "none", label: "Aucun pour l'instant", hint: "la boutique marche, sans e-mails" },
135
+ ],
136
+ initialValue: "smtp",
137
+ }));
138
+ if (kind !== "smtp")
139
+ return { kind };
140
+ p.note([
141
+ "Gmail : Compte Google → Sécurité → Validation en deux étapes (activée)",
142
+ "→ Mots de passe des applications → créer (16 caractères). Jamais le mot de passe du compte.",
143
+ "Les e-mails partent de l'adresse Gmail, avec le nom de la boutique ; les réponses vont au marchand.",
144
+ ].join("\n"), "SMTP");
145
+ const host = stop(await p.text({ message: "Serveur SMTP", initialValue: "smtp.gmail.com" }));
146
+ const port = Number(stop(await p.text({ message: "Port (465 = SSL, 587 = STARTTLS)", initialValue: "465", validate: (v) => (/^\d+$/.test(v ?? "") ? undefined : "un nombre") })));
147
+ const user = stop(await p.text({ message: "Identifiant (adresse e-mail du compte)", placeholder: "boutique@gmail.com" }));
148
+ const pass = stop(await p.password({ message: "Mot de passe d'application (vide = plus tard dans .env)" })) || undefined;
149
+ return { kind, smtp: { smtpHost: host.trim(), smtpPort: port, smtpUser: user.trim() || undefined, smtpPass: pass } };
150
+ }
@@ -98,8 +98,8 @@
98
98
  <div class="screen">
99
99
  <div class="num">3</div>
100
100
  <div>
101
- <h2>Campaigns <small>texts and images of each store</small></h2>
102
- <div class="where">Menu: Campaigns</div>
101
+ <h2>My stores <small>texts and images of each store</small></h2>
102
+ <div class="where">Menu: My stores</div>
103
103
  <ol>
104
104
  <li>Pick the store (Halloween, Black Friday, Christmas…) and click <b>Edit</b>.</li>
105
105
  <li>Change the title, text, button, hero image, logo and the offer dates.</li>
@@ -98,8 +98,8 @@
98
98
  <div class="screen">
99
99
  <div class="num">3</div>
100
100
  <div>
101
- <h2>Campagnes <small>textes et images de chaque boutique</small></h2>
102
- <div class="where">Menu : Campagnes</div>
101
+ <h2>Mes boutiques <small>site en ligne ou fermé, textes et images</small></h2>
102
+ <div class="where">Menu : Mes boutiques</div>
103
103
  <ol>
104
104
  <li>Choisissez la boutique (Halloween, Black Friday, Noël…) et cliquez sur <b>Modifier</b>.</li>
105
105
  <li>Changez le titre, le texte, le bouton, l'image principale, le logo et les dates de l'offre.</li>