@mohasinac/appkit 2.7.59 → 2.8.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 (311) hide show
  1. package/dist/_internal/client/features/filters/filter-load-options.d.ts +4 -5
  2. package/dist/_internal/server/features/auctions/og.js +8 -32
  3. package/dist/_internal/server/features/blog/og.js +10 -49
  4. package/dist/_internal/server/features/checkout/actions.js +6 -0
  5. package/dist/_internal/server/features/classified/og.js +9 -51
  6. package/dist/_internal/server/features/digital-code/actions.d.ts +11 -0
  7. package/dist/_internal/server/features/digital-code/actions.js +30 -0
  8. package/dist/_internal/server/features/digital-code/index.d.ts +1 -0
  9. package/dist/_internal/server/features/digital-code/index.js +1 -0
  10. package/dist/_internal/server/features/digital-code/og.js +9 -51
  11. package/dist/_internal/server/features/events/og.js +10 -49
  12. package/dist/_internal/server/features/item-requests/data.d.ts +17 -0
  13. package/dist/_internal/server/features/item-requests/data.js +20 -0
  14. package/dist/_internal/server/features/item-requests/index.d.ts +2 -0
  15. package/dist/_internal/server/features/item-requests/index.js +2 -0
  16. package/dist/_internal/server/features/item-requests/og.d.ts +40 -0
  17. package/dist/_internal/server/features/item-requests/og.js +136 -0
  18. package/dist/_internal/server/features/live/actions.d.ts +5 -0
  19. package/dist/_internal/server/features/live/actions.js +24 -0
  20. package/dist/_internal/server/features/live/index.d.ts +1 -0
  21. package/dist/_internal/server/features/live/index.js +1 -0
  22. package/dist/_internal/server/features/live/og.js +9 -51
  23. package/dist/_internal/server/features/media/contextGuards.js +19 -8
  24. package/dist/_internal/server/features/pre-orders/og.js +8 -32
  25. package/dist/_internal/server/features/prize-draws/data.d.ts +17 -0
  26. package/dist/_internal/server/features/prize-draws/data.js +20 -0
  27. package/dist/_internal/server/features/prize-draws/index.d.ts +2 -0
  28. package/dist/_internal/server/features/prize-draws/index.js +2 -0
  29. package/dist/_internal/server/features/prize-draws/og.d.ts +30 -0
  30. package/dist/_internal/server/features/prize-draws/og.js +50 -0
  31. package/dist/_internal/server/features/products/og.js +8 -33
  32. package/dist/_internal/server/features/seo/og-layout.d.ts +42 -0
  33. package/dist/_internal/server/features/seo/og-layout.js +69 -0
  34. package/dist/_internal/server/features/stores/og.js +8 -41
  35. package/dist/_internal/server/features/sublisting-categories/og.js +11 -42
  36. package/dist/client.d.ts +10 -6
  37. package/dist/client.js +7 -3
  38. package/dist/constants/index.d.ts +4 -0
  39. package/dist/constants/index.js +2 -0
  40. package/dist/constants/notification-types.d.ts +20 -0
  41. package/dist/constants/notification-types.js +37 -0
  42. package/dist/constants/pagination-presets.d.ts +39 -0
  43. package/dist/constants/pagination-presets.js +38 -0
  44. package/dist/errors/messages.d.ts +0 -4
  45. package/dist/errors/messages.js +0 -4
  46. package/dist/features/account/components/UserOffersPanel.js +1 -1
  47. package/dist/features/account/components/UserSupportView.d.ts +1 -2
  48. package/dist/features/account/components/UserSupportView.js +9 -1
  49. package/dist/features/admin/actions/admin-actions.js +16 -1
  50. package/dist/features/admin/components/AdminAllEventEntriesView.d.ts +2 -3
  51. package/dist/features/admin/components/AdminAllEventEntriesView.js +50 -83
  52. package/dist/features/admin/components/AdminBidsView.d.ts +2 -3
  53. package/dist/features/admin/components/AdminBidsView.js +64 -98
  54. package/dist/features/admin/components/AdminBlogView.d.ts +3 -7
  55. package/dist/features/admin/components/AdminBlogView.js +53 -83
  56. package/dist/features/admin/components/AdminBrandsView.d.ts +2 -3
  57. package/dist/features/admin/components/AdminBrandsView.js +84 -83
  58. package/dist/features/admin/components/AdminBundlesView.js +82 -125
  59. package/dist/features/admin/components/AdminCarouselView.d.ts +2 -2
  60. package/dist/features/admin/components/AdminCarouselView.js +17 -52
  61. package/dist/features/admin/components/AdminCartsView.d.ts +2 -3
  62. package/dist/features/admin/components/AdminCartsView.js +39 -88
  63. package/dist/features/admin/components/AdminCategoriesView.d.ts +3 -3
  64. package/dist/features/admin/components/AdminCategoriesView.js +98 -92
  65. package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
  66. package/dist/features/admin/components/AdminClassifiedView.d.ts +3 -0
  67. package/dist/features/admin/components/AdminClassifiedView.js +44 -0
  68. package/dist/features/admin/components/AdminContactView.d.ts +2 -2
  69. package/dist/features/admin/components/AdminContactView.js +104 -111
  70. package/dist/features/admin/components/AdminCouponsView.d.ts +3 -6
  71. package/dist/features/admin/components/AdminCouponsView.js +66 -86
  72. package/dist/features/admin/components/AdminDigitalCodesView.d.ts +3 -0
  73. package/dist/features/admin/components/AdminDigitalCodesView.js +51 -0
  74. package/dist/features/admin/components/AdminFaqsView.d.ts +4 -4
  75. package/dist/features/admin/components/AdminFaqsView.js +77 -33
  76. package/dist/features/admin/components/AdminFeaturesView.d.ts +2 -3
  77. package/dist/features/admin/components/AdminFeaturesView.js +57 -70
  78. package/dist/features/admin/components/AdminGroupedListingsView.d.ts +3 -0
  79. package/dist/features/admin/components/AdminGroupedListingsView.js +43 -0
  80. package/dist/features/admin/components/AdminHistoryView.d.ts +2 -3
  81. package/dist/features/admin/components/AdminHistoryView.js +35 -52
  82. package/dist/features/admin/components/AdminListingScaffold.d.ts +2 -2
  83. package/dist/features/admin/components/AdminListingScaffold.js +3 -3
  84. package/dist/features/admin/components/AdminLiveView.d.ts +3 -0
  85. package/dist/features/admin/components/AdminLiveView.js +50 -0
  86. package/dist/features/admin/components/AdminNewsletterView.d.ts +2 -2
  87. package/dist/features/admin/components/AdminNewsletterView.js +67 -97
  88. package/dist/features/admin/components/AdminNotificationsView.d.ts +2 -3
  89. package/dist/features/admin/components/AdminNotificationsView.js +67 -93
  90. package/dist/features/admin/components/AdminOrderEditorView.js +10 -10
  91. package/dist/features/admin/components/AdminOrdersView.d.ts +2 -3
  92. package/dist/features/admin/components/AdminOrdersView.js +93 -107
  93. package/dist/features/admin/components/AdminPayoutsView.d.ts +2 -3
  94. package/dist/features/admin/components/AdminPayoutsView.js +51 -91
  95. package/dist/features/admin/components/AdminPrizeDrawsView.d.ts +2 -3
  96. package/dist/features/admin/components/AdminPrizeDrawsView.js +39 -76
  97. package/dist/features/admin/components/AdminProductEditorView.js +3 -3
  98. package/dist/features/admin/components/AdminProductsView.d.ts +4 -4
  99. package/dist/features/admin/components/AdminProductsView.js +167 -163
  100. package/dist/features/admin/components/AdminReturnRequestsView.d.ts +1 -1
  101. package/dist/features/admin/components/AdminReturnRequestsView.js +64 -66
  102. package/dist/features/admin/components/AdminReviewsView.d.ts +3 -7
  103. package/dist/features/admin/components/AdminReviewsView.js +94 -110
  104. package/dist/features/admin/components/AdminScammersView.d.ts +2 -3
  105. package/dist/features/admin/components/AdminScammersView.js +53 -97
  106. package/dist/features/admin/components/AdminSectionsView.js +1 -1
  107. package/dist/features/admin/components/AdminSessionsView.d.ts +2 -3
  108. package/dist/features/admin/components/AdminSessionsView.js +62 -89
  109. package/dist/features/admin/components/AdminStoreAddressesView.d.ts +1 -1
  110. package/dist/features/admin/components/AdminStoreAddressesView.js +39 -53
  111. package/dist/features/admin/components/AdminStoreEditorView.js +21 -8
  112. package/dist/features/admin/components/AdminStoresView.d.ts +2 -3
  113. package/dist/features/admin/components/AdminStoresView.js +67 -99
  114. package/dist/features/admin/components/AdminSublistingCategoriesView.d.ts +1 -1
  115. package/dist/features/admin/components/AdminSublistingCategoriesView.js +28 -44
  116. package/dist/features/admin/components/AdminSupportTicketDetailView.d.ts +10 -1
  117. package/dist/features/admin/components/AdminSupportTicketDetailView.js +191 -5
  118. package/dist/features/admin/components/AdminSupportTicketsView.d.ts +2 -3
  119. package/dist/features/admin/components/AdminSupportTicketsView.js +65 -96
  120. package/dist/features/admin/components/AdminTeamView.d.ts +2 -2
  121. package/dist/features/admin/components/AdminTeamView.js +40 -74
  122. package/dist/features/admin/components/AdminUserEditorView.d.ts +11 -1
  123. package/dist/features/admin/components/AdminUserEditorView.js +80 -6
  124. package/dist/features/admin/components/AdminUsersView.d.ts +2 -3
  125. package/dist/features/admin/components/AdminUsersView.js +108 -100
  126. package/dist/features/admin/components/AdminViewCards.d.ts +1 -1
  127. package/dist/features/admin/components/AdminWishlistsView.d.ts +2 -3
  128. package/dist/features/admin/components/AdminWishlistsView.js +36 -62
  129. package/dist/features/admin/components/DataListingView.d.ts +122 -0
  130. package/dist/features/admin/components/DataListingView.js +66 -0
  131. package/dist/features/admin/components/index.d.ts +10 -4
  132. package/dist/features/admin/components/index.js +6 -2
  133. package/dist/features/admin/constants/filter-tabs.d.ts +23 -17
  134. package/dist/features/admin/constants/filter-tabs.js +21 -17
  135. package/dist/features/admin/hooks/useAdminListing.d.ts +1 -0
  136. package/dist/features/admin/hooks/useAdminListing.js +1 -1
  137. package/dist/features/admin/schemas/firestore.js +6 -7
  138. package/dist/features/auctions/server.d.ts +0 -1
  139. package/dist/features/auctions/server.js +0 -1
  140. package/dist/features/auth/actions/realtime-token-actions.js +1 -1
  141. package/dist/features/auth/repository/user.repository.js +1 -1
  142. package/dist/features/cart/repository/cart.repository.d.ts +4 -1
  143. package/dist/features/cart/repository/cart.repository.js +12 -0
  144. package/dist/features/checkout/hooks/useCheckoutApi.d.ts +0 -2
  145. package/dist/features/checkout/index.d.ts +1 -1
  146. package/dist/features/events/components/AdminEventsView.d.ts +3 -3
  147. package/dist/features/events/components/AdminEventsView.js +50 -86
  148. package/dist/features/events/components/EventOfferCard.d.ts +19 -0
  149. package/dist/features/events/components/EventOfferCard.js +29 -0
  150. package/dist/features/events/components/EventRaffleEntryForm.d.ts +25 -0
  151. package/dist/features/events/components/EventRaffleEntryForm.js +37 -0
  152. package/dist/features/events/components/EventRafflesSection.d.ts +1 -4
  153. package/dist/features/events/components/EventRafflesSection.js +17 -6
  154. package/dist/features/events/components/EventSaleBanner.d.ts +21 -0
  155. package/dist/features/events/components/EventSaleBanner.js +18 -0
  156. package/dist/features/events/components/index.d.ts +6 -0
  157. package/dist/features/events/components/index.js +4 -0
  158. package/dist/features/events/repository/event-entry.repository.d.ts +5 -0
  159. package/dist/features/events/repository/event-entry.repository.js +7 -0
  160. package/dist/features/filters/AsyncFacetSection.d.ts +1 -1
  161. package/dist/features/homepage/components/FAQSection.d.ts +0 -5
  162. package/dist/features/homepage/lib/live-stats.js +1 -1
  163. package/dist/features/homepage/schemas/firestore.d.ts +0 -2
  164. package/dist/features/layout/index.d.ts +0 -2
  165. package/dist/features/layout/index.js +0 -1
  166. package/dist/features/pre-orders/server.d.ts +0 -1
  167. package/dist/features/pre-orders/server.js +0 -1
  168. package/dist/features/products/components/GroupSettingsPanel.js +2 -2
  169. package/dist/features/products/components/PrizeDrawItemsEditor.js +2 -2
  170. package/dist/features/products/components/PrizeDrawsSection.d.ts +1 -4
  171. package/dist/features/products/components/PrizeDrawsSection.js +17 -6
  172. package/dist/features/products/components/PrizeRevealModal.js +1 -1
  173. package/dist/features/products/components/ProductCardMetadataSection.d.ts +11 -0
  174. package/dist/features/products/components/ProductCardMetadataSection.js +25 -0
  175. package/dist/features/products/components/ProductDetailPageView.js +3 -1
  176. package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
  177. package/dist/features/products/components/ProductGradingTab.d.ts +13 -0
  178. package/dist/features/products/components/ProductGradingTab.js +25 -0
  179. package/dist/features/products/components/ProductGrid.js +14 -2
  180. package/dist/features/products/components/ProductTabsShell.d.ts +5 -1
  181. package/dist/features/products/components/ProductTabsShell.js +6 -1
  182. package/dist/features/products/components/index.d.ts +4 -0
  183. package/dist/features/products/components/index.js +3 -0
  184. package/dist/features/products/constants/action-defs.d.ts +5 -0
  185. package/dist/features/products/constants/action-defs.js +52 -32
  186. package/dist/features/products/constants/sieve.d.ts +24 -4
  187. package/dist/features/products/constants/sieve.js +20 -11
  188. package/dist/features/products/schemas/firestore.d.ts +17 -0
  189. package/dist/features/products/schemas/product-features.validators.d.ts +2 -2
  190. package/dist/features/products/schemas/product-templates.d.ts +2 -4
  191. package/dist/features/products/types/index.d.ts +4 -0
  192. package/dist/features/promotions/server.d.ts +0 -1
  193. package/dist/features/promotions/server.js +0 -1
  194. package/dist/features/search/api/route.js +44 -1
  195. package/dist/features/search/components/Search.js +2 -0
  196. package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
  197. package/dist/features/search/hooks/useNavSuggestions.js +31 -4
  198. package/dist/features/seller/components/BrandInlineSelect.js +2 -2
  199. package/dist/features/seller/components/CategoryInlineSelect.js +2 -2
  200. package/dist/features/seller/components/SellerAddressesView.js +1 -1
  201. package/dist/features/seller/components/SellerAuctionsView.d.ts +3 -3
  202. package/dist/features/seller/components/SellerAuctionsView.js +60 -90
  203. package/dist/features/seller/components/SellerBidsView.js +1 -1
  204. package/dist/features/seller/components/SellerBundlesView.js +64 -89
  205. package/dist/features/seller/components/SellerClassifiedView.js +67 -90
  206. package/dist/features/seller/components/SellerCouponEditorView.js +1 -1
  207. package/dist/features/seller/components/SellerCouponsView.d.ts +2 -2
  208. package/dist/features/seller/components/SellerCouponsView.js +45 -84
  209. package/dist/features/seller/components/SellerDigitalCodesView.js +63 -88
  210. package/dist/features/seller/components/SellerGroupedListingsView.js +33 -49
  211. package/dist/features/seller/components/SellerLiveView.js +63 -84
  212. package/dist/features/seller/components/SellerOffersPanel.js +1 -1
  213. package/dist/features/seller/components/SellerOffersView.d.ts +2 -2
  214. package/dist/features/seller/components/SellerOffersView.js +37 -69
  215. package/dist/features/seller/components/SellerOrdersView.d.ts +2 -2
  216. package/dist/features/seller/components/SellerOrdersView.js +4 -4
  217. package/dist/features/seller/components/SellerPayoutMethodsView.js +1 -1
  218. package/dist/features/seller/components/SellerPayoutsView.d.ts +2 -2
  219. package/dist/features/seller/components/SellerPayoutsView.js +34 -68
  220. package/dist/features/seller/components/SellerPreOrdersView.d.ts +2 -2
  221. package/dist/features/seller/components/SellerPreOrdersView.js +49 -82
  222. package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +2 -2
  223. package/dist/features/seller/components/SellerPrizeDrawsView.js +49 -82
  224. package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
  225. package/dist/features/seller/components/SellerProductsView.d.ts +2 -2
  226. package/dist/features/seller/components/SellerProductsView.js +15 -10
  227. package/dist/features/seller/components/SellerShippingConfigsView.js +1 -1
  228. package/dist/features/seller/components/SellerSidebar.d.ts +0 -4
  229. package/dist/features/seller/components/SellerSidebar.js +0 -2
  230. package/dist/features/seller/components/SellerStoreCategoriesView.js +1 -1
  231. package/dist/features/seller/components/SellerTemplatesView.js +2 -2
  232. package/dist/features/seller/components/SellersListView.d.ts +1 -2
  233. package/dist/features/seller/components/analytics/SellerAnalyticsStats.d.ts +5 -0
  234. package/dist/features/seller/components/analytics/SellerAnalyticsStats.js +2 -8
  235. package/dist/features/seller/components/index.d.ts +0 -3
  236. package/dist/features/seller/components/index.js +0 -2
  237. package/dist/features/seller/server.d.ts +0 -1
  238. package/dist/features/seller/server.js +0 -1
  239. package/dist/features/shell/FormShell.d.ts +1 -1
  240. package/dist/features/shell/FormShell.js +4 -1
  241. package/dist/features/shell/field-groups/ImageFieldGroup.d.ts +18 -0
  242. package/dist/features/shell/field-groups/ImageFieldGroup.js +15 -0
  243. package/dist/features/shell/field-groups/SeoFieldGroup.d.ts +22 -0
  244. package/dist/features/shell/field-groups/SeoFieldGroup.js +16 -0
  245. package/dist/features/shell/field-groups/StatusFieldGroup.d.ts +35 -0
  246. package/dist/features/shell/field-groups/StatusFieldGroup.js +16 -0
  247. package/dist/features/shell/field-groups/TitleDescriptionGroup.d.ts +40 -0
  248. package/dist/features/shell/field-groups/TitleDescriptionGroup.js +16 -0
  249. package/dist/features/shell/field-groups/index.d.ts +13 -0
  250. package/dist/features/shell/field-groups/index.js +9 -0
  251. package/dist/features/shell/index.d.ts +2 -0
  252. package/dist/features/shell/index.js +1 -0
  253. package/dist/features/stores/components/StoreCapabilitiesGuideView.d.ts +1 -2
  254. package/dist/features/stores/components/StoreFinanceGuideView.d.ts +1 -2
  255. package/dist/features/stores/components/StoreHeader.js +2 -1
  256. package/dist/features/stores/components/StoreListingsGuideView.d.ts +1 -2
  257. package/dist/features/stores/components/StoreOrdersGuideView.d.ts +1 -2
  258. package/dist/features/stores/components/StoreScopedSearch.d.ts +16 -0
  259. package/dist/features/stores/components/StoreScopedSearch.js +27 -0
  260. package/dist/features/stores/components/StoreSettingsGuideView.d.ts +1 -2
  261. package/dist/features/stores/server.d.ts +0 -1
  262. package/dist/features/stores/server.js +0 -1
  263. package/dist/features/support/schemas/firestore.d.ts +22 -1
  264. package/dist/features/support/schemas/firestore.js +10 -0
  265. package/dist/features/wishlist/server.d.ts +0 -1
  266. package/dist/features/wishlist/server.js +0 -1
  267. package/dist/index.d.ts +9 -27
  268. package/dist/index.js +14 -50
  269. package/dist/next/routing/route-map.d.ts +8 -0
  270. package/dist/next/routing/route-map.js +4 -0
  271. package/dist/react/hooks/useListingTypeFlags.d.ts +32 -0
  272. package/dist/react/hooks/useListingTypeFlags.js +19 -0
  273. package/dist/security/index.d.ts +1 -1
  274. package/dist/security/index.js +1 -1
  275. package/dist/security/pii-redact.d.ts +8 -0
  276. package/dist/security/pii-redact.js +10 -1
  277. package/dist/seed/grouped-listings-seed-data.d.ts +4 -0
  278. package/dist/seed/products-preorders-seed-data.d.ts +4 -0
  279. package/dist/seed/products-prize-draws-seed-data.js +3 -0
  280. package/dist/seed/site-settings-seed-data.js +5 -5
  281. package/dist/seed/sub-listings-seed-data.d.ts +4 -0
  282. package/dist/server.d.ts +2 -0
  283. package/dist/server.js +4 -0
  284. package/dist/styles.css +54 -395
  285. package/dist/tailwind-utilities.css +1 -1
  286. package/dist/ui/components/BaseListingCard.d.ts +10 -0
  287. package/dist/ui/components/BaseListingCard.js +17 -1
  288. package/dist/ui/components/DetailPageGallery.d.ts +32 -0
  289. package/dist/ui/components/DetailPageGallery.js +47 -0
  290. package/dist/ui/components/DetailPageHero.d.ts +30 -0
  291. package/dist/ui/components/DetailPageHero.js +22 -0
  292. package/dist/ui/components/DetailPageTabs.d.ts +34 -0
  293. package/dist/ui/components/DetailPageTabs.js +38 -0
  294. package/dist/ui/components/HorizontalScroller.style.css +15 -2
  295. package/dist/ui/components/ListingLayout.d.ts +11 -2
  296. package/dist/ui/components/ListingLayout.js +12 -6
  297. package/dist/ui/components/PaginatedSelect.d.ts +66 -0
  298. package/dist/ui/components/PaginatedSelect.js +201 -0
  299. package/dist/ui/components/PaginatedSelect.style.css +254 -0
  300. package/dist/ui/components/Semantic.d.ts +0 -4
  301. package/dist/ui/components/Semantic.js +0 -2
  302. package/dist/ui/components/SettingsSection.d.ts +47 -0
  303. package/dist/ui/components/SettingsSection.js +38 -0
  304. package/dist/ui/components/index.style.css +1 -4
  305. package/dist/ui/index.d.ts +12 -14
  306. package/dist/ui/index.js +6 -7
  307. package/dist/utils/id-generators.d.ts +1 -18
  308. package/dist/utils/id-generators.js +51 -18
  309. package/dist/validation/schemas.d.ts +25 -0
  310. package/dist/validation/schemas.js +28 -0
  311. package/package.json +1 -1
