@mohasinac/appkit 4.53.2 → 4.54.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 (350) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/server/features/auth/capabilities.js +7 -5
  3. package/dist/_internal/server/features/checkout/actions.d.ts +22 -6
  4. package/dist/_internal/server/features/checkout/actions.js +96 -51
  5. package/dist/_internal/server/features/checkout/index.d.ts +1 -1
  6. package/dist/_internal/server/features/checkout/index.js +1 -1
  7. package/dist/_internal/server/features/checkout/supplier-identity.js +28 -6
  8. package/dist/_internal/server/features/orders/adapters.js +1 -3
  9. package/dist/_internal/server/features/products/listing-tab-counts.js +81 -25
  10. package/dist/_internal/server/features/search/index-build/sources.js +43 -2
  11. package/dist/_internal/server/features/site-settings/adapters.js +1 -1
  12. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  13. package/dist/_internal/server/features/stores/adapters.js +0 -1
  14. package/dist/_internal/server/jobs/core/analyticsAlertEvaluate.js +2 -5
  15. package/dist/_internal/server/jobs/core/jobRunners.js +0 -10
  16. package/dist/_internal/server/jobs/core/onOrderCreate.js +10 -104
  17. package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +0 -2
  18. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +0 -1
  19. package/dist/_internal/server/jobs/core/onScamReportCreate.js +0 -1
  20. package/dist/_internal/server/jobs/core/onScamReportRejected.js +0 -1
  21. package/dist/_internal/server/jobs/core/onScamReportVerified.js +0 -1
  22. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +0 -1
  23. package/dist/_internal/server/jobs/core/pendingOrderTimeout.js +0 -1
  24. package/dist/_internal/server/notifications/channel-health.d.ts +7 -3
  25. package/dist/_internal/server/notifications/channel-health.js +2 -2
  26. package/dist/_internal/server/notifications/send-guard.d.ts +1 -1
  27. package/dist/_internal/server/notifications/send-guard.js +2 -4
  28. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  29. package/dist/_internal/shared/actions/action-registry.js +128 -28
  30. package/dist/_internal/shared/checkout/order-math.d.ts +1 -1
  31. package/dist/_internal/shared/checkout/order-math.js +1 -1
  32. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
  33. package/dist/_internal/shared/features/checkout/config.d.ts +0 -2
  34. package/dist/_internal/shared/features/checkout/config.js +0 -2
  35. package/dist/_internal/shared/features/messaging/config.d.ts +7 -5
  36. package/dist/_internal/shared/features/messaging/config.js +1 -4
  37. package/dist/_internal/shared/features/site-settings/fees.d.ts +0 -2
  38. package/dist/_internal/shared/features/site-settings/fees.js +0 -2
  39. package/dist/_internal/shared/features/site-settings/supplier-of-record.d.ts +56 -0
  40. package/dist/_internal/shared/features/site-settings/supplier-of-record.js +57 -0
  41. package/dist/_internal/shared/fees/calculator.d.ts +0 -8
  42. package/dist/_internal/shared/fees/calculator.js +0 -7
  43. package/dist/_internal/shared/listing-types/tiers.d.ts +94 -0
  44. package/dist/_internal/shared/listing-types/tiers.js +150 -0
  45. package/dist/cli/index.js +0 -2
  46. package/dist/client.d.ts +25 -4
  47. package/dist/client.js +36 -5
  48. package/dist/constants/api-endpoints.d.ts +21 -15
  49. package/dist/constants/api-endpoints.js +7 -8
  50. package/dist/constants/field-names.d.ts +1 -0
  51. package/dist/constants/field-names.js +1 -0
  52. package/dist/constants/notification-types.d.ts +1 -1
  53. package/dist/constants/notification-types.js +1 -1
  54. package/dist/core/integration-keys.d.ts +5 -5
  55. package/dist/core/integration-keys.js +21 -10
  56. package/dist/features/about/components/FeesView.js +0 -6
  57. package/dist/features/about/components/GuidesHubView.js +15 -2
  58. package/dist/features/account/components/NotificationPreferencesPanel.js +3 -4
  59. package/dist/features/account/components/UserOrdersView.js +26 -7
  60. package/dist/features/account/types/index.d.ts +0 -2
  61. package/dist/features/addresses/utils/postal-autofill.d.ts +39 -0
  62. package/dist/features/addresses/utils/postal-autofill.js +54 -0
  63. package/dist/features/admin/actions/notification-actions.d.ts +2 -8
  64. package/dist/features/admin/actions/notification-actions.js +9 -104
  65. package/dist/features/admin/components/AdminGuideHubView.js +1 -8
  66. package/dist/features/admin/components/AdminOrderEditorView.d.ts +1 -1
  67. package/dist/features/admin/components/AdminOrdersView.js +0 -2
  68. package/dist/features/admin/components/AdminSectionsView.js +3 -2
  69. package/dist/features/admin/components/AdminSiteSettingsView.js +22 -68
  70. package/dist/features/admin/components/AdminStoreEditorView.js +72 -34
  71. package/dist/features/admin/components/OwnerQuickActions.d.ts +29 -0
  72. package/dist/features/admin/components/OwnerQuickActions.js +142 -0
  73. package/dist/features/admin/components/sections/adminSectionsTypes.js +13 -4
  74. package/dist/features/admin/components/sections/icon-options.d.ts +21 -0
  75. package/dist/features/admin/components/sections/icon-options.js +23 -0
  76. package/dist/features/admin/components/sections/sectionTemplates.js +4 -1
  77. package/dist/features/admin/constants/site-settings-tabs.d.ts +7 -12
  78. package/dist/features/admin/constants/site-settings-tabs.js +6 -12
  79. package/dist/features/admin/repository/site-settings.repository.js +1 -1
  80. package/dist/features/admin/schemas/admin-editor-forms.d.ts +1 -1
  81. package/dist/features/admin/schemas/admin-editor-forms.js +4 -1
  82. package/dist/features/admin/schemas/admin-user-form.d.ts +0 -61
  83. package/dist/features/admin/schemas/admin-user-form.js +3 -44
  84. package/dist/features/admin/schemas/firestore.d.ts +28 -54
  85. package/dist/features/admin/schemas/firestore.js +40 -18
  86. package/dist/features/auctions/components/AuctionDetailPageView.js +4 -3
  87. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -1
  88. package/dist/features/auctions/components/MarketplaceAuctionCard.js +9 -2
  89. package/dist/features/auth/checkout-value-otp.d.ts +1 -1
  90. package/dist/features/auth/checkout-value-otp.js +1 -1
  91. package/dist/features/auth/permissions/constants.d.ts +29 -2
  92. package/dist/features/auth/permissions/constants.js +67 -1
  93. package/dist/features/blog/components/BlogFeaturedCard.d.ts +11 -1
  94. package/dist/features/blog/components/BlogFeaturedCard.js +9 -2
  95. package/dist/features/cart/components/CartDrawer.js +3 -2
  96. package/dist/features/cart/components/CartGroupLineRow.js +2 -1
  97. package/dist/features/cart/components/CartPriceBreakdown.d.ts +0 -2
  98. package/dist/features/cart/components/CartPriceBreakdown.js +1 -1
  99. package/dist/features/cart/components/StoreAddonsPicker.d.ts +2 -5
  100. package/dist/features/cart/components/StoreAddonsPicker.js +4 -5
  101. package/dist/features/cart/hooks/useAddToCart.js +10 -5
  102. package/dist/features/cart/repository/cart.repository.js +1 -2
  103. package/dist/features/cart/schemas/firestore.d.ts +1 -2
  104. package/dist/features/cart/utils/pending-ops.d.ts +11 -1
  105. package/dist/features/cart/utils/pending-ops.js +14 -2
  106. package/dist/features/categories/components/BrandDetailPageView.js +2 -1
  107. package/dist/features/categories/components/BundleDetailView.js +2 -1
  108. package/dist/features/categories/components/CategoryDetailPageView.js +2 -1
  109. package/dist/features/categories/components/CategoryGrid.d.ts +11 -1
  110. package/dist/features/categories/components/CategoryGrid.js +9 -2
  111. package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +10 -5
  112. package/dist/features/checkout/actions/checkout-value-otp-actions.js +2 -88
  113. package/dist/features/classified/components/ClassifiedDetailPageView.js +2 -1
  114. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +2 -1
  115. package/dist/features/events/components/EventCard.d.ts +11 -1
  116. package/dist/features/events/components/EventCard.js +9 -2
  117. package/dist/features/grouped/components/GroupedListingDetailView.js +3 -1
  118. package/dist/features/homepage/components/CTABannerSection.d.ts +3 -1
  119. package/dist/features/homepage/components/CTABannerSection.js +6 -3
  120. package/dist/features/homepage/components/HeroCarousel.js +8 -6
  121. package/dist/features/homepage/components/MarketplaceHomepageView.js +33 -7
  122. package/dist/features/homepage/components/SectionCarousel.js +13 -5
  123. package/dist/features/homepage/components/SecurityHighlightsSection.d.ts +8 -3
  124. package/dist/features/homepage/components/SecurityHighlightsSection.js +2 -2
  125. package/dist/features/homepage/components/StatsCounterSection.d.ts +9 -4
  126. package/dist/features/homepage/components/StatsCounterSection.js +2 -2
  127. package/dist/features/homepage/components/TestimonialsCarousel.js +1 -1
  128. package/dist/features/homepage/components/TrustFeaturesSection.d.ts +18 -4
  129. package/dist/features/homepage/components/TrustFeaturesSection.js +5 -5
  130. package/dist/features/homepage/components/WelcomeSection.d.ts +13 -2
  131. package/dist/features/homepage/components/WelcomeSection.js +3 -3
  132. package/dist/features/homepage/components/WhatsAppCommunitySection.d.ts +28 -0
  133. package/dist/features/homepage/components/WhatsAppCommunitySection.js +42 -6
  134. package/dist/features/homepage/components/WhatsAppGlyph.d.ts +20 -0
  135. package/dist/features/homepage/components/WhatsAppGlyph.js +20 -0
  136. package/dist/features/homepage/constants/hero-carousel-styles.d.ts +8 -6
  137. package/dist/features/homepage/constants/hero-carousel-styles.js +8 -6
  138. package/dist/features/homepage/constants/section-copy.d.ts +12 -10
  139. package/dist/features/homepage/constants/section-copy.js +28 -6
  140. package/dist/features/homepage/constants/stat-icons.d.ts +12 -0
  141. package/dist/features/homepage/constants/stat-icons.js +17 -0
  142. package/dist/features/homepage/constants/trust-icon-aliases.d.ts +8 -0
  143. package/dist/features/homepage/constants/trust-icon-aliases.js +75 -0
  144. package/dist/features/homepage/lib/section-defaults.d.ts +16 -1
  145. package/dist/features/homepage/lib/section-defaults.js +24 -4
  146. package/dist/features/homepage/lib/section-renderer.js +48 -8
  147. package/dist/features/homepage/schemas/firestore.d.ts +18 -1
  148. package/dist/features/layout/AppLayoutShell.js +2 -1
  149. package/dist/features/layout/BottomActions.d.ts +1 -0
  150. package/dist/features/layout/BottomActions.js +330 -0
  151. package/dist/features/layout/BottomChrome.d.ts +45 -0
  152. package/dist/features/layout/BottomChrome.js +5 -17
  153. package/dist/features/layout/SideActionDock.js +36 -13
  154. package/dist/features/layout/index.d.ts +2 -7
  155. package/dist/features/layout/index.js +7 -7
  156. package/dist/features/layout/useIsDesktop.d.ts +36 -0
  157. package/dist/features/layout/useIsDesktop.js +48 -0
  158. package/dist/features/live/components/LiveItemDetailPageView.js +2 -1
  159. package/dist/features/orders/actions/refund-actions.js +0 -1
  160. package/dist/features/orders/components/OrderAddonBadges.d.ts +1 -3
  161. package/dist/features/orders/components/OrderAddonBadges.js +3 -4
  162. package/dist/features/orders/components/OrderPaymentSummary.js +15 -2
  163. package/dist/features/orders/components/YourPurchaseStrip.d.ts +24 -0
  164. package/dist/features/orders/components/YourPurchaseStrip.js +77 -0
  165. package/dist/features/orders/components/index.d.ts +2 -1
  166. package/dist/features/orders/components/index.js +1 -1
  167. package/dist/features/orders/repository/orders.repository.d.ts +0 -8
  168. package/dist/features/orders/repository/orders.repository.js +0 -2
  169. package/dist/features/orders/schemas/firestore.d.ts +17 -9
  170. package/dist/features/orders/types/index.d.ts +2 -5
  171. package/dist/features/orders/utils/order-announcement-message.d.ts +18 -0
  172. package/dist/features/orders/utils/order-announcement-message.js +16 -0
  173. package/dist/features/orders/utils/payment-proof-message.d.ts +20 -0
  174. package/dist/features/orders/utils/payment-proof-message.js +17 -0
  175. package/dist/features/parts/components/PartDetailPageView.js +96 -1
  176. package/dist/features/parts/schemas/firestore.d.ts +247 -0
  177. package/dist/features/parts/schemas/firestore.js +6 -6
  178. package/dist/features/pre-orders/components/MarketplacePreorderCard.d.ts +11 -1
  179. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +10 -3
  180. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +4 -3
  181. package/dist/features/products/api/[id]/route.js +8 -1
  182. package/dist/features/products/api/route.js +16 -4
  183. package/dist/features/products/components/AvailabilityScopeHint.js +1 -1
  184. package/dist/features/products/components/InteractiveProductCard.d.ts +7 -1
  185. package/dist/features/products/components/InteractiveProductCard.js +16 -4
  186. package/dist/features/products/components/MarketplaceBundleCard.d.ts +11 -1
  187. package/dist/features/products/components/MarketplaceBundleCard.js +9 -2
  188. package/dist/features/products/components/MarketplacePrizeDrawCard.d.ts +11 -1
  189. package/dist/features/products/components/MarketplacePrizeDrawCard.js +9 -2
  190. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -1
  191. package/dist/features/products/components/ProductDetailPageView.js +27 -2
  192. package/dist/features/products/components/ProductGrid.d.ts +14 -1
  193. package/dist/features/products/components/ProductGrid.js +22 -5
  194. package/dist/features/products/components/ProductsIndexListing.js +2 -2
  195. package/dist/features/reviews/api/[id]/route.js +83 -23
  196. package/dist/features/reviews/components/PurchaseReviewAffordance.d.ts +6 -0
  197. package/dist/features/reviews/components/PurchaseReviewAffordance.js +42 -0
  198. package/dist/features/reviews/components/ReviewFormDrawer.d.ts +34 -0
  199. package/dist/features/reviews/components/ReviewFormDrawer.js +127 -0
  200. package/dist/features/reviews/components/index.d.ts +4 -0
  201. package/dist/features/reviews/components/index.js +2 -0
  202. package/dist/features/scams/schemas/index.d.ts +10 -10
  203. package/dist/features/search/action-index/settings-entries.js +0 -9
  204. package/dist/features/seller/actions/seller-actions.d.ts +1 -1
  205. package/dist/features/seller/actions/seller-actions.js +49 -3
  206. package/dist/features/seller/components/SellerPayoutSettingsView.js +30 -2
  207. package/dist/features/seller/components/SellerProductShell.d.ts +17 -1
  208. package/dist/features/seller/hooks/useStoreListingEntitlements.d.ts +8 -0
  209. package/dist/features/seller/hooks/useStoreListingEntitlements.js +70 -0
  210. package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +1 -6
  211. package/dist/features/store-extensions/repository/store-extensions.repositories.js +1 -10
  212. package/dist/features/store-extensions/schemas/firestore.d.ts +1 -17
  213. package/dist/features/store-extensions/schemas/firestore.js +0 -10
  214. package/dist/features/stores/components/InteractiveStoreCard.d.ts +11 -1
  215. package/dist/features/stores/components/InteractiveStoreCard.js +9 -2
  216. package/dist/features/stores/components/StoreCapabilitiesGuideView.js +0 -1
  217. package/dist/features/stores/components/StoreGuideHubView.js +2 -8
  218. package/dist/features/stores/components/StoreSettingsGuideView.js +2 -6
  219. package/dist/features/stores/components/index.d.ts +0 -2
  220. package/dist/features/stores/components/index.js +0 -1
  221. package/dist/features/stores/repository/store.repository.d.ts +1 -26
  222. package/dist/features/stores/repository/store.repository.js +5 -60
  223. package/dist/features/stores/schemas/firestore.d.ts +6 -25
  224. package/dist/features/stores/schemas/firestore.js +6 -3
  225. package/dist/features/stores/schemas/index.d.ts +1 -87
  226. package/dist/features/stores/schemas/index.js +10 -15
  227. package/dist/features/tester/seed-data/authored/admin__site-system.js +0 -48
  228. package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +0 -40
  229. package/dist/features/tester/seed-data/authored/selling__quick-add-minimum-details.js +0 -16
  230. package/dist/features/tester/seed-data/authored/selling__seller-guide.js +1 -1
  231. package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.js +0 -79
  232. package/dist/features/tester/seed-data/authored/selling__seller-orders.js +1 -1
  233. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +415 -69
  234. package/dist/index.d.ts +20 -32
  235. package/dist/index.js +34 -43
  236. package/dist/next/routing/route-map.d.ts +2 -8
  237. package/dist/next/routing/route-map.js +21 -4
  238. package/dist/providers/db-firebase/base.repository.d.ts +1 -1
  239. package/dist/providers/db-firebase/base.repository.js +1 -1
  240. package/dist/providers/payment-razorpay/index.d.ts +125 -0
  241. package/dist/providers/payment-razorpay/index.js +232 -0
  242. package/dist/providers/payment-razorpay/signature.d.ts +53 -0
  243. package/dist/providers/payment-razorpay/signature.js +71 -0
  244. package/dist/providers/shipping-shiprocket/index.d.ts +98 -0
  245. package/dist/providers/shipping-shiprocket/index.js +356 -0
  246. package/dist/providers.d.ts +3 -0
  247. package/dist/providers.js +3 -0
  248. package/dist/react/hooks/useCanSeePrices.d.ts +0 -27
  249. package/dist/react/hooks/useCanSeePrices.js +25 -0
  250. package/dist/repositories/index.d.ts +1 -1
  251. package/dist/repositories/index.js +1 -1
  252. package/dist/schemas/registry.d.ts +10 -115
  253. package/dist/schemas/registry.js +1 -2
  254. package/dist/security/pii-encrypt.js +1 -1
  255. package/dist/seed/_helpers/category-forest.js +27 -0
  256. package/dist/seed/_helpers/category-parts-glossary-map.js +1816 -88
  257. package/dist/seed/_helpers/category-parts.d.ts +1 -1
  258. package/dist/seed/_helpers/category-parts.js +13399 -3019
  259. package/dist/seed/carousel-slides-seed-data.js +47 -10
  260. package/dist/seed/categories-seed-data.js +48 -6
  261. package/dist/seed/faq-seed-data.js +2 -2
  262. package/dist/seed/homepage-sections-seed-data.js +286 -54
  263. package/dist/seed/index.d.ts +1 -1
  264. package/dist/seed/index.js +1 -1
  265. package/dist/seed/manifest.d.ts +1 -1
  266. package/dist/seed/manifest.js +1 -2
  267. package/dist/seed/model-composition-seed-data.js +6 -8
  268. package/dist/seed/part-glossary/chunk0.d.ts +9 -0
  269. package/dist/seed/part-glossary/chunk0.js +35448 -0
  270. package/dist/seed/part-glossary/chunk1.d.ts +9 -0
  271. package/dist/seed/part-glossary/chunk1.js +40494 -0
  272. package/dist/seed/part-glossary/chunk2.d.ts +9 -0
  273. package/dist/seed/part-glossary/chunk2.js +51242 -0
  274. package/dist/seed/part-glossary/chunk3.d.ts +9 -0
  275. package/dist/seed/part-glossary/chunk3.js +53337 -0
  276. package/dist/seed/part-glossary/chunk4.d.ts +9 -0
  277. package/dist/seed/part-glossary/chunk4.js +51454 -0
  278. package/dist/seed/part-glossary-seed-data.d.ts +1 -1
  279. package/dist/seed/part-glossary-seed-data.js +23 -47483
  280. package/dist/seed/products-catalogue-seed-data.d.ts +5 -5
  281. package/dist/seed/products-catalogue-seed-data.js +63936 -13803
  282. package/dist/seed/site-settings-seed-data.js +34 -35
  283. package/dist/seed/store-extensions-seed-data.d.ts +1 -2
  284. package/dist/seed/store-extensions-seed-data.js +1 -23
  285. package/dist/seed/stores-seed-data.js +0 -1
  286. package/dist/server-entry.d.ts +1 -1
  287. package/dist/server-entry.js +1 -1
  288. package/dist/server.d.ts +8 -10
  289. package/dist/server.js +16 -22
  290. package/dist/styles.css +392 -43
  291. package/dist/tailwind-utilities.css +1 -1
  292. package/dist/tokens/themes/default-dark.js +1 -1
  293. package/dist/tokens/themes/default-light.js +2 -1
  294. package/dist/tokens/tokens.css +6 -2
  295. package/dist/ui/components/Button.d.ts +11 -0
  296. package/dist/ui/components/Button.js +11 -0
  297. package/dist/ui/components/Button.style.css +29 -0
  298. package/dist/ui/components/CarouselNav.d.ts +57 -0
  299. package/dist/ui/components/CarouselNav.js +25 -0
  300. package/dist/ui/components/CarouselNav.style.css +265 -0
  301. package/dist/ui/components/GatedPrice.js +30 -3
  302. package/dist/ui/components/GatedPrice.style.css +43 -40
  303. package/dist/ui/components/HorizontalScroller.d.ts +12 -1
  304. package/dist/ui/components/HorizontalScroller.js +43 -3
  305. package/dist/ui/components/ImageMosaic.d.ts +8 -1
  306. package/dist/ui/components/ImageMosaic.js +3 -2
  307. package/dist/ui/components/ImageMosaic.style.css +22 -0
  308. package/dist/ui/components/Layout.style.css +6 -0
  309. package/dist/ui/components/Semantic.d.ts +3 -2
  310. package/dist/ui/components/Semantic.js +1 -0
  311. package/dist/ui/components/Typography.d.ts +2 -0
  312. package/dist/ui/components/Typography.js +2 -0
  313. package/dist/ui/components/Typography.style.css +19 -0
  314. package/dist/ui/components/index.style.css +1 -0
  315. package/dist/ui/index.d.ts +3 -1
  316. package/dist/ui/index.js +4 -0
  317. package/package.json +3 -2
  318. package/dist/_internal/server/jobs/core/whatsappCatalogImport.d.ts +0 -27
  319. package/dist/_internal/server/jobs/core/whatsappCatalogImport.js +0 -172
  320. package/dist/_internal/server/jobs/core/whatsappNotify.d.ts +0 -35
  321. package/dist/_internal/server/jobs/core/whatsappNotify.js +0 -140
  322. package/dist/features/admin/components/AdminWhatsAppGuideView.d.ts +0 -2
  323. package/dist/features/admin/components/AdminWhatsAppGuideView.js +0 -35
  324. package/dist/features/orders/components/MarketplaceOrderCard.d.ts +0 -36
  325. package/dist/features/orders/components/MarketplaceOrderCard.js +0 -57
  326. package/dist/features/stores/components/StoreWhatsAppGuideView.d.ts +0 -3
  327. package/dist/features/stores/components/StoreWhatsAppGuideView.js +0 -12
  328. package/dist/features/whatsapp-bot/api/send-status/route.d.ts +0 -12
  329. package/dist/features/whatsapp-bot/api/send-status/route.js +0 -38
  330. package/dist/features/whatsapp-bot/api/webhook/route.d.ts +0 -12
  331. package/dist/features/whatsapp-bot/api/webhook/route.js +0 -43
  332. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.d.ts +0 -6
  333. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +0 -270
  334. package/dist/features/whatsapp-bot/components/WhatsAppChatButton.d.ts +0 -9
  335. package/dist/features/whatsapp-bot/components/WhatsAppChatButton.js +0 -7
  336. package/dist/features/whatsapp-bot/components/index.d.ts +0 -3
  337. package/dist/features/whatsapp-bot/components/index.js +0 -2
  338. package/dist/features/whatsapp-bot/helpers/payment-proof-message.d.ts +0 -14
  339. package/dist/features/whatsapp-bot/helpers/payment-proof-message.js +0 -17
  340. package/dist/features/whatsapp-bot/helpers/whatsapp.d.ts +0 -52
  341. package/dist/features/whatsapp-bot/helpers/whatsapp.js +0 -283
  342. package/dist/features/whatsapp-bot/index.d.ts +0 -3
  343. package/dist/features/whatsapp-bot/index.js +0 -3
  344. package/dist/features/whatsapp-bot/manifest.d.ts +0 -2
  345. package/dist/features/whatsapp-bot/manifest.js +0 -13
  346. package/dist/features/whatsapp-bot/messages/en.json +0 -7
  347. package/dist/features/whatsapp-bot/server.d.ts +0 -7
  348. package/dist/features/whatsapp-bot/server.js +0 -7
  349. package/dist/features/whatsapp-bot/types/index.d.ts +0 -126
  350. package/dist/features/whatsapp-bot/types/index.js +0 -1
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useRef, useState, useEffect, useCallback, } from "react";
4
4
  import { ChevronLeft, ChevronRight } from "lucide-react";
