@mohasinac/appkit 4.53.1 → 4.54.0-next.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 (220) hide show
  1. package/dist/.build-stamp.json +5 -5
  2. package/dist/_internal/client/theme/ThemeProvider.js +15 -1
  3. package/dist/_internal/server/features/auth/capabilities.js +7 -5
  4. package/dist/_internal/server/features/categories/price-guidance-resolve.d.ts +66 -0
  5. package/dist/_internal/server/features/categories/price-guidance-resolve.js +138 -0
  6. package/dist/_internal/server/features/checkout/supplier-identity.js +28 -6
  7. package/dist/_internal/server/features/price-index/market.d.ts +57 -0
  8. package/dist/_internal/server/features/price-index/market.js +120 -0
  9. package/dist/_internal/server/features/products/list-public.d.ts +12 -0
  10. package/dist/_internal/server/features/products/list-public.js +10 -1
  11. package/dist/_internal/server/features/products/listing-tab-counts.d.ts +25 -1
  12. package/dist/_internal/server/features/products/listing-tab-counts.js +184 -10
  13. package/dist/_internal/server/features/search/index-build/sources.js +43 -2
  14. package/dist/_internal/server/features/stores/tabs.d.ts +7 -2
  15. package/dist/_internal/server/features/stores/tabs.js +5 -3
  16. package/dist/_internal/server/jobs/core/jobRunners.js +0 -10
  17. package/dist/_internal/server/jobs/core/onOrderCreate.js +10 -104
  18. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  19. package/dist/_internal/shared/actions/action-registry.js +128 -0
  20. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
  21. package/dist/_internal/shared/features/site-settings/supplier-of-record.d.ts +56 -0
  22. package/dist/_internal/shared/features/site-settings/supplier-of-record.js +57 -0
  23. package/dist/_internal/shared/listing-types/tiers.d.ts +94 -0
  24. package/dist/_internal/shared/listing-types/tiers.js +150 -0
  25. package/dist/_internal/shared/search/match.js +28 -2
  26. package/dist/cli/index.js +0 -2
  27. package/dist/client.d.ts +21 -2
  28. package/dist/client.js +23 -4
  29. package/dist/constants/api-endpoints.d.ts +28 -0
  30. package/dist/constants/api-endpoints.js +14 -0
  31. package/dist/features/account/components/UserOrdersView.js +26 -7
  32. package/dist/features/addresses/utils/postal-autofill.d.ts +39 -0
  33. package/dist/features/addresses/utils/postal-autofill.js +54 -0
  34. package/dist/features/admin/components/AdminStoreEditorView.js +73 -34
  35. package/dist/features/admin/components/OwnerQuickActions.d.ts +29 -0
  36. package/dist/features/admin/components/OwnerQuickActions.js +142 -0
  37. package/dist/features/admin/schemas/admin-editor-forms.d.ts +1 -1
  38. package/dist/features/admin/schemas/admin-editor-forms.js +4 -1
  39. package/dist/features/auctions/components/AuctionDetailPageView.js +8 -7
  40. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -1
  41. package/dist/features/auctions/components/MarketplaceAuctionCard.js +9 -2
  42. package/dist/features/auth/permissions/constants.d.ts +29 -2
  43. package/dist/features/auth/permissions/constants.js +68 -1
  44. package/dist/features/blog/components/BlogFeaturedCard.d.ts +11 -1
  45. package/dist/features/blog/components/BlogFeaturedCard.js +9 -2
  46. package/dist/features/cart/components/CartDrawer.js +3 -2
  47. package/dist/features/cart/components/CartGroupLineRow.js +2 -1
  48. package/dist/features/cart/hooks/useAddToCart.js +10 -5
  49. package/dist/features/cart/utils/pending-ops.d.ts +11 -1
  50. package/dist/features/cart/utils/pending-ops.js +14 -2
  51. package/dist/features/categories/api/[id]/price-guidance/route.js +1 -24
  52. package/dist/features/categories/components/BrandDetailPageView.js +15 -6
  53. package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -1
  54. package/dist/features/categories/components/BrandDetailTabs.js +18 -3
  55. package/dist/features/categories/components/BundleCollage.js +3 -2
  56. package/dist/features/categories/components/BundleItemsPicker.js +3 -4
  57. package/dist/features/categories/components/CategoryDetailPageView.js +19 -8
  58. package/dist/features/categories/components/CategoryDetailTabs.d.ts +7 -1
  59. package/dist/features/categories/components/CategoryDetailTabs.js +29 -7
  60. package/dist/features/categories/components/CategoryGrid.d.ts +11 -1
  61. package/dist/features/categories/components/CategoryGrid.js +9 -2
  62. package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +10 -5
  63. package/dist/features/checkout/actions/checkout-value-otp-actions.js +2 -88
  64. package/dist/features/classified/components/ClassifiedDetailPageView.js +10 -1
  65. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +10 -1
  66. package/dist/features/events/components/EventCard.d.ts +11 -1
  67. package/dist/features/events/components/EventCard.js +9 -2
  68. package/dist/features/layout/AppLayoutShell.js +2 -1
  69. package/dist/features/layout/BottomActions.d.ts +1 -0
  70. package/dist/features/layout/BottomActions.js +330 -0
  71. package/dist/features/layout/BottomChrome.d.ts +45 -0
  72. package/dist/features/layout/BottomChrome.js +5 -17
  73. package/dist/features/layout/SideActionDock.js +36 -13
  74. package/dist/features/layout/index.d.ts +2 -7
  75. package/dist/features/layout/index.js +7 -7
  76. package/dist/features/layout/useIsDesktop.d.ts +36 -0
  77. package/dist/features/layout/useIsDesktop.js +48 -0
  78. package/dist/features/live/components/LiveItemDetailPageView.js +10 -1
  79. package/dist/features/media/messages/en.json +67 -67
  80. package/dist/features/orders/components/YourPurchaseStrip.d.ts +24 -0
  81. package/dist/features/orders/components/YourPurchaseStrip.js +77 -0
  82. package/dist/features/orders/components/index.d.ts +2 -1
  83. package/dist/features/orders/components/index.js +1 -1
  84. package/dist/features/orders/utils/order-announcement-message.d.ts +18 -0
  85. package/dist/features/orders/utils/order-announcement-message.js +16 -0
  86. package/dist/features/orders/utils/payment-proof-message.d.ts +20 -0
  87. package/dist/features/orders/utils/payment-proof-message.js +17 -0
  88. package/dist/features/parts/schemas/firestore.d.ts +227 -0
  89. package/dist/features/parts/schemas/firestore.js +6 -6
  90. package/dist/features/pre-orders/components/MarketplacePreorderCard.d.ts +11 -1
  91. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +10 -3
  92. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +8 -7
  93. package/dist/features/price-index/components/PriceIndexToolView.d.ts +21 -0
  94. package/dist/features/price-index/components/PriceIndexToolView.js +230 -27
  95. package/dist/features/price-index/lookup-target.d.ts +48 -0
  96. package/dist/features/price-index/lookup-target.js +107 -0
  97. package/dist/features/products/api/[id]/route.js +8 -1
  98. package/dist/features/products/api/route.js +16 -4
  99. package/dist/features/products/components/AvailabilityScopeHint.d.ts +22 -0
  100. package/dist/features/products/components/AvailabilityScopeHint.js +27 -0
  101. package/dist/features/products/components/CompareOverlay.d.ts +2 -1
  102. package/dist/features/products/components/CompareOverlay.js +2 -1
  103. package/dist/features/products/components/GroupSettingsPanel.js +2 -1
  104. package/dist/features/products/components/InteractiveProductCard.d.ts +7 -1
  105. package/dist/features/products/components/InteractiveProductCard.js +16 -4
  106. package/dist/features/products/components/MarketplaceBundleCard.d.ts +11 -1
  107. package/dist/features/products/components/MarketplaceBundleCard.js +9 -2
  108. package/dist/features/products/components/MarketplacePrizeDrawCard.d.ts +11 -1
  109. package/dist/features/products/components/MarketplacePrizeDrawCard.js +9 -2
  110. package/dist/features/products/components/ProductDetailPageView.js +34 -4
  111. package/dist/features/products/components/ProductGrid.d.ts +14 -1
  112. package/dist/features/products/components/ProductGrid.js +22 -5
  113. package/dist/features/products/components/ProductsIndexListing.js +2 -2
  114. package/dist/features/products/components/SublistingCarouselSection.js +2 -1
  115. package/dist/features/products/repository/products.repository.d.ts +1 -0
  116. package/dist/features/products/repository/products.repository.js +5 -1
  117. package/dist/features/products/utils/group-member-image.d.ts +9 -2
  118. package/dist/features/products/utils/group-member-image.js +2 -1
  119. package/dist/features/reviews/api/[id]/route.js +83 -23
  120. package/dist/features/reviews/components/PurchaseReviewAffordance.d.ts +6 -0
  121. package/dist/features/reviews/components/PurchaseReviewAffordance.js +42 -0
  122. package/dist/features/reviews/components/ReviewFormDrawer.d.ts +34 -0
  123. package/dist/features/reviews/components/ReviewFormDrawer.js +127 -0
  124. package/dist/features/reviews/components/index.d.ts +4 -0
  125. package/dist/features/reviews/components/index.js +2 -0
  126. package/dist/features/seller/actions/seller-actions.d.ts +1 -1
  127. package/dist/features/seller/actions/seller-actions.js +49 -3
  128. package/dist/features/seller/components/SellerPayoutSettingsView.js +30 -2
  129. package/dist/features/seller/components/SellerProductShell.d.ts +17 -1
  130. package/dist/features/seller/components/SellerProductsView.js +3 -2
  131. package/dist/features/seller/hooks/useStoreListingEntitlements.d.ts +8 -0
  132. package/dist/features/seller/hooks/useStoreListingEntitlements.js +70 -0
  133. package/dist/features/stores/components/InteractiveStoreCard.d.ts +11 -1
  134. package/dist/features/stores/components/InteractiveStoreCard.js +9 -2
  135. package/dist/features/stores/components/StoreAuctionsListing.js +2 -1
  136. package/dist/features/stores/components/index.d.ts +0 -2
  137. package/dist/features/stores/components/index.js +0 -1
  138. package/dist/features/stores/schemas/index.d.ts +1 -1
  139. package/dist/features/stores/schemas/index.js +10 -1
  140. package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +114 -2
  141. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +415 -0
  142. package/dist/index.d.ts +8 -27
  143. package/dist/index.js +14 -35
  144. package/dist/next/routing/route-map.d.ts +2 -0
  145. package/dist/next/routing/route-map.js +9 -0
  146. package/dist/providers/shipping-shiprocket/index.d.ts +98 -0
  147. package/dist/providers/shipping-shiprocket/index.js +353 -0
  148. package/dist/schemas/registry.d.ts +2 -2
  149. package/dist/security/csp.d.ts +16 -1
  150. package/dist/security/csp.js +5 -3
  151. package/dist/seed/_helpers/category-forest.js +27 -0
  152. package/dist/seed/_helpers/category-parts-glossary-map.js +1816 -86
  153. package/dist/seed/_helpers/category-parts.d.ts +1 -1
  154. package/dist/seed/_helpers/category-parts.js +13412 -3020
  155. package/dist/seed/categories-seed-data.js +48 -6
  156. package/dist/seed/homepage-sections-seed-data.js +41 -28
  157. package/dist/seed/model-composition-seed-data.js +6 -8
  158. package/dist/seed/part-glossary/chunk0.d.ts +9 -0
  159. package/dist/seed/part-glossary/chunk0.js +35044 -0
  160. package/dist/seed/part-glossary/chunk1.d.ts +9 -0
  161. package/dist/seed/part-glossary/chunk1.js +39955 -0
  162. package/dist/seed/part-glossary/chunk2.d.ts +9 -0
  163. package/dist/seed/part-glossary/chunk2.js +50335 -0
  164. package/dist/seed/part-glossary/chunk3.d.ts +9 -0
  165. package/dist/seed/part-glossary/chunk3.js +52386 -0
  166. package/dist/seed/part-glossary/chunk4.d.ts +9 -0
  167. package/dist/seed/part-glossary/chunk4.js +50549 -0
  168. package/dist/seed/part-glossary-seed-data.d.ts +1 -1
  169. package/dist/seed/part-glossary-seed-data.js +23 -47483
  170. package/dist/seed/products-beyblade-x-seed-data.js +36 -1
  171. package/dist/server.d.ts +6 -8
  172. package/dist/server.js +16 -20
  173. package/dist/styles.css +516 -358
  174. package/dist/tailwind-utilities.css +1 -1
  175. package/dist/tokens/themes/index.d.ts +3 -1
  176. package/dist/tokens/themes/index.js +11 -1
  177. package/dist/tokens/themes/technical-dark.d.ts +30 -0
  178. package/dist/tokens/themes/technical-dark.js +103 -0
  179. package/dist/tokens/themes/technical-light.d.ts +22 -0
  180. package/dist/tokens/themes/technical-light.js +89 -0
  181. package/dist/tokens/tokens.css +155 -0
  182. package/dist/ui/components/GatedPrice.js +30 -3
  183. package/dist/ui/components/GatedPrice.style.css +22 -19
  184. package/dist/ui/components/QuickCreateModal.style.css +78 -78
  185. package/dist/ui/forms/FormShell.style.css +260 -260
  186. package/package.json +1 -1
  187. package/scripts/seed-cli.mjs +133 -27
  188. package/dist/_internal/server/jobs/core/whatsappCatalogImport.d.ts +0 -27
  189. package/dist/_internal/server/jobs/core/whatsappCatalogImport.js +0 -172
  190. package/dist/_internal/server/jobs/core/whatsappNotify.d.ts +0 -35
  191. package/dist/_internal/server/jobs/core/whatsappNotify.js +0 -140
  192. package/dist/features/admin/components/AdminWhatsAppGuideView.d.ts +0 -2
  193. package/dist/features/admin/components/AdminWhatsAppGuideView.js +0 -35
  194. package/dist/features/orders/components/MarketplaceOrderCard.d.ts +0 -36
  195. package/dist/features/orders/components/MarketplaceOrderCard.js +0 -57
  196. package/dist/features/stores/components/StoreWhatsAppGuideView.d.ts +0 -3
  197. package/dist/features/stores/components/StoreWhatsAppGuideView.js +0 -12
  198. package/dist/features/whatsapp-bot/api/send-status/route.d.ts +0 -12
  199. package/dist/features/whatsapp-bot/api/send-status/route.js +0 -38
  200. package/dist/features/whatsapp-bot/api/webhook/route.d.ts +0 -12
  201. package/dist/features/whatsapp-bot/api/webhook/route.js +0 -43
  202. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.d.ts +0 -6
  203. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +0 -270
  204. package/dist/features/whatsapp-bot/components/WhatsAppChatButton.d.ts +0 -9
  205. package/dist/features/whatsapp-bot/components/WhatsAppChatButton.js +0 -7
  206. package/dist/features/whatsapp-bot/components/index.d.ts +0 -3
  207. package/dist/features/whatsapp-bot/components/index.js +0 -2
  208. package/dist/features/whatsapp-bot/helpers/payment-proof-message.d.ts +0 -14
  209. package/dist/features/whatsapp-bot/helpers/payment-proof-message.js +0 -17
  210. package/dist/features/whatsapp-bot/helpers/whatsapp.d.ts +0 -52
  211. package/dist/features/whatsapp-bot/helpers/whatsapp.js +0 -283
  212. package/dist/features/whatsapp-bot/index.d.ts +0 -3
  213. package/dist/features/whatsapp-bot/index.js +0 -3
  214. package/dist/features/whatsapp-bot/manifest.d.ts +0 -2
  215. package/dist/features/whatsapp-bot/manifest.js +0 -13
  216. package/dist/features/whatsapp-bot/messages/en.json +0 -7
  217. package/dist/features/whatsapp-bot/server.d.ts +0 -7
  218. package/dist/features/whatsapp-bot/server.js +0 -7
  219. package/dist/features/whatsapp-bot/types/index.d.ts +0 -126
  220. package/dist/features/whatsapp-bot/types/index.js +0 -1