@@ -0,0 +1,136 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
3
+ function formatPriceInr(paise) {
4
+ const rupees = Math.round(paise / 100);
5
+ return `≤ ₹${rupees.toLocaleString("en-IN")}`;
6
+ }
7
+ function normalizeStatus(s) {
8
+ if (s === "open" || s === "closed" || s === "removed" || s === "approved")
9
+ return s;
10
+ return null;
11
+ }
12
+ export function renderItemRequestOg(doc, opts) {
13
+ const budgetLabel = typeof doc?.maxBudgetInPaise === "number" && doc.maxBudgetInPaise > 0
14
+ ? formatPriceInr(doc.maxBudgetInPaise)
15
+ : null;
16
+ return renderItemRequestOgImage({
17
+ title: doc?.title ?? "Item Request",
18
+ opDisplayName: doc?.opDisplayName ?? null,
19
+ budgetLabel,
20
+ imageUrl: resolveOgImageUrl(doc?.imageUrls?.[0] ?? null, opts.baseUrl),
21
+ status: normalizeStatus(doc?.status),
22
+ replyCount: doc?.replyCount ?? null,
23
+ category: doc?.category ?? null,
24
+ }, opts.siteName);
25
+ }
26
+ const STATUS_BADGE = {
27
+ open: {
28
+ text: "Open",
29
+ bg: "rgba(74, 222, 128, 0.18)",
30
+ border: "rgba(74, 222, 128, 0.4)",
31
+ color: "#bbf7d0",
32
+ },
33
+ approved: {
34
+ text: "Approved",
35
+ bg: "rgba(56, 189, 248, 0.18)",
36
+ border: "rgba(56, 189, 248, 0.4)",
37
+ color: "#e0f2fe",
38
+ },
39
+ closed: {
40
+ text: "Closed",
41
+ bg: "rgba(148, 163, 184, 0.2)",
42
+ border: "rgba(148, 163, 184, 0.45)",
43
+ color: "#e2e8f0",
44
+ },
45
+ removed: {
46
+ text: "Removed",
47
+ bg: "rgba(239, 68, 68, 0.18)",
48
+ border: "rgba(239, 68, 68, 0.45)",
49
+ color: "#fee2e2",
50
+ },
51
+ };
52
+ export function renderItemRequestOgImage(data, siteName) {
53
+ const { title, opDisplayName, budgetLabel, imageUrl, status, replyCount, category } = data;
54
+ const statusBadge = status ? STATUS_BADGE[status] : null;
55
+ const replyLabel = typeof replyCount === "number" && replyCount >= 0
56
+ ? `${replyCount} repl${replyCount === 1 ? "y" : "ies"}`
57
+ : null;
58
+ return (_jsxs("div", { style: {
59
+ display: "flex",
60
+ width: "100%",
61
+ height: "100%",
62
+ background: "#0f172a",
63
+ fontFamily: "sans-serif",
64
+ position: "relative",
65
+ overflow: "hidden",
66
+ }, children: [imageUrl && (_jsx("img", { src: imageUrl, alt: "", style: {
67
+ position: "absolute",
68
+ inset: 0,
69
+ width: "100%",
70
+ height: "100%",
71
+ objectFit: "cover",
72
+ opacity: 0.14,
73
+ } })), _jsx("div", { style: {
74
+ position: "absolute",
75
+ inset: 0,
76
+ background: "linear-gradient(135deg, rgba(15,23,42,0.95) 0%, rgba(15,23,42,0.8) 100%)",
77
+ } }), _jsxs("div", { style: {
78
+ position: "relative",
79
+ display: "flex",
80
+ flexDirection: "column",
81
+ gap: 22,
82
+ padding: "72px",
83
+ width: "100%",
84
+ height: "100%",
85
+ justifyContent: "flex-end",
86
+ }, children: [_jsxs("div", { style: {
87
+ fontSize: 18,
88
+ color: "#22d3ee",
89
+ fontWeight: 600,
90
+ letterSpacing: 2,
91
+ textTransform: "uppercase",
92
+ }, children: [siteName, " \u00B7 Wanted"] }), _jsx("div", { style: {
93
+ fontSize: 64,
94
+ fontWeight: 800,
95
+ color: "#f1f5f9",
96
+ lineHeight: 1.1,
97
+ display: "-webkit-box",
98
+ WebkitLineClamp: 3,
99
+ WebkitBoxOrient: "vertical",
100
+ overflow: "hidden",
101
+ letterSpacing: -1,
102
+ }, children: title }), (opDisplayName || category) && (_jsxs("div", { style: { fontSize: 22, color: "#94a3b8" }, children: [opDisplayName ? `by ${opDisplayName}` : "", opDisplayName && category ? " · " : "", category ?? ""] })), _jsxs("div", { style: {
103
+ display: "flex",
104
+ flexDirection: "row",
105
+ gap: 12,
106
+ marginTop: 4,
107
+ flexWrap: "wrap",
108
+ }, children: [statusBadge && (_jsx("div", { style: {
109
+ display: "inline-flex",
110
+ padding: "10px 22px",
111
+ borderRadius: 999,
112
+ background: statusBadge.bg,
113
+ color: statusBadge.color,
114
+ fontSize: 22,
115
+ fontWeight: 700,
116
+ border: `1px solid ${statusBadge.border}`,
117
+ }, children: statusBadge.text })), budgetLabel && (_jsx("div", { style: {
118
+ display: "inline-flex",
119
+ padding: "10px 22px",
120
+ borderRadius: 999,
121
+ background: "rgba(34, 211, 238, 0.18)",
122
+ color: "#cffafe",
123
+ fontSize: 22,
124
+ fontWeight: 700,
125
+ border: "1px solid rgba(34, 211, 238, 0.4)",
126
+ }, children: budgetLabel })), replyLabel && (_jsx("div", { style: {
127
+ display: "inline-flex",
128
+ padding: "10px 22px",
129
+ borderRadius: 999,
130
+ background: "rgba(148, 163, 184, 0.18)",
131
+ color: "#e2e8f0",
132
+ fontSize: 22,
133
+ fontWeight: 600,
134
+ border: "1px solid rgba(148, 163, 184, 0.35)",
135
+ }, children: replyLabel }))] })] })] }));
136
+ }
@@ -0,0 +1,5 @@
1
+ import type { ConversationDocument } from "../../../../features/messages/schemas/firestore";
2
+ export interface StartLiveConversationInput {
3
+ productId: string;
4
+ }
5
+ export declare function startLiveConversationAction(input: StartLiveConversationInput): Promise<ConversationDocument>;
@@ -0,0 +1,24 @@
1
+ "use server";
2
+ import { conversationsRepository } from "../../../../features/messages/repository/conversations.repository";
3
+ import { requireRoleUser } from "../../../../providers/auth-firebase/helpers";
4
+ import { storeRepository, productRepository } from "../../../../repositories";
5
+ export async function startLiveConversationAction(input) {
6
+ const user = await requireRoleUser(["user", "buyer", "seller", "admin"]);
7
+ const product = await productRepository.findByIdOrSlug(input.productId);
8
+ if (!product || product.listingType !== "live") {
9
+ throw new Error("Product not found or not a live-item listing");
10
+ }
11
+ const store = await storeRepository.findById(product.storeId);
12
+ if (!store) {
13
+ throw new Error("Store not found");
14
+ }
15
+ return conversationsRepository.findOrCreateByContext({
16
+ buyerId: user.uid,
17
+ buyerDisplayName: user.name ?? user.email ?? user.uid,
18
+ storeId: product.storeId,
19
+ storeName: store.storeName,
20
+ sellerDisplayName: store.storeName,
21
+ productId: product.id,
22
+ productTitle: product.title,
23
+ });
24
+ }
@@ -1,4 +1,5 @@
1
1
  export * from "./data";