5
+ import { CarouselNav } from "./CarouselNav.js";
5
6
  const BREAKPOINTS = [
6
7
  ["2xl", 1536],
7
8
  ["xl", 1280],
@@ -21,12 +22,23 @@ function resolvePerView(perView, containerWidth) {
21
22
  }
22
23
  return 1;
23
24
  }
24
- export function HorizontalScroller({ children, className = "", gap = 16, snapToItems, showArrows, arrowSize = "md", arrowStyle = "circle", showScrollbar, scrollContainerRef: externalRef, onScroll, items, renderItem, keyExtractor, perView, rows = 1, autoScroll, autoScrollInterval = 3500, minItemWidth, pauseOnHover = false, itemClassName = "", loop = false, }) {
25
+ export function HorizontalScroller({ children, className = "", gap = 16, snapToItems, showArrows, arrowSize = "md", arrowStyle = "circle", showScrollbar, scrollContainerRef: externalRef, onScroll, items, renderItem, keyExtractor, perView, rows = 1, autoScroll, autoScrollInterval = 3500, minItemWidth, pauseOnHover = false, itemClassName = "", loop = false, showNav = false, navAriaLabel, }) {
25
26
  const [itemWidth, setItemWidth] = useState(undefined);
26
27
  const [colCount, setColCount] = useState(typeof perView === "number" ? perView : 3);
27
28
  const [isPaused, setIsPaused] = useState(false);
28
29
  const [atStart, setAtStart] = useState(true);
29
30
  const [atEnd, setAtEnd] = useState(true);
31
+ /*
32
+ * Page position, for the optional `showNav` indicator.
33
+ *
34
+ * 🛑 Derived from scroll geometry, never stored as the source of truth: the
35
+ * rail is a native scroller and a user can drag it to any offset, so an index
36
+ * this component *decided* would disagree with where the content actually is
37
+ * the first time someone swipes. One page = one viewport width, matching what
38
+ * `scrollBy` already moves.
39
+ */
40
+ const [pageIndex, setPageIndex] = useState(0);
41
+ const [pageCount, setPageCount] = useState(1);
30
42
  const internalRef = useRef(null);
31
43
  const containerRef = (externalRef ??
32
44
  internalRef);
@@ -91,6 +103,11 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
91
103
  return;
92
104
  // 1px tolerance for sub-pixel rounding
93
105
  const maxScroll = el.scrollWidth - el.clientWidth;
106
+ const width = el.clientWidth;
107
+ if (width > 0) {
108
+ setPageCount(Math.max(1, Math.ceil(el.scrollWidth / width)));
109
+ setPageIndex(Math.round(el.scrollLeft / width));
110
+ }
94
111
  if (maxScroll <= 1) {
95
112
  setAtStart(true);
96
113
  setAtEnd(true);
@@ -99,6 +116,12 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
99
116
  setAtStart(el.scrollLeft <= 1);
100
117
  setAtEnd(el.scrollLeft >= maxScroll - 1);
101
118
  }, [containerRef]);
119
+ const goToPage = useCallback((page) => {
120
+ const el = containerRef.current;
121
+ if (!el)
122
+ return;
123
+ el.scrollTo({ left: page * el.clientWidth, behavior: "smooth" });
124
+ }, [containerRef]);
102
125
  const handleKeyDown = useCallback((e) => {
103
126
  if (e.key === "ArrowRight") {
104
127
  e.preventDefault();
@@ -196,6 +219,23 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
196
219
  updateExtents();
197
220
  onScroll?.();
198
221
  };
222
+ /*
223
+ * The in-flow position indicator.
224
+ *
225
+ * 🛑 This is the fix for "no affordance on a phone", and it deliberately does
226
+ * NOT un-hide the absolute arrows below 640px. Those are `display:none` there
227
+ * for a documented reason — a 44px circle has nowhere to go over a
228
+ * single-column card, and `--with-arrows` reserves 2 × 44px of wrapper gutter
229
+ * to seat them, which on a narrow screen is width taken from the card itself.
230
+ * An in-flow control needs no gutter and no breakpoint, so the affordance
231
+ * exists at every width instead of appearing at one.
232
+ *
233
+ * No `progressMs`: a rail's autoScroll advances by 0.8 of a viewport rather
234
+ * than one page, so a fill timed to the interval would not line up with the
235
+ * segment it sits in. A position cue that is right beats a progress cue that
236
+ * is approximately right.
237
+ */
238
+ const navNode = showNav && pageCount > 1 ? (_jsx(CarouselNav, { count: pageCount, index: pageIndex, onSelect: goToPage, onPrev: () => scrollBy(-1), onNext: () => scrollBy(1), tone: "on-surface", atStart: atStart, atEnd: atEnd, loop: loop, paused: isPaused, ariaLabel: navAriaLabel ?? "Scroll position", className: "appkit-hscroller__nav" })) : null;
199
239
  if (showArrows) {
200
240
  const prevDisabled = !loop && atStart;
201
241
  const nextDisabled = !loop && atEnd;
@@ -206,9 +246,9 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
206
246
  className,
207
247
  ]
208
248
  .filter(Boolean)
209
- .join(" "), tabIndex: 0, onKeyDown: handleKeyDown, ...interactionHandlers, "data-section": "horizontalscroller-div-511", children: [!arrowsHidden && (_jsx("button", { type: "button", onClick: () => scrollBy(-1), "aria-label": "Previous", "aria-disabled": prevDisabled || undefined, disabled: prevDisabled, className: `appkit-hscroller__arrow appkit-hscroller__arrow--prev appkit-hscroller__arrow--${arrowSize} ${arrowStyle === "full-height" ? "appkit-hscroller__arrow--full-height" : ""}`, children: _jsx(ChevronLeft, { className: "w-4 h-4" }) })), _jsx("div", { ref: containerRef, onScroll: combinedOnScroll, className: scrollerCls(snapToItems, showScrollbar), style: { gap: `${gap}px` }, "data-section": "horizontalscroller-div-512", children: content }), !arrowsHidden && (_jsx("button", { type: "button", onClick: () => scrollBy(1), "aria-label": "Next", "aria-disabled": nextDisabled || undefined, disabled: nextDisabled, className: `appkit-hscroller__arrow appkit-hscroller__arrow--next appkit-hscroller__arrow--${arrowSize} ${arrowStyle === "full-height" ? "appkit-hscroller__arrow--full-height" : ""}`, children: _jsx(ChevronRight, { className: "w-4 h-4" }) }))] }));
249
+ .join(" "), tabIndex: 0, onKeyDown: handleKeyDown, ...interactionHandlers, "data-section": "horizontalscroller-div-511", children: [!arrowsHidden && (_jsx("button", { type: "button", onClick: () => scrollBy(-1), "aria-label": "Previous", "aria-disabled": prevDisabled || undefined, disabled: prevDisabled, className: `appkit-hscroller__arrow appkit-hscroller__arrow--prev appkit-hscroller__arrow--${arrowSize} ${arrowStyle === "full-height" ? "appkit-hscroller__arrow--full-height" : ""}`, children: _jsx(ChevronLeft, { className: "w-4 h-4" }) })), _jsx("div", { ref: containerRef, onScroll: combinedOnScroll, className: scrollerCls(snapToItems, showScrollbar), style: { gap: `${gap}px` }, "data-section": "horizontalscroller-div-512", children: content }), !arrowsHidden && (_jsx("button", { type: "button", onClick: () => scrollBy(1), "aria-label": "Next", "aria-disabled": nextDisabled || undefined, disabled: nextDisabled, className: `appkit-hscroller__arrow appkit-hscroller__arrow--next appkit-hscroller__arrow--${arrowSize} ${arrowStyle === "full-height" ? "appkit-hscroller__arrow--full-height" : ""}`, children: _jsx(ChevronRight, { className: "w-4 h-4" }) })), navNode] }));
210
250
  }