@@ -387,6 +387,104 @@ const COLLECTION_MAP = {
387
387
  * A root with no mapping yields NO franchise (living collectibles are not an
388
388
  * IP) — see `FRANCHISE_BY_CATEGORY_ROOT`.
389
389
  */
390
+ /**
391
+ * The real, sellable catalogue — everything else in the product seed is demo
392
+ * furniture and is DROPPED HERE, before anything is written.
393
+ *
394
+ * ── 🛑 WHY A FILTER AND NOT A DELETION ────────────────────────────────────
395
+ *
396
+ * The fixtures stay in their files on purpose: they are the only coverage
397
+ * several features have (every `preOrderProductionStatus` value, a sold-out
398
+ * reservation, a depleted digital-code pool, an ended-unsold auction), and
399
+ * the tester checklist deep-links to them by id. Deleting them would silently
400
+ * remove that coverage. Not LOADING them is the part that was wanted.
401
+ *
402
+ * ── 🛑 WHY THIS HAD TO EXIST ──────────────────────────────────────────────
403
+ *
404
+ * `appkit-seed load` writes every registered row and `purge` deletes
405
+ * `seedPurpose: "test"` afterwards — so the demo listings came BACK on every
406
+ * reseed, and anyone who ran `load` to fix imagery silently republished 76
407
+ * fake products to the live storefront. That happened twice in one session.
408
+ * The owner's instruction was "delete them from the seed itself and make sure
409
+ * they do not return"; this is the point where that can be guaranteed, since
410
+ * every write path funnels through here.
411
+ *
412
+ * ── The discriminator is the STORE, not the listing type ──────────────────
413
+ *
414
+ * `store-beyblade-arena` is the fictional demo seller; `store-letitrip-
415
+ * official` is the real one. Keying on the store rather than on a per-file
416
+ * allow-list means a fixture added to ANY product seed file later is excluded
417
+ * automatically, which is the failure mode this is replacing — a list that
418
+ * has to be remembered is a list that drifts.
419
+ *
420
+ * Set `SEED_INCLUDE_DEMO=1` to load them anyway, for local demo work.
421
+ */
422
+ const REAL_STORE_IDS = new Set(["store-letitrip-official"]);
423
+
424
+ function realListingsOnly(products) {
425
+ if (process.env.SEED_INCLUDE_DEMO === "1") {
426
+ console.log("▌ SEED_INCLUDE_DEMO=1 — demo listings INCLUDED");
427
+ return products;
428
+ }
429
+ /*
430
+ * 🛑 TWO rules, because the store alone is not enough. Six prize-draw
431
+ * fixtures live under the REAL store and are still demo furniture — their
432
+ * imagery is a 460-byte generated colour tile from /images/seed-tiles/,
433
+ * which is the giveaway: a listing nobody photographed is a listing nobody
434
+ * intends to sell. Measured before writing this: every one of the six, and
435
+ * nothing real, matches.
436
+ */
437
+ const isPlaceholder = (p) => {
438
+ const urls = [p?.mainImage, ...(Array.isArray(p?.images) ? p.images : [])]
439
+ .map((v) => (typeof v === "string" ? v : v?.url))
440
+ .filter(Boolean);
441
+ return urls.length > 0 && urls.every((u) => String(u).includes("/images/seed-tiles/"));
442
+ };
443
+ const kept = products.filter((p) => REAL_STORE_IDS.has(p?.storeId) && !isPlaceholder(p));
444
+ const dropped = products.length - kept.length;
445
+ if (dropped > 0) {
446
+ console.log(` • ${dropped} demo listing(s) not loaded (set SEED_INCLUDE_DEMO=1 to include)`);
447
+ }
448
+ return kept;
449
+ }
450
+
451
+ /**
452
+ * Drop bundle categories whose members are not in the loaded catalogue.
453
+ *
454
+ * 🛑 A bundle is ALL-OR-NOTHING — `canAddToCart` buys every member together —
455
+ * so a bundle missing even one member is unsellable, and one missing ALL of
456
+ * them is a card that opens an empty page. Measured right after the demo
457
+ * listings stopped loading: of the five seeded bundles, three had zero
458
+ * surviving members and the other two had 1-of-4 and 2-of-3. All five were
459
+ * still `isActive: true`, and all five rendered on the HOMEPAGE as collage
460
+ * cards built from `display.mosaicImages` — which are baked at seed time from
461
+ * the members' own imagery, so they kept showing pictures of products that no
462
+ * longer exist. That is 92 placeholder-tile references on the homepage.
463
+ *
464
+ * Derived rather than hand-maintained for the same reason as the product
465
+ * filter: a bundle that becomes valid later starts loading again on its own,
466
+ * and one that breaks stops, with nobody having to remember either.
467
+ *
468
+ * Only `categoryType: "bundle"` rows are considered. Every other category is
469
+ * taxonomy and is never touched — rebuilding the tree is expensive and is
470
+ * explicitly not what this is for.
471
+ */
472
+ function loadableBundlesOnly(categories, loadedProducts) {
473
+ const live = new Set(loadedProducts.map((p) => p?.id).filter(Boolean));
474
+ let dropped = 0;
475
+ const kept = categories.filter((c) => {
476
+ if (c?.categoryType !== "bundle") return true;
477
+ const ids = Array.isArray(c.bundleProductIds) ? c.bundleProductIds : [];
478
+ const ok = ids.length > 0 && ids.every((id) => live.has(id));
479
+ if (!ok) dropped++;
480
+ return ok;
481
+ });
482
+ if (dropped > 0) {
483
+ console.log(` • ${dropped} bundle(s) not loaded — members missing from the catalogue`);
484
+ }
485
+ return kept;
486
+ }
487
+
390
488
  function withFranchise(products) {
391
489
  let stamped = 0;
392
490
  const out = products.map((p) => {
@@ -469,32 +567,13 @@ function withDerivedCategoryFields(products) {
469
567
  return out;
470
568
  }
471
569
 
472
- const SEED_DATA_MAP = {
473
- users: usersSeedData,
474
- /*
475
- * store-extensions. COLLECTION_MAP was only HALF the wiring: load() reads
476
- * SEED_DATA_MAP and short-circuits on an empty array, so adding the ten
477
- * collection names alone produced "no seed data, skipping" for all ten.
478
- * Same shape as the art/stickers note further down -- added everywhere
479
- * except the map that actually runs.
480
- */
481
- payoutMethods: payoutMethodsSeedData,
482
- shippingConfigs: shippingConfigsSeedData,
483
- analyticsCards: analyticsCardsSeedData,
484
- analyticsAlerts: analyticsAlertsSeedData,
485
- storeCategories: storeCategoriesSeedData,
486
- listingTemplates: listingTemplatesSeedData,
487
- moderationQueue: moderationQueueSeedData,
488
- reports: reportsSeedData,
489
- itemRequests: itemRequestsSeedData,
490
- storeGoogleConfig: storeGoogleConfigSeedData,
491
- addresses: addressesSeedData,
492
- storeAddresses: storeAddressesSeedData,
493
- // SB-UNI-C: brands were merged into categories (categoryType:"brand") — no
494
- // standalone brands collection/seed exists anymore.
495
- categories: categoriesSeedData,
496
- stores: storesSeedData,
497
- products: withFranchise(withDerivedCategoryFields([
570
+ /**
571
+ * The real catalogue, computed ONCE so the bundle filter and the product
572
+ * loader cannot disagree about what "loaded" means. Deriving it twice is
573
+ * Root Cause #75's shape — two copies of one rule, one of which learns about
574
+ * a change and the other does not.
575
+ */
576
+ const REAL_PRODUCTS = realListingsOnly([
498
577
  ...(productsStandardSeedData || []),
499
578
  // Real, wiki-verified Beyblade X listings under store-letitrip-official —
500
579
  // added 2026-10-19 for PhonePe merchant registration, which requires at
@@ -536,7 +615,34 @@ const SEED_DATA_MAP = {
536
615
  * what should happen to a leaf that now has BOTH a real listing and a
537
616
  * generated one.
538
617
  */
539
- ])),
618
+ ]);
619
+
620
+ const SEED_DATA_MAP = {
621
+ users: usersSeedData,
622
+ /*
623
+ * store-extensions. COLLECTION_MAP was only HALF the wiring: load() reads
624
+ * SEED_DATA_MAP and short-circuits on an empty array, so adding the ten
625
+ * collection names alone produced "no seed data, skipping" for all ten.
626
+ * Same shape as the art/stickers note further down -- added everywhere
627
+ * except the map that actually runs.
628
+ */
629
+ payoutMethods: payoutMethodsSeedData,
630
+ shippingConfigs: shippingConfigsSeedData,
631
+ analyticsCards: analyticsCardsSeedData,
632
+ analyticsAlerts: analyticsAlertsSeedData,
633
+ storeCategories: storeCategoriesSeedData,
634
+ listingTemplates: listingTemplatesSeedData,
635
+ moderationQueue: moderationQueueSeedData,
636
+ reports: reportsSeedData,
637
+ itemRequests: itemRequestsSeedData,
638
+ storeGoogleConfig: storeGoogleConfigSeedData,
639
+ addresses: addressesSeedData,
640
+ storeAddresses: storeAddressesSeedData,
641
+ // SB-UNI-C: brands were merged into categories (categoryType:"brand") — no
642
+ // standalone brands collection/seed exists anymore.
643
+ categories: loadableBundlesOnly(categoriesSeedData, REAL_PRODUCTS),
644
+ stores: storesSeedData,
645
+ products: withFranchise(withDerivedCategoryFields(REAL_PRODUCTS)),
540
646
  orders: ordersSeedData,
541
647
  reviews: reviewsSeedData,
542
648
  bids: bidsSeedData,
@@ -1,27 +0,0 @@
1
- /**
2
- * Core: async WhatsApp Business Catalog import (`whatsappCatalogImport` job).
3
- *
4
- * `POST /api/store/whatsapp-settings/catalog-import` used to do this inline:
5
- * one Meta fetch capped at `limit=250`, then a sequential per-item loop doing
6
- * up to 2 `findBySlug` reads plus 1 `create` write EACH — a real 10s-timeout
7
- * risk on Vercel Hobby (CLAUDE.md Rule #6) well before the cap was reached.
8
- * It also declared `paging.next` in its response type and never followed it,
9
- * so a catalog larger than 250 items silently imported only the first page.
10
- *
11
- * Running here instead buys the Function's 300s budget, which is what makes
12
- * following `paging.next` safe rather than making the timeout worse.
13
- *
14
- * Two efficiency changes vs. the old route:
15
- * - Existing-slug detection is ONE `findByStore` read into a Set, not 2 reads
16
- * per item. A seller's WA catalog is populated by pushing their own store's
17
- * products, so store-scoped lookup is equivalent to the old global
18
- * `findBySlug` here while costing a single query.
19
- * - That Set is updated as we create, so duplicate rows within one Meta
20
- * response can't produce two products.
21
- */
22
- import type { JobContext } from "../runtime/types";
23
- import type { JobRunResult } from "./jobRunners";
24
- export interface WhatsAppCatalogImportPayload {
25
- storeSlug: string;
26
- }
27
- export declare function runWhatsAppCatalogImport(payload: WhatsAppCatalogImportPayload, ctx: JobContext): Promise<JobRunResult>;
@@ -1,172 +0,0 @@
1
- /**
2
- * Core: async WhatsApp Business Catalog import (`whatsappCatalogImport` job).
3
- *
4
- * `POST /api/store/whatsapp-settings/catalog-import` used to do this inline:
5
- * one Meta fetch capped at `limit=250`, then a sequential per-item loop doing
6
- * up to 2 `findBySlug` reads plus 1 `create` write EACH — a real 10s-timeout
7
- * risk on Vercel Hobby (CLAUDE.md Rule #6) well before the cap was reached.
8
- * It also declared `paging.next` in its response type and never followed it,
9
- * so a catalog larger than 250 items silently imported only the first page.
10
- *
11
- * Running here instead buys the Function's 300s budget, which is what makes
12
- * following `paging.next` safe rather than making the timeout worse.
13
- *
14
- * Two efficiency changes vs. the old route:
15
- * - Existing-slug detection is ONE `findByStore` read into a Set, not 2 reads
16
- * per item. A seller's WA catalog is populated by pushing their own store's
17
- * products, so store-scoped lookup is equivalent to the old global
18
- * `findBySlug` here while costing a single query.
19
- * - That Set is updated as we create, so duplicate rows within one Meta
20
- * response can't produce two products.
21
- */
22
- import { normalizeError } from "../../../../errors/normalize.js";
23
- import { storeRepository, productRepository } from "../../../../repositories/index.js";
24
- import { decryptPii } from "../../../../security/index.js";
25
- const META_GRAPH_BASE = "https://graph.facebook.com/v20.0";
26
- const SLUG_PREFIX = "product-";
27
- const PAGE_SIZE = 250;
28
- /**
29
- * Defensive stop. 40 pages x 250 = 10,000 items, far beyond any realistic
30
- * seller catalog — this exists so a malformed/looping `paging.next` from Meta
31
- * can't spin until the Function's 300s ceiling kills the job mid-write.
32
- */
33
- const MAX_PAGES = 40;
34
- /** Meta returns "<amount> <currency>", e.g. "450.00 INR". */
35
- function parseMetaPrice(raw) {
36
- if (!raw)
37
- return 0;
38
- const digits = raw.replace(/[^0-9.]/g, "");
39
- return Math.round((parseFloat(digits) || 0) * 100) / 100;
40
- }
41
- /**
42
- * The LetItRip slug a Meta item claims to correspond to, if any. Items we
43
- * pushed carry it in `description` (see catalog-sync's field mapping) and/or
44
- * `retailer_id`; items authored natively in Meta carry neither.
45
- */
46
- function claimedSlug(item) {
47
- const description = (item.description ?? "").trim();
48
- if (description.startsWith(SLUG_PREFIX))
49
- return description;
50
- const retailerId = (item.retailer_id ?? "").trim();
51
- if (retailerId.startsWith(SLUG_PREFIX))
52
- return retailerId;
53
- return null;
54
- }
55
- export async function runWhatsAppCatalogImport(payload, ctx) {
56
- const { storeSlug } = payload;
57
- const empty = { total: 0, succeeded: 0, skipped: 0, failed: 0 };
58
- const store = await storeRepository.findBySlug(storeSlug);
59
- if (!store) {
60
- return {
61
- summary: empty,
62
- succeeded: [],
63
- skipped: [],
64
- failed: [{ id: storeSlug, reason: "Store not found" }],
65
- };
66
- }
67
- const cfg = store.whatsappConfig;
68
- const token = cfg?.accessToken ? decryptPii(cfg.accessToken) : null;
69
- // Re-checked here even though the route gates on the same conditions — the
70
- // job doc outlives the request, and the seller could disconnect in between.
71
- if (!cfg?.connected || !cfg.catalogId || !token) {
72
- return {
73
- summary: empty,
74
- succeeded: [],
75
- skipped: [],
76
- failed: [{ id: storeSlug, reason: "WhatsApp is not connected for this store" }],
77
- };
78
- }
79
- const existing = await productRepository.findByStore(store.storeSlug);
80
- const knownSlugs = new Set(existing.map((p) => p.slug).filter((s) => Boolean(s)));
81
- const imported = [];
82
- const skipped = [];
83
- const failed = [];
84
- let url = `${META_GRAPH_BASE}/${cfg.catalogId}/products` +
85
- `?fields=name,price,currency,image_url,description,retailer_id&limit=${PAGE_SIZE}`;
86
- let page = 0;
87
- let truncated = false;
88
- while (url) {
89
- if (page >= MAX_PAGES) {
90
- truncated = true;
91
- ctx.logger.warn("whatsappCatalogImport: page cap reached — catalog truncated", {
92
- storeSlug,
93
- maxPages: MAX_PAGES,
94
- importedSoFar: imported.length,
95
- });
96
- break;
97
- }
98
- page++;
99
- const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
100
- if (!res.ok) {
101
- const body = (await res.json().catch(() => ({})));
102
- const reason = body.error?.message ?? res.statusText;
103
- ctx.logger.error("whatsappCatalogImport: Meta fetch failed", null, { storeSlug, page, reason });
104
- failed.push({ id: `page-${page}`, reason: `Meta fetch failed: ${reason}` });
105
- break;
106
- }
107
- const body = (await res.json());
108
- const items = body.data ?? [];
109
- for (const item of items) {
110
- const slug = claimedSlug(item);
111
- if (slug && knownSlugs.has(slug)) {
112
- skipped.push(item.id);
113
- continue;
114
- }
115
- const input = {
116
- title: item.name ?? `WhatsApp Import ${item.id}`,
117
- description: "",
118
- slug: "",
119
- listingType: "standard",
120
- status: "draft",
121
- price: parseMetaPrice(item.price),
122
- currency: "INR",
123
- mainImage: item.image_url ?? "",
124
- images: [],
125
- stockQuantity: 1,
126
- condition: "new",
127
- storeId: store.id,
128
- storeName: store.storeName,
129
- tags: ["whatsapp-import"],
130
- categorySlugs: [],
131
- featured: false,
132
- };
133
- try {
134
- const created = await productRepository.create(input);
135
- // Guard against the same item appearing twice in one Meta response.
136
- if (slug)
137
- knownSlugs.add(slug);
138
- if (created.slug)
139
- knownSlugs.add(created.slug);
140
- imported.push(created.id);
141
- }
142
- catch (err) {
143
- void normalizeError(err);
144
- failed.push({
145
- id: item.id,
146
- reason: err instanceof Error ? err.message : "Product create failed",
147
- });
148
- }
149
- }
150
- url = body.paging?.next;
151
- }
152
- ctx.logger.info("whatsappCatalogImport: complete", {
153
- storeSlug,
154
- pages: page,
155
- imported: imported.length,
156
- skipped: skipped.length,
157
- failed: failed.length,
158
- truncated,
159
- });
160
- return {
161
- summary: {
162
- total: imported.length + skipped.length + failed.length,
163
- succeeded: imported.length,
164
- skipped: skipped.length,
165
- failed: failed.length,
166
- },
167
- succeeded: imported,
168
- skipped,
169
- failed,
170
- data: { imported: imported.length, skipped: skipped.length, pages: page, truncated },
171
- };
172
- }
@@ -1,35 +0,0 @@
1
- /**
2
- * Core: async WhatsApp notification dispatch (`whatsappNotify` job).
3
- *
4
- * `sendNotification()` (appkit/src/features/admin/actions/notification-actions.ts)
5
- * no longer calls the Meta Cloud API synchronously in-request — it enqueues
6
- * this job instead (CLAUDE.md Rule #6 async job primitive) and returns
7
- * immediately. This runner does the actual send, with a bounded retry, and
8
- * writes the outcome back onto `notifications/{notificationId}` so admins
9
- * have visibility beyond the `jobs/{jobId}` doc itself.
10
- *
11
- * Retry note: `sendWhatsAppTemplateMessage`/`sendWhatsAppBusinessMessage`
12
- * return a plain boolean (not a status code), so this runner cannot cleanly
13
- * distinguish a transient 5xx from a terminal 4xx (bad token, unknown
14
- * template) the way a status-code-aware client could. It retries any
15
- * failure up to MAX_ATTEMPTS with a short backoff — an acceptable
16
- * simplification since this runs off the critical path, well inside the
17
- * Firebase Function's 300s budget, and a terminal error just burns two
18
- * extra fast failures before giving up.
19
- */
20
- import type { JobContext } from "../runtime/types";
21
- import type { JobRunResult } from "./jobRunners";
22
- export interface WhatsAppNotifyPayload {
23
- toPhone: string;
24
- title: string;
25
- message: string;
26
- type: string;
27
- /** Approved Meta template name for this notification type, if configured. */
28
- templateName?: string;
29
- /** BCP-47 language code the template was approved in. Defaults to "en". */
30
- templateLanguage?: string;
31
- notificationId: string;
32
- phoneNumberId: string;
33
- accessToken: string;
34
- }
35
- export declare function runWhatsAppNotify(payload: WhatsAppNotifyPayload, ctx: JobContext): Promise<JobRunResult>;
@@ -1,140 +0,0 @@
1
- /**
2
- * Core: async WhatsApp notification dispatch (`whatsappNotify` job).
3
- *
4
- * `sendNotification()` (appkit/src/features/admin/actions/notification-actions.ts)
5
- * no longer calls the Meta Cloud API synchronously in-request — it enqueues
6
- * this job instead (CLAUDE.md Rule #6 async job primitive) and returns
7
- * immediately. This runner does the actual send, with a bounded retry, and
8
- * writes the outcome back onto `notifications/{notificationId}` so admins
9
- * have visibility beyond the `jobs/{jobId}` doc itself.
10
- *
11
- * Retry note: `sendWhatsAppTemplateMessage`/`sendWhatsAppBusinessMessage`
12
- * return a plain boolean (not a status code), so this runner cannot cleanly
13
- * distinguish a transient 5xx from a terminal 4xx (bad token, unknown
14
- * template) the way a status-code-aware client could. It retries any
15
- * failure up to MAX_ATTEMPTS with a short backoff — an acceptable
16
- * simplification since this runs off the critical path, well inside the
17
- * Firebase Function's 300s budget, and a terminal error just burns two
18
- * extra fast failures before giving up.
19
- */
20
- import { normalizeError } from "../../../../errors/normalize.js";
21
- import { notificationRepository } from "../../../../features/admin/repository/notification.repository.js";
22
- import { sendWhatsAppTemplateMessage, sendWhatsAppBusinessMessage, } from "../../../../features/whatsapp-bot/helpers/whatsapp.js";
23
- import { isChannelHealthy, recordChannelOutcome } from "../../notifications/channel-health.js";
24
- import { guardSend, logSuppressedSend } from "../../notifications/send-guard.js";
25
- const MAX_ATTEMPTS = 3;
26
- const RETRY_DELAY_MS = 1500;
27
- function delay(ms) {
28
- return new Promise((resolve) => setTimeout(resolve, ms));
29
- }
30
- export async function runWhatsAppNotify(payload, ctx) {
31
- const { toPhone, title, message, type, templateName, templateLanguage, notificationId, phoneNumberId, accessToken } = payload;
32
- /*
33
- * The messaging guard, applied HERE rather than inside
34
- * `sendWhatsAppBusinessMessage` itself.
35
- *
36
- * 🛑 That placement is deliberate and must not be "improved" by pushing a
37
- * required guard parameter down into the sender. `features/whatsapp-bot/helpers/whatsapp.ts`
38
- * is re-exported from `appkit/src/index.ts` (via `isAdminNumber`), and
39
- * `guardSend` reaches `firebase-admin` through the settings and budget
40
- * repositories. A static import of it from that file would put
41
- * `firebase-admin` in the main entry's import graph, which is precisely the
42
- * Turbopack client-bundle trap in Root Cause #6/#24 — it builds fine locally
43
- * on webpack and fails every page in production.
44
- *
45
- * So the senders stay pure and each of the three real call sites guards
46
- * itself; `scripts/audit-unguarded-send.mjs` enforces that a file calling a
47
- * WhatsApp sender also calls `guardSend`.
48
- *
49
- * Note this job is only ever reached for an email/WhatsApp-ELIGIBLE type —
50
- * `sendNotification` returns before enqueuing otherwise — so this is the
51
- * ceiling and kill-switch check, not the type check.
52
- */
53
- const guard = {
54
- channel: "whatsapp",
55
- feature: type,
56
- audience: "user",
57
- };
58
- const decision = await guardSend(guard);
59
- if (!decision.allow) {
60
- logSuppressedSend(guard, decision);
61
- await notificationRepository
62
- .update(notificationId, { whatsappStatus: "skipped" })
63
- .catch((err) => {
64
- void normalizeError(err);
65
- });
66
- return {
67
- summary: { total: 1, succeeded: 0, skipped: 1, failed: 0 },
68
- succeeded: [],
69
- skipped: [notificationId],
70
- failed: [],
71
- };
72
- }
73
- if (!(await isChannelHealthy("whatsapp"))) {
74
- ctx.logger.warn("whatsappNotify: WhatsApp channel circuit is open — skipping send", { notificationId });
75
- await notificationRepository
76
- .update(notificationId, { whatsappStatus: "skipped" })
77
- .catch((err) => {
78
- void normalizeError(err);
79
- });
80
- return {
81
- summary: { total: 1, succeeded: 0, skipped: 1, failed: 0 },
82
- succeeded: [],
83
- skipped: [notificationId],
84
- failed: [],
85
- };
86
- }
87
- let sent = false;
88
- let lastError;
89
- for (let attempt = 1; attempt <= MAX_ATTEMPTS && !sent; attempt++) {
90
- try {
91
- sent = templateName
92
- ? await sendWhatsAppTemplateMessage({
93
- toPhone,
94
- phoneNumberId,
95
- accessToken,
96
- templateName,
97
- languageCode: templateLanguage ?? "en",
98
- bodyParams: [title, message],
99
- })
100
- : await sendWhatsAppBusinessMessage({
101
- toPhone,
102
- phoneNumberId,
103
- accessToken,
104
- message: `*${title}*\n${message}`,
105
- });
106
- if (!sent)
107
- lastError = "Meta WhatsApp API returned a non-OK response";
108
- }
109
- catch (err) {
110
- void normalizeError(err);
111
- lastError = err instanceof Error ? err.message : String(err);
112
- }
113
- if (!sent && attempt < MAX_ATTEMPTS) {
114
- ctx.logger.warn("whatsappNotify: send attempt failed, retrying", { notificationId, attempt, error: lastError ?? "" });
115
- await delay(RETRY_DELAY_MS * attempt);
116
- }
117
- }
118
- if (!templateName) {
119
- ctx.logger.warn("whatsappNotify: no approved template configured for this notification type — sent as free-form text, which Meta rejects outside the 24h customer-service window", { notificationId });
120
- }
121
- await recordChannelOutcome("whatsapp", sent);
122
- await notificationRepository
123
- .update(notificationId, { whatsappStatus: sent ? "sent" : "failed" })
124
- .catch((err) => {
125
- void normalizeError(err);
126
- ctx.logger.warn("whatsappNotify: failed to write back whatsappStatus onto notification doc", {
127
- notificationId,
128
- error: err instanceof Error ? err.message : String(err),
129
- });
130
- });
131
- if (!sent) {
132
- throw new Error(lastError ?? "WhatsApp send failed after retries");
133
- }
134
- return {
135
- summary: { total: 1, succeeded: 1, skipped: 0, failed: 0 },
136
- succeeded: [notificationId],
137
- skipped: [],
138
- failed: [],
139
- };
140
- }
@@ -1,2 +0,0 @@
1
- import React from "react";
2
- export declare function AdminWhatsAppGuideView(): React.JSX.Element;
@@ -1,35 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Building2, Smartphone, KeyRound, MessageSquare, Power, FlaskConical } from "lucide-react";
3
- import { Alert, Code, Div, Heading, Li, Row, Section, Span, Stack, Table, Tbody, Td, Text, Th, Thead, Tr, Ul } from "../../../ui/index.js";
4
- import { GC } from "../../_guide-cls.js";
5
- export function AdminWhatsAppGuideView() {
6
- return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "xl", children: _jsx(MessageSquare, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "WhatsApp Integration" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Connecting a real Meta WhatsApp Business Cloud API account so the platform-level order-notification addon and admin purchase announcements actually deliver. Do these steps outside the codebase, then paste the resulting values into Site Settings \u2192 WhatsApp." })] }), [
7
- {
8
- Icon: Building2, title: "Meta Business Manager & App",
9
- content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: ["Create or verify a Business account at ", _jsx(Span, { weight: "bold", children: "business.facebook.com" }), ". Complete Business Verification (legal name, address, phone) \u2014 most message-template categories require a verified business before they'll be approved."] }), _jsxs(Li, { children: ["In ", _jsx(Span, { weight: "bold", children: "developers.facebook.com" }), ", create an App and add the ", _jsx(Span, { weight: "bold", children: "WhatsApp" }), " product. This gives you a test phone number for development immediately."] })] })),
10
- },
11
- {
12
- Icon: Smartphone, title: "Register the Real Business Phone Number",
13
- content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsx(Li, { children: "WhatsApp Manager \u2192 Phone Numbers \u2192 Add phone number \u2192 verify via SMS or voice OTP." }), _jsx(Li, { children: "Complete Display Name review \u2014 Meta approves the shown business name before it goes live." })] })),
14
- },
15
- {
16
- Icon: KeyRound, title: "System User & Permanent Access Token",
17
- content: (_jsxs(_Fragment, { children: [_jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: ["Business Settings \u2192 Users \u2192 System Users \u2192 create a System User with ", _jsx(Span, { weight: "bold", children: "Admin" }), " role."] }), _jsxs(Li, { children: ["Assign it the WhatsApp Business Account asset with ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "whatsapp_business_messaging" }), " + ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "whatsapp_business_management" }), " permissions, then generate a token. System User tokens don't expire the way personal User tokens do, so this is the one to use in production."] })] }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)] mt-3", size: "sm", children: ["Copy the token into ", _jsx(Span, { weight: "bold", children: "Site Settings \u2192 WhatsApp \u2192 Cloud API System User Token" }), ", and the ", _jsx(Span, { weight: "bold", children: "Phone Number ID" }), " (WhatsApp Manager \u2192 API Setup) into ", _jsx(Span, { weight: "bold", children: "Site Settings \u2192 WhatsApp \u2192 Phone Number ID" }), "."] })] })),
18
- },
19
- {
20
- Icon: MessageSquare, title: "Message Templates",
21
- content: (_jsxs(_Fragment, { children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)] mb-3", size: "sm", children: "Meta's Cloud API only allows free-form text messages within a 24-hour window after the customer has messaged the business first. Any business-initiated proactive notification \u2014 order placed, shipped, delivered, cancelled, refund initiated \u2014 needs a pre-approved Message Template." }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)] mb-3", size: "sm", children: ["WhatsApp Manager \u2192 Message Templates \u2192 create one template per notification type, category ", _jsx(Span, { weight: "bold", children: "UTILITY" }), " (transactional order updates get cheaper, faster approval than MARKETING):"] }), _jsx(Div, { overflow: "x-auto", children: _jsxs(Table, { size: "sm", children: [_jsx(Thead, { children: _jsxs(Tr, { className: "border-b border-[var(--appkit-color-border)]", children: [_jsx(Th, { align: "left", paddingSide: "pr-md", className: "text-[var(--appkit-color-text)]", padding: "xs-tall", weight: "semibold", children: "Notification type" }), _jsx(Th, { align: "left", className: "text-[var(--appkit-color-text)]", padding: "xs-tall", weight: "semibold", children: "Suggested template name" })] }) }), _jsx(Tbody, { size: "sm", color: "muted", children: [
22
- ["Order placed", "order_placed_update"],
23
- ["Order confirmed", "order_confirmed_update"],
24
- ["Order shipped", "order_shipped_update"],
25
- ["Order delivered", "order_delivered_update"],
26
- ["Order cancelled", "order_cancelled_update"],
27
- ["Refund initiated", "refund_initiated_update"],
28
- ].map(([type, name]) => (_jsxs(Tr, { className: "border-b border-[var(--appkit-color-border)]/50", children: [_jsx(Td, { paddingSide: "pr-md", padding: "xs-tall", children: type }), _jsx(Td, { padding: "xs-tall", children: _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: name }) })] }, type))) })] }) }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)] mt-3", size: "sm", children: ["Give each template two ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "{{1}}" }), "/", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "{{2}}" }), " body variables \u2014 the runner substitutes the notification title and message text in that order. Submit for review; approval usually takes minutes to ~24 hours. Don't go live on a template until its status shows ", _jsx(Span, { weight: "bold", children: "APPROVED" }), "."] }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)] mt-2", size: "sm", children: ["Once each template is approved, copy its exact template name (not the display label you gave it in Meta's UI) into ", _jsx(Span, { weight: "bold", children: "Site Settings \u2192 WhatsApp \u2192 [type] template name" }), ", and set the approved language code (e.g. ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "en" }), ") in ", _jsx(Span, { weight: "bold", children: "Template language code" }), "."] })] })),
29
- },
30
- {
31
- Icon: Power, title: "Enable the Addon & Test",
32
- content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "Site Settings \u2192 Fees \u2192 \"Offer the WhatsApp order-updates addon at checkout\"" }), " \u2014 turn this on once credentials + at least one template are configured. Set the fee amount (default \u20B910)."] }), _jsx(Li, { children: "Place a real test order with the addon checked. Confirm the buyer receives the templated WhatsApp message. Until real credentials + an approved template exist, the async delivery job will fail at the Meta API call with a clear credential/template error \u2014 that's expected, not a code bug." })] })),
33
- },
34
- ].map(({ Icon, title, content }) => (_jsxs(Section, { overflow: "hidden", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "2xl", children: [_jsxs(Row, { className: "border-b border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-2,var(--appkit-color-border))]/20", padding: "inlineLg", align: "center", gap: "3", children: [_jsx(Icon, { className: "w-5 h-5 text-[var(--appkit-color-primary)]" }), _jsx(Heading, { level: 2, size: "base", weight: "semibold", children: title })] }), _jsx(Div, { paddingY: "y-md-lg", padding: "x-lg", children: content })] }, title))), _jsx(Alert, { variant: "info", children: _jsxs(Row, { align: "center", gap: "sm", children: [_jsx(FlaskConical, { className: "w-4 h-4 flex-shrink-0" }), _jsx(Text, { size: "sm", children: "Store-level Meta Commerce Catalog sync (product catalog on WhatsApp) uses separate, per-store credentials \u2014 see the Seller Guide's \"WhatsApp Catalog Sync\" page (Store dashboard \u2192 Guides). It shares the same Meta Business Manager account but not the same access token. The inbound-reply webhook (buyer messages the business number within 24h) uses the free-text send path and doesn't need a template." })] }) })] }));
35
- }
@@ -1,36 +0,0 @@
1
- export interface MarketplaceOrderCardOrder {
2
- id: string;
3
- status: string;
4
- orderDate: string | Date;
5
- totalPrice: number;
6
- currency?: string;
7
- productId?: string;
8
- productTitle?: string;
9
- trackingNumber?: string;
10
- items?: Array<unknown>;
11
- }
12
- export interface MarketplaceOrderCardLabels {
13
- orderNumber: string;
14
- placedOn: string;
15
- items: string;
16
- trackOrder: string;
17
- writeReview: string;
18
- viewOrder: string;
19
- }
20
- export interface MarketplaceOrderCardLinks {
21
- detailHref: (order: MarketplaceOrderCardOrder) => string;
22
- trackHref?: (order: MarketplaceOrderCardOrder) => string;
23
- reviewHref?: (order: MarketplaceOrderCardOrder) => string;
24
- }
25
- export interface MarketplaceOrderCardProps {
26
- order: MarketplaceOrderCardOrder;
27
- links?: MarketplaceOrderCardLinks;
28
- labels?: Partial<MarketplaceOrderCardLabels>;
29
- className?: string;
30
- variant?: "grid" | "list";
31
- selectable?: boolean;
32
- isSelected?: boolean;
33
- onSelect?: (id: string, selected: boolean) => void;
34
- onNavigate?: (href: string) => void;
35
- }
36
- export declare function MarketplaceOrderCard({ order, links, labels, className, variant, selectable, isSelected, onSelect, onNavigate, }: MarketplaceOrderCardProps): import("react").JSX.Element;