2
2
  export * from "./adapters";
3
+ export * from "./actions";
3
4
  export * from "./metadata";
4
5
  export * from "./og";
@@ -1,4 +1,5 @@
1
1
  export * from "./data";
2
2
  export * from "./adapters";
3
+ export * from "./actions";
3
4
  export * from "./metadata";
4
5
  export * from "./og";
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { renderOgLayout } from "../seo/og-layout";
2
2
  import { resolveOgImageUrl } from "../seo/og";
3
3
  export function renderLiveItemOg(doc, opts) {
4
4
  const priceLabel = doc?.price != null
@@ -16,56 +16,14 @@ export function renderLiveItemOg(doc, opts) {
16
16
  }, opts.siteName);
17
17
  }
18
18
  export function renderLiveItemOgImage(data, siteName) {
19
- const { title, species, priceLabel, imageUrl } = data;
20
- return (_jsxs("div", { style: {
21
- display: "flex",
22
- width: "100%",
23
- height: "100%",
24
- background: "#052e16",
25
- fontFamily: "sans-serif",
26
- position: "relative",
27
- overflow: "hidden",
28
- }, children: [imageUrl && (_jsx("img", { src: imageUrl, alt: "", style: {
29
- position: "absolute",
30
- inset: 0,
31
- width: "100%",
32
- height: "100%",
33
- objectFit: "cover",
34
- opacity: 0.15,
35
- } })), _jsx("div", { style: {
36
- position: "absolute",
37
- inset: 0,
38
- background: "linear-gradient(135deg, rgba(5,46,22,0.96) 0%, rgba(5,46,22,0.82) 100%)",
39
- } }), _jsxs("div", { style: {
40
- position: "relative",
41
- display: "flex",
42
- width: "100%",
43
- height: "100%",
44
- padding: "60px",
45
- gap: "48px",
46
- alignItems: "center",
47
- }, children: [imageUrl && (_jsx("img", { src: imageUrl, alt: title, style: {
48
- width: 400,
49
- height: 400,
50
- objectFit: "contain",
51
- borderRadius: 16,
52
- flexShrink: 0,
53
- } })), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 20, flex: 1 }, children: [_jsxs("div", { style: {
54
- fontSize: 18,
55
- color: "#86efac",
56
- fontWeight: 600,
57
- letterSpacing: 2,
58
- textTransform: "uppercase",
59
- }, children: [siteName, " \u00B7 Live Listing"] }), _jsx("div", { style: {
60
- fontSize: imageUrl ? 44 : 56,
61
- fontWeight: 700,
62
- color: "#f1f5f9",
63
- lineHeight: 1.2,
64
- display: "-webkit-box",
65
- WebkitLineClamp: 3,
66
- WebkitBoxOrient: "vertical",
67
- overflow: "hidden",
68
- }, children: title }), species && (_jsx("div", { style: { fontSize: 24, color: "#86efac", fontStyle: "italic" }, children: species })), priceLabel && (_jsx("div", { style: { fontSize: 32, fontWeight: 700, color: "#4ade80" }, children: priceLabel }))] })] })] }));
19
+ return renderOgLayout({
20
+ title: data.title,
21
+ subtitle: data.species ?? undefined,
22
+ imageUrl: data.imageUrl,
23
+ siteName: `${siteName} · Live Listing`,
24
+ accentSlot: data.priceLabel,
25
+ theme: { background: "#052e16", accentColor: "#86efac" },
26
+ });
69
27
  }