211
- return (_jsx("div", { className: ["appkit-hscroller", className].filter(Boolean).join(" "), tabIndex: 0, onKeyDown: handleKeyDown, ...interactionHandlers, "data-section": "horizontalscroller-div-513", children: _jsx("div", { ref: containerRef, onScroll: combinedOnScroll, className: scrollerCls(snapToItems, showScrollbar), style: { gap: `${gap}px` }, "data-section": "horizontalscroller-div-514", children: content }) }));
251
+ return (_jsxs("div", { className: ["appkit-hscroller", className].filter(Boolean).join(" "), tabIndex: 0, onKeyDown: handleKeyDown, ...interactionHandlers, "data-section": "horizontalscroller-div-513", children: [_jsx("div", { ref: containerRef, onScroll: combinedOnScroll, className: scrollerCls(snapToItems, showScrollbar), style: { gap: `${gap}px` }, "data-section": "horizontalscroller-div-514", children: content }), navNode] }));
212
252
  }
213
253
  function scrollerCls(snapToItems, showScrollbar) {
214
254
  return [
@@ -1,11 +1,18 @@
1
1
  import React from "react";
2
2
  /** Below this there is nothing to arrange, so the caller should render nothing. */
3
3
  export declare const MOSAIC_MIN = 3;
4
+ /**
5
+ * `band` — the category-hero strip this was built for (180/260px).
6
+ * `hero` — the homepage hero's right column, roughly double height.
7
+ */
8
+ export type ImageMosaicSize = "band" | "hero";
4
9
  export interface ImageMosaicProps {
5
10
  /** Image URLs, most important first. Fewer than `MOSAIC_MIN` renders null. */
6
11
  images: readonly string[];
7
12
  /** Describes the SUBJECT, not the images — one alt for a decorative group. */
8
13
  alt: string;
14
+ /** Height preset. Defaults to `"band"`, the original behaviour. */
15
+ size?: ImageMosaicSize;
9
16
  className?: string;
10
17
  }
11
18
  /**
@@ -40,4 +47,4 @@ export interface ImageMosaicProps {
40
47
  * breaks SSR/hydration agreement and makes the page feel unstable — and it is
41
48
  * the shape Root Cause #25 is about on the seed side.
42
49
  */
43
- export declare function ImageMosaic({ images, alt, className }: ImageMosaicProps): React.JSX.Element | null;
50
+ export declare function ImageMosaic({ images, alt, size, className }: ImageMosaicProps): React.JSX.Element | null;
@@ -37,10 +37,11 @@ export const MOSAIC_MIN = 3;
37
37
  * breaks SSR/hydration agreement and makes the page feel unstable — and it is
38
38
  * the shape Root Cause #25 is about on the seed side.
39
39
  */
40
- export function ImageMosaic({ images, alt, className }) {
40
+ export function ImageMosaic({ images, alt, size = "band", className }) {
41
41
  const tiles = images.filter(Boolean).slice(0, MOSAIC_MAX);
42
42
  if (tiles.length < MOSAIC_MIN)
43
43
  return null;
44
44
  const [lead, ...rest] = tiles;
45
- return (_jsxs(Div, { className: `appkit-image-mosaic ${className ?? ""}`, role: "img", "aria-label": alt, children: [_jsx(Div, { className: "appkit-image-mosaic__lead", children: _jsx(MediaImage, { src: lead, alt: "", size: "card" }) }), _jsx(Div, { className: "appkit-image-mosaic__rest", children: rest.map((src, i) => (_jsx(Div, { className: "appkit-image-mosaic__tile", children: _jsx(MediaImage, { src: src, alt: "", size: "thumbnail" }) }, `${src}-${i}`))) })] }));
45
+ const sizeClass = size === "hero" ? "appkit-image-mosaic--hero" : "";
46
+ return (_jsxs(Div, { className: `appkit-image-mosaic ${sizeClass} ${className ?? ""}`, role: "img", "aria-label": alt, children: [_jsx(Div, { className: "appkit-image-mosaic__lead", children: _jsx(MediaImage, { src: lead, alt: "", size: "card" }) }), _jsx(Div, { className: "appkit-image-mosaic__rest", children: rest.map((src, i) => (_jsx(Div, { className: "appkit-image-mosaic__tile", children: _jsx(MediaImage, { src: src, alt: "", size: "thumbnail" }) }, `${src}-${i}`))) })] }));
46
47
  }
@@ -27,6 +27,28 @@
27
27
  }
28
28
  }
29
29
 
30
+ /*
31
+ * `size="hero"` — the homepage hero's right column.
32
+ *
33
+ * 🛑 A separate modifier rather than a caller `className` height, because the
34
+ * fixed-height rule above is this primitive's OWN decision (see the header) and
35
+ * a consumer utility would simply fight it — with `important: true` in both
36
+ * Tailwind configs, which of the two wins is decided by emission order, not by
37
+ * the author. The band height is tuned for a category title; a hero column
38
+ * standing beside a `size="7xl"` headline needs roughly double or the mosaic
39
+ * reads as a letterboxed strip next to the copy.
40
+ */
41
+ .appkit-image-mosaic--hero {
42
+ height: clamp(320px, 34vw, 460px);
43
+ border-radius: var(--appkit-radius-2xl);
44
+ }
45
+
46
+ @media (min-width: 768px) {
47
+ .appkit-image-mosaic--hero {
48
+ height: clamp(360px, 34vw, 480px);
49
+ }
50
+ }
51
+
30
52
  .appkit-image-mosaic__lead,
31
53
  .appkit-image-mosaic__tile {
32
54
  position: relative;
@@ -324,6 +324,12 @@
324
324
  .appkit-section--tone-page-header { background-image: var(--appkit-gradient-page-header); }
325
325
  .appkit-section--tone-hero { background-image: var(--appkit-gradient-section-mesh); }
326
326
  .appkit-section--tone-accent-banner { background-image: var(--appkit-gradient-accent-banner); }
327
+ /* The variant layer finally exposing --appkit-gradient-whatsapp-card, which
328
+ had existed in the token layer with that exact name and NO consumer. The
329
+ community card used `accent-banner` instead — the same primary→secondary
330
+ gradient as CTABannerSection two sections below it, which is a real part of
331
+ why the homepage read monotonous. */
332
+ .appkit-section--tone-whatsapp { background-image: var(--appkit-gradient-whatsapp-card); }
327
333
 
328
334
  /* Semantic — Ul/Ol marker presets */
329
335
  .appkit-list--marker-disc { list-style: disc; padding-left: 1.25rem; }
@@ -41,7 +41,7 @@ import type { SurfaceProps, RoundedKey, PaddingKey, BorderKey, SurfaceKey } from
41
41
  * `--appkit-gradient-*` CSS variable so admin custom themes restyle the
42
42
  * section without consumer changes.
43
43
  */
44
- export type SectionTone = "plain" | "page-header" | "hero" | "accent-banner";
44
+ export type SectionTone = "plain" | "page-header" | "hero" | "accent-banner" | "whatsapp";
45
45
  type SemanticColor = "default" | "primary" | "muted" | "faint" | "inverse" | "success" | "warning" | "error" | "info";
46
46
  type SectionLayout = "default" | "flex" | "flex-col" | "flex-sm-row" | "flex-sm-row-between";
47
47
  type SectionGap = "none" | "xs" | "sm" | "md" | "lg";
@@ -62,7 +62,8 @@ export interface SectionProps extends React.HTMLAttributes<HTMLElement>, Surface
62
62
  /**
63
63
  * Themed background tone. Defaults to `"plain"`. Use `"page-header"` for
64
64
  * subtle gradient page-headers, `"hero"` for radial mesh hero backgrounds,
65
- * `"accent-banner"` for primary→secondary brand banners.
65
+ * `"accent-banner"` for primary→secondary brand banners, `"whatsapp"` for
66
+ * the WhatsApp-brand green→teal community card.
66
67
  */
67
68
  tone?: SectionTone;
68
69
  /** Scoped color/gradient/image background — an alternative to `tone` for admin/CMS-configured section backgrounds. Renders behind `children`, absolutely positioned within the section. */
@@ -6,6 +6,7 @@ const SECTION_TONE_MAP = {
6
6
  "page-header": "appkit-section--tone-page-header",
7
7
  hero: "appkit-section--tone-hero",
8
8
  "accent-banner": "appkit-section--tone-accent-banner",
9
+ whatsapp: "appkit-section--tone-whatsapp",
9
10
  };
10
11
  const SEMANTIC_COLOR_MAP = {
11
12
  default: "",
@@ -44,6 +44,8 @@ declare const TYPOGRAPHY: {
44
44
  readonly inherit: "";
45
45
  readonly accent: "appkit-color--accent";
46
46
  readonly inverse: "appkit-color--inverse";
47
+ /** Second tier on a dark/branded surface. See Typography.style.css. */
48
+ readonly "inverse-muted": "appkit-color--inverse-muted";
47
49
  };
48
50
  };
49
51
  export type ColorVariant = keyof typeof TYPOGRAPHY.colorVariant;
@@ -46,6 +46,8 @@ const TYPOGRAPHY = {
46
46
  inherit: "",
47
47
  accent: "appkit-color--accent",
48
48
  inverse: "appkit-color--inverse",
49
+ /** Second tier on a dark/branded surface. See Typography.style.css. */
50
+ "inverse-muted": "appkit-color--inverse-muted",
49
51
  },
50
52
  };
51
53
  const DROP_SHADOW_MAP = {
@@ -165,6 +165,25 @@
165
165
  color: var(--appkit-color-text-on-primary, #ffffff);
166
166
  }
167
167
 
168
+ /* Secondary text on a surface that stays dark/branded in EVERY theme.
169
+ *
170
+ * WHY: eight live sites hand-rolled a second tier on a branded surface and
171
+ * none of them worked the same way. Three in WhatsAppCommunitySection were
172
+ * `className="/80"` / `"/90"` / `"/75"` — codemod residue from a sweep that
173
+ * stripped `text-white` off `text-white/80` and left the modifier. Tailwind
174
+ * cannot generate a class named `/80`, so all three were silent no-ops and
175
+ * the intended hierarchy rendered flat. Five more in HeroCarousel used raw
176
+ * `opacity-70`/`-80`/`-90`, which fades the whole element, not just its ink.
177
+ *
178
+ * Reads --appkit-color-text-on-primary rather than literal white, so a theme
179
+ * whose on-primary ink is not #fff still gets a correct second tier. Three
180
+ * different alphas for one idea is the drift that produced this; there is one
181
+ * tier here on purpose.
182
+ */
183
+ .appkit-color--inverse-muted {
184
+ color: color-mix(in srgb, var(--appkit-color-text-on-primary, #ffffff) 78%, transparent);
185
+ }
186
+
168
187
  .dark .appkit-color--primary {
169
188
  color: var(--appkit-color-text);
170
189
  }
@@ -40,6 +40,7 @@
40
40
  @import "./DescriptionField.style.css";
41
41
  @import "./FormGrid.style.css";
42
42
  @import "./HorizontalScroller.style.css";
43
+ @import "./CarouselNav.style.css";
43
44
  @import "./NavigationLoader.style.css";
44
45
  @import "./ImageLightbox.style.css";
45
46
  @import "./SideModal.style.css";
@@ -106,7 +106,7 @@ export { StarRating } from "./components/StarRating";
106
106
  export type { BreadcrumbProps, BreadcrumbItem } from "./components/Breadcrumb";
107
107
  export { Breadcrumb } from "./components/Breadcrumb";
108
108
  export { ImageMosaic, MOSAIC_MIN } from "./components/ImageMosaic";
109
- export type { ImageMosaicProps } from "./components/ImageMosaic";
109
+ export type { ImageMosaicProps, ImageMosaicSize } from "./components/ImageMosaic";
110
110
  export { SplitTile } from "./components/SplitTile";
111
111
  export type { SplitTileProps, SplitTilePane } from "./components/SplitTile";
112
112
  export type { ImageLightboxProps, LightboxImage, } from "./components/ImageLightbox";
@@ -133,6 +133,8 @@ export type { ItemRowProps } from "./components/ItemRow";
133
133
  export { ItemRow } from "./components/ItemRow";
134
134
  export type { HorizontalScrollerProps, PerViewConfig, } from "./components/HorizontalScroller";
135
135
  export { HorizontalScroller } from "./components/HorizontalScroller";
136
+ export { CarouselNav, CAROUSEL_RAIL_MAX } from "./components/CarouselNav";
137
+ export type { CarouselNavProps, CarouselNavTone } from "./components/CarouselNav";
136
138
  export type { FormGridProps, FormFieldProps } from "./components/FormGrid";
137
139
  export { FormGrid, FormField as FormGridField } from "./components/FormGrid";
138
140
  export type { SettingsSectionProps, ToggleRowProps } from "./components/SettingsSection";
package/dist/ui/index.js CHANGED
@@ -98,6 +98,10 @@ export { SummaryCard } from "./components/SummaryCard.js";
98
98
  export { CountdownDisplay } from "./components/CountdownDisplay.js";
99
99
  export { ItemRow } from "./components/ItemRow.js";
100
100
  export { HorizontalScroller } from "./components/HorizontalScroller.js";
101
+ /* The ONE carousel position/navigation control — replaces the hero's bespoke
102
+ pill dots, the rail arrows that vanished below 640px, and the testimonial
103
+ rail's complete absence of an indicator. */
104
+ export { CarouselNav, CAROUSEL_RAIL_MAX } from "./components/CarouselNav.js";
101
105
  export { FormGrid, FormField as FormGridField } from "./components/FormGrid.js";
102
106
  export { SettingsSection, ToggleRow } from "./components/SettingsSection.js";
103
107
  export { DetailPageHero } from "./components/DetailPageHero.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.53.2",
3
+ "version": "4.54.1",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -254,7 +254,8 @@
254
254
  "driver.js": "^1.8.0",
255
255
  "jsbarcode": "^3.12.3",
256
256
  "motion": "^13.1.1",
257
- "phonepe-pg-sdk-node": "^2.0.2",
257
+ "phonepe-pg-sdk-node": ">=2.0.2",
258
+ "razorpay": "^2.9.8",
258
259
  "react-advanced-cropper": "^0.20.1",
259
260
  "tailwind-merge": "^3.3.0",
260
261
  "zod": "^3.24.0",
@@ -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>;