70
28
  /** Type-safe overload that accepts the full ProductDocument. */
71
29
  export function renderLiveItemOgFromDoc(doc, opts) {
@@ -10,7 +10,22 @@
10
10
  * import from any server route.
11
11
  */
12
12
  import { classifyMime, MIME_TO_EXT, } from "../../../shared/media/limits";
13
- import { generateMediaFilename, } from "../../../../utils/id-generators";
13
+ import { generateMediaFilename, validateMediaFilename, } from "../../../../utils/id-generators";
14
+ /**
15
+ * W1-51 — validate the generated filename before returning ok:true so a
16
+ * malformed name can never enter signed-URL storage. Generators producing
17
+ * unexpected output fail-closed with a 500.
18
+ */
19
+ function okWithFilename(ctx, filename, ext) {
20
+ if (validateMediaFilename(filename))
21
+ return { ok: true, filename, ext };
22
+ return {
23
+ ok: false,
24
+ status: 500,
25
+ error: "Generated media filename failed validation",
26
+ details: { context: ctx.type, filename },
27
+ };
28
+ }
14
29
  // Per-context limits — mirrored from the previous inline constants in
15
30
  // /api/media/upload. Adjust here, not at the call site.
16
31
  export const CONTEXT_LIMITS = {
@@ -183,7 +198,7 @@ export function applyMediaContextGuards({ detectedMime, context: ctx, }) {
183
198
  details: { context: ctx.type, detected: detectedMime },
184
199
  };
185
200
  }
186
- return { ok: true, filename: generateMediaFilename(ctx), ext };
201
+ return okWithFilename(ctx, generateMediaFilename(ctx), ext);
187
202
  }
188
203
  if (isImageOrPdfContext(ctx)) {
189
204
  // shipping-proof and refund-proof accept images or PDFs, not video.
@@ -194,7 +209,7 @@ export function applyMediaContextGuards({ detectedMime, context: ctx, }) {
194
209
  error: `${ctx.type} must be an image or PDF, not a video`,
195
210
  };
196
211
  }
197
- return { ok: true, filename: generateMediaFilename(ctx), ext };
212
+ return okWithFilename(ctx, generateMediaFilename(ctx), ext);
198
213
  }
199
214
  if (isPdf) {
200
215
  return {
@@ -204,9 +219,5 @@ export function applyMediaContextGuards({ detectedMime, context: ctx, }) {
204
219
  details: { context: ctx.type, detected: detectedMime },
205
220
  };
206
221
  }
207
- return {
208
- ok: true,
209
- filename: generateMediaFilename({ ...ctx, ext }),
210
- ext,
211
- };
222
+ return okWithFilename(ctx, generateMediaFilename({ ...ctx, ext }), ext);
212
223
  }
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { renderOgLayout } from "../seo/og-layout";
2
2
  import { resolveOgImageUrl } from "../seo/og";
3
3
  /** High-level OG renderer — accepts the raw pre-order document from `getPreOrderForDetail`. */
4
4
  export function renderPreOrderOg(doc, opts) {
@@ -16,35 +16,11 @@ export function renderPreOrderOg(doc, opts) {
16
16
  }, opts.siteName);
17
17
  }
18
18
  export function renderPreOrderOgImage(data, siteName) {
19
- const { title, releaseDateLabel, imageUrl } = data;
20
- return (_jsxs("div", { style: {
21
- display: "flex",
22
- width: "100%",
23
- height: "100%",
24
- background: "#0f172a",
25
- fontFamily: "sans-serif",
26
- position: "relative",
27
- overflow: "hidden",
28
- }, children: [imageUrl && (_jsx("img", { src: imageUrl, alt: "", style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", opacity: 0.15 } })), _jsx("div", { style: {
29
- position: "absolute",
30
- inset: 0,
31
- background: "linear-gradient(135deg, rgba(15,23,42,0.95) 0%, rgba(15,23,42,0.8) 100%)",
32
- } }), _jsxs("div", { style: {
33
- position: "relative",
34
- display: "flex",
35
- width: "100%",
36
- height: "100%",
37
- padding: "60px",
38
- gap: "48px",
39
- alignItems: "center",
40
- }, children: [imageUrl && (_jsx("img", { src: imageUrl, alt: title, style: { width: 420, height: 420, objectFit: "contain", borderRadius: 16, flexShrink: 0 } })), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 24, flex: 1 }, children: [_jsxs("div", { style: { fontSize: 20, color: "#818cf8", fontWeight: 600, letterSpacing: 2, textTransform: "uppercase" }, children: [siteName, " \u00B7 Pre-Order"] }), _jsx("div", { style: {
41
- fontSize: imageUrl ? 44 : 56,
42
- fontWeight: 700,
43
- color: "#f1f5f9",
44
- lineHeight: 1.2,
45
- display: "-webkit-box",
46
- WebkitLineClamp: 3,
47
- WebkitBoxOrient: "vertical",
48
- overflow: "hidden",
49
- }, children: title }), releaseDateLabel && (_jsx("div", { style: { fontSize: 28, fontWeight: 600, color: "#818cf8" }, children: releaseDateLabel }))] })] })] }));
19
+ return renderOgLayout({
20
+ title: data.title,
21
+ imageUrl: data.imageUrl,
22
+ siteName: `${siteName} · Pre-Order`,
23
+ accentSlot: data.releaseDateLabel,
24
+ theme: { accentColor: "#818cf8" },
25
+ });
50
26
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Prize Draw data layer — OG-coverage-followup 2026-05-23.
3
+ *
4
+ * Prize draws live as products with `listingType:"prize-draw"`. This file
5
+ * wraps the repo call in `React.cache` so the OG renderer and any future
6
+ * page/generateMetadata pair share one Firestore read per request.
7
+ */
8
+ import type { ProductDocument } from "../../../../features/products/schemas/firestore";
9
+ export interface PrizeDrawDataOptions {
10
+ _reserved?: never;
11
+ }
12
+ /**
13
+ * Fetch a single prize-draw product by slug or id. Returns null when the
14
+ * slug is empty or no matching product exists. The caller is responsible
15
+ * for asserting `listingType === "prize-draw"` if a strict gate is required.
16
+ */
17
+ export declare const getPrizeDrawForDetail: (slugOrId: string, _opts?: PrizeDrawDataOptions) => Promise<ProductDocument | null>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Prize Draw data layer — OG-coverage-followup 2026-05-23.
3
+ *
4
+ * Prize draws live as products with `listingType:"prize-draw"`. This file
5
+ * wraps the repo call in `React.cache` so the OG renderer and any future
6
+ * page/generateMetadata pair share one Firestore read per request.
7
+ */
8
+ import { cache } from "react";
9
+ import { productRepository } from "../../../../repositories";
10
+ /**
11
+ * Fetch a single prize-draw product by slug or id. Returns null when the
12
+ * slug is empty or no matching product exists. The caller is responsible
13
+ * for asserting `listingType === "prize-draw"` if a strict gate is required.
14
+ */
15
+ export const getPrizeDrawForDetail = cache(async (slugOrId, _opts) => {
16
+ void _opts;
17
+ if (!slugOrId)
18
+ return null;
19
+ return (await productRepository.findByIdOrSlug(slugOrId).catch(() => null)) ?? null;
20
+ });
@@ -0,0 +1,2 @@
1
+ export { getPrizeDrawForDetail, type PrizeDrawDataOptions } from "./data";
2
+ export { renderPrizeDrawOg, renderPrizeDrawOgImage, type PrizeDrawOgData, } from "./og";
@@ -0,0 +1,2 @@
1
+ export { getPrizeDrawForDetail } from "./data";
2
+ export { renderPrizeDrawOg, renderPrizeDrawOgImage, } from "./og";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Prize Draw OG image renderer — OG-coverage-followup 2026-05-23.
3
+ * Migrated to renderOgLayout factory 2026-05-24 (Session 5 W1-1).
4
+ */
5
+ import type { ReactElement } from "react";
6
+ export interface PrizeDrawOgData {
7
+ title: string;
8
+ pricePerEntryLabel?: string | null;
9
+ imageUrl?: string | null;
10
+ revealStatus?: "pending" | "open" | "closed" | null;
11
+ entriesLabel?: string | null;
12
+ }
13
+ interface PrizeDrawDocLike {
14
+ title?: string | null;
15
+ name?: string | null;
16
+ pricePerEntry?: number | null;
17
+ price?: number | null;
18
+ currency?: string | null;
19
+ mainImage?: string | null;
20
+ images?: (string | null | undefined)[] | null;
21
+ prizeRevealStatus?: "pending" | "open" | "closed" | null;
22
+ prizeMaxEntries?: number | null;
23
+ prizeCurrentEntries?: number | null;
24
+ }
25
+ export declare function renderPrizeDrawOg(doc: PrizeDrawDocLike | null | undefined, opts: {
26
+ siteName: string;
27
+ baseUrl?: string;
28
+ }): ReactElement;
29
+ export declare function renderPrizeDrawOgImage(data: PrizeDrawOgData, siteName: string): ReactElement;
30
+ export {};
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Prize Draw OG image renderer — OG-coverage-followup 2026-05-23.
3
+ * Migrated to renderOgLayout factory 2026-05-24 (Session 5 W1-1).
4
+ */
5
+ import { renderOgLayout } from "../seo/og-layout";
6
+ import { resolveOgImageUrl } from "../seo/og";
7
+ function formatPriceInr(paise, currency) {
8
+ return new Intl.NumberFormat("en-IN", {
9
+ style: "currency",
10
+ currency,
11
+ maximumFractionDigits: 0,
12
+ }).format(paise / 100);
13
+ }
14
+ export function renderPrizeDrawOg(doc, opts) {
15
+ const entryPaise = doc?.pricePerEntry ?? doc?.price ?? null;
16
+ const pricePerEntryLabel = typeof entryPaise === "number" && entryPaise > 0
17
+ ? `${formatPriceInr(entryPaise, doc?.currency ?? "INR")} / entry`
18
+ : "Free entry";
19
+ const max = doc?.prizeMaxEntries ?? null;
20
+ const current = doc?.prizeCurrentEntries ?? 0;
21
+ const entriesLabel = typeof max === "number" && max > 0
22
+ ? `${Math.max(0, max - current).toLocaleString("en-IN")} of ${max.toLocaleString("en-IN")} entries left`
23
+ : null;
24
+ return renderPrizeDrawOgImage({
25
+ title: doc?.title ?? doc?.name ?? "Prize Draw",
26
+ pricePerEntryLabel,
27
+ imageUrl: resolveOgImageUrl(doc?.mainImage || doc?.images?.[0] || null, opts.baseUrl),
28
+ revealStatus: doc?.prizeRevealStatus ?? null,
29
+ entriesLabel,
30
+ }, opts.siteName);
31
+ }
32
+ const REVEAL_BADGE_TEXT = {
33
+ pending: "Reveal pending",
34
+ open: "Reveal open",
35
+ closed: "Reveal closed",
36
+ };
37
+ export function renderPrizeDrawOgImage(data, siteName) {
38
+ const badges = [
39
+ data.pricePerEntryLabel,
40
+ data.revealStatus ? REVEAL_BADGE_TEXT[data.revealStatus] : null,
41
+ ].filter((s) => Boolean(s));
42
+ return renderOgLayout({
43
+ title: data.title,
44
+ subtitle: data.entriesLabel ?? undefined,
45
+ imageUrl: data.imageUrl,
46
+ siteName: `${siteName} · Prize Draw`,
47
+ badges: badges.length > 0 ? badges : undefined,
48
+ theme: { accentColor: "#fbbf24" },
49
+ });
50
+ }
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { renderOgLayout } from "../seo/og-layout";
2
2
  import { resolveOgImageUrl } from "../seo/og";
3
3
  /** High-level OG renderer — accepts the raw product document from `getProductForDetail`. */
4
4
  export function renderProductOg(doc, opts) {
@@ -9,36 +9,11 @@ export function renderProductOg(doc, opts) {
9
9
  }, opts.siteName);
10
10
  }
11
11
  export function renderProductOgImage(data, siteName) {
12
- const { title, price, imageUrl } = data;
13
- const priceLabel = price ? `₹${(price / 100).toLocaleString("en-IN")}` : null;
14
- return (_jsxs("div", { style: {
15
- display: "flex",
16
- width: "100%",
17
- height: "100%",
18
- background: "#0f172a",
19
- fontFamily: "sans-serif",
20
- position: "relative",
21
- overflow: "hidden",
22
- }, children: [imageUrl && (_jsx("img", { src: imageUrl, alt: "", style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", opacity: 0.15 } })), _jsx("div", { style: {
23
- position: "absolute",
24
- inset: 0,
25
- background: "linear-gradient(135deg, rgba(15,23,42,0.95) 0%, rgba(15,23,42,0.8) 100%)",
26
- } }), _jsxs("div", { style: {
27
- position: "relative",
28
- display: "flex",
29
- width: "100%",
30
- height: "100%",
31
- padding: "60px",
32
- gap: "48px",
33
- alignItems: "center",
34
- }, children: [imageUrl && (_jsx("img", { src: imageUrl, alt: title, style: { width: 420, height: 420, objectFit: "contain", borderRadius: 16, flexShrink: 0 } })), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 24, flex: 1 }, children: [_jsx("div", { style: { fontSize: 20, color: "#84e122", fontWeight: 600, letterSpacing: 2, textTransform: "uppercase" }, children: siteName }), _jsx("div", { style: {
35
- fontSize: imageUrl ? 44 : 56,
36
- fontWeight: 700,
37
- color: "#f1f5f9",
38
- lineHeight: 1.2,
39
- display: "-webkit-box",
40
- WebkitLineClamp: 3,
41
- WebkitBoxOrient: "vertical",
42
- overflow: "hidden",
43
- }, children: title }), priceLabel && (_jsx("div", { style: { fontSize: 36, fontWeight: 800, color: "#84e122" }, children: priceLabel }))] })] })] }));
12
+ const priceLabel = data.price ? `₹${(data.price / 100).toLocaleString("en-IN")}` : null;
13
+ return renderOgLayout({
14
+ title: data.title,
15
+ imageUrl: data.imageUrl,
16
+ siteName,
17
+ accentSlot: priceLabel,
18
+ });
44
19
  }
@@ -0,0 +1,42 @@
1
+ import type { ReactElement, ReactNode } from "react";
2
+ /**
3
+ * `renderOgLayout` — W1-1 — single shared OG card renderer that the 18 per-feature
4
+ * `og.tsx` files can delegate to. Layout: dark hero with optional background image
5
+ * (15% opacity wash), large feature image on the left, title + subtitle + badges +
6
+ * site brand stacked on the right. Each feature builds its own thin adapter that
7
+ * pulls the right fields off its document and passes them as slots here.
8
+ *
9
+ * The signature is intentionally narrow — domain-specific decorations (price
10
+ * pill, countdown, score chip) flow in via the `badges` array or the optional
11
+ * `accentSlot` (e.g. price label, urgency message). Anything more bespoke
12
+ * should compose this layout from outside, not extend it.
13
+ */
14
+ export interface OgLayoutSlots {
15
+ /** Headline rendered at the largest type size. */
16
+ title: string;
17
+ /** Optional subtitle below the title. */
18
+ subtitle?: string;
19
+ /** Optional feature image rendered on the left (square contain-fit). */
20
+ imageUrl?: string | null;
21
+ /** Site brand label rendered as the eyebrow. */
22
+ siteName: string;
23
+ /** Optional badge chips rendered above the title. */
24
+ badges?: ReactNode[];
25
+ /** Optional accent line rendered below the title (price, countdown, etc.). */
26
+ accentSlot?: ReactNode;
27
+ /** Optional theme overrides — hex strings only (Satori doesn't accept CSS vars). */
28
+ theme?: Partial<OgLayoutTheme>;
29
+ }
30
+ export interface OgLayoutTheme {
31
+ /** Background base color. */
32
+ background: string;
33
+ /** Headline color. */
34
+ titleColor: string;
35
+ /** Subtitle / body text color. */
36
+ bodyColor: string;
37
+ /** Brand accent color used for the eyebrow + price. */
38
+ accentColor: string;
39
+ /** Gradient overlay (CSS gradient string). */
40
+ overlayGradient: string;
41
+ }
42
+ export declare function renderOgLayout(slots: OgLayoutSlots): ReactElement;
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ const DEFAULT_THEME = {
3
+ background: "#0f172a",
4
+ titleColor: "#f1f5f9",
5
+ bodyColor: "#cbd5e1",
6
+ accentColor: "#84e122",
7
+ overlayGradient: "linear-gradient(135deg, rgba(15,23,42,0.95) 0%, rgba(15,23,42,0.8) 100%)",
8
+ };
9
+ export function renderOgLayout(slots) {
10
+ const theme = { ...DEFAULT_THEME, ...(slots.theme ?? {}) };
11
+ const { title, subtitle, imageUrl, siteName, badges, accentSlot } = slots;
12
+ return (_jsxs("div", { style: {
13
+ display: "flex",
14
+ width: "100%",
15
+ height: "100%",
16
+ background: theme.background,
17
+ fontFamily: "sans-serif",
18
+ position: "relative",
19
+ overflow: "hidden",
20
+ }, children: [imageUrl ? (_jsx("img", { src: imageUrl, alt: "", style: {
21
+ position: "absolute",
22
+ inset: 0,
23
+ width: "100%",
24
+ height: "100%",
25
+ objectFit: "cover",
26
+ opacity: 0.15,
27
+ } })) : null, _jsx("div", { style: {
28
+ position: "absolute",
29
+ inset: 0,
30
+ background: theme.overlayGradient,
31
+ } }), _jsxs("div", { style: {
32
+ position: "relative",
33
+ display: "flex",
34
+ width: "100%",
35
+ height: "100%",
36
+ padding: "60px",
37
+ gap: "48px",
38
+ alignItems: "center",
39
+ }, children: [imageUrl ? (_jsx("img", { src: imageUrl, alt: title, style: {
40
+ width: 420,
41
+ height: 420,
42
+ objectFit: "contain",
43
+ borderRadius: 16,
44
+ flexShrink: 0,
45
+ } })) : null, _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 24, flex: 1 }, children: [_jsx("div", { style: {
46
+ fontSize: 20,
47
+ color: theme.accentColor,
48
+ fontWeight: 600,
49
+ letterSpacing: 2,
50
+ textTransform: "uppercase",
51
+ }, children: siteName }), badges && badges.length > 0 ? (_jsx("div", { style: { display: "flex", gap: 12, flexWrap: "wrap" }, children: badges.map((b, i) => (_jsx("div", { style: {
52
+ display: "flex",
53
+ padding: "8px 16px",
54
+ borderRadius: 9999,
55
+ background: "rgba(255,255,255,0.1)",
56
+ color: theme.bodyColor,
57
+ fontSize: 18,
58
+ fontWeight: 600,
59
+ }, children: b }, i))) })) : null, _jsx("div", { style: {
60
+ fontSize: imageUrl ? 44 : 56,
61
+ fontWeight: 700,
62
+ color: theme.titleColor,
63
+ lineHeight: 1.2,
64
+ display: "-webkit-box",
65
+ WebkitLineClamp: 3,
66
+ WebkitBoxOrient: "vertical",
67
+ overflow: "hidden",
68
+ }, children: title }), accentSlot ? (_jsx("div", { style: { display: "flex", fontSize: 36, fontWeight: 800, color: theme.accentColor }, children: accentSlot })) : null, subtitle ? (_jsx("div", { style: { display: "flex", fontSize: 24, color: theme.bodyColor }, children: subtitle })) : null] })] })] }));
69
+ }