@mohasinac/appkit 2.7.27 → 2.7.30

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 (359) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
  2. package/dist/_internal/server/features/checkout/actions.js +42 -35
  3. package/dist/_internal/server/features/classified/adapters.d.ts +21 -0
  4. package/dist/_internal/server/features/classified/adapters.js +24 -0
  5. package/dist/_internal/server/features/classified/index.d.ts +3 -0
  6. package/dist/_internal/server/features/classified/index.js +3 -0
  7. package/dist/_internal/server/features/classified/metadata.d.ts +8 -0
  8. package/dist/_internal/server/features/classified/metadata.js +43 -0
  9. package/dist/_internal/server/features/classified/og.d.ts +30 -0
  10. package/dist/_internal/server/features/classified/og.js +76 -0
  11. package/dist/_internal/server/features/digital-code/adapters.d.ts +21 -0
  12. package/dist/_internal/server/features/digital-code/adapters.js +29 -0
  13. package/dist/_internal/server/features/digital-code/index.d.ts +3 -0
  14. package/dist/_internal/server/features/digital-code/index.js +3 -0
  15. package/dist/_internal/server/features/digital-code/metadata.d.ts +8 -0
  16. package/dist/_internal/server/features/digital-code/metadata.js +36 -0
  17. package/dist/_internal/server/features/digital-code/og.d.ts +27 -0
  18. package/dist/_internal/server/features/digital-code/og.js +77 -0
  19. package/dist/_internal/server/features/live/adapters.d.ts +21 -0
  20. package/dist/_internal/server/features/live/adapters.js +34 -0
  21. package/dist/_internal/server/features/live/index.d.ts +3 -0
  22. package/dist/_internal/server/features/live/index.js +3 -0
  23. package/dist/_internal/server/features/live/metadata.d.ts +8 -0
  24. package/dist/_internal/server/features/live/metadata.js +39 -0
  25. package/dist/_internal/server/features/live/og.d.ts +27 -0
  26. package/dist/_internal/server/features/live/og.js +72 -0
  27. package/dist/_internal/server/features/promotions/actions.js +4 -3
  28. package/dist/_internal/server/jobs/core/onOrderCreate.js +19 -16
  29. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +25 -22
  30. package/dist/_internal/server/jobs/core/onProductWrite.js +48 -58
  31. package/dist/_internal/shared/actions/action-registry.js +310 -8
  32. package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
  33. package/dist/_internal/shared/features/brands/schema.d.ts +5 -5
  34. package/dist/_internal/shared/features/cart/schema.d.ts +18 -18
  35. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
  36. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +38 -38
  37. package/dist/_internal/shared/features/events/schema.d.ts +8 -8
  38. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  39. package/dist/_internal/shared/features/products/schema.d.ts +42 -42
  40. package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
  41. package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
  42. package/dist/_internal/shared/media/limits.js +4 -3
  43. package/dist/client.d.ts +2 -0
  44. package/dist/client.js +1 -0
  45. package/dist/configs/next.js +4 -0
  46. package/dist/constants/table-keys.d.ts +6 -0
  47. package/dist/constants/table-keys.js +9 -0
  48. package/dist/core/hooks/useSyncManager.js +9 -8
  49. package/dist/features/about/components/FeesView.js +10 -7
  50. package/dist/features/about/components/HelpPageView.js +10 -1
  51. package/dist/features/about/components/HowAuctionsWorkView.js +10 -1
  52. package/dist/features/about/components/HowCheckoutWorksView.js +10 -1
  53. package/dist/features/about/components/HowOffersWorkView.js +10 -1
  54. package/dist/features/about/components/HowOrdersWorkView.js +10 -1
  55. package/dist/features/about/components/HowPayoutsWorkView.js +10 -1
  56. package/dist/features/about/components/HowPreOrdersWorkView.js +10 -1
  57. package/dist/features/about/components/HowReviewsWorkView.js +10 -1
  58. package/dist/features/about/components/PublicProfileView.js +44 -14
  59. package/dist/features/about/components/SecurityPrivacyView.js +7 -1
  60. package/dist/features/account/components/AddressesIndexListing.js +2 -2
  61. package/dist/features/account/components/NotificationBell.js +27 -16
  62. package/dist/features/account/components/NotificationPreferencesPanel.d.ts +9 -0
  63. package/dist/features/account/components/NotificationPreferencesPanel.js +91 -0
  64. package/dist/features/account/components/UserSidebar.js +4 -4
  65. package/dist/features/account/components/UserSupportView.js +16 -6
  66. package/dist/features/account/components/index.d.ts +2 -0
  67. package/dist/features/account/components/index.js +1 -0
  68. package/dist/features/account/schemas/index.d.ts +4 -4
  69. package/dist/features/account/types/index.d.ts +34 -0
  70. package/dist/features/admin/actions/admin-actions.js +4 -3
  71. package/dist/features/admin/actions/notification-actions.d.ts +24 -22
  72. package/dist/features/admin/actions/notification-actions.js +122 -22
  73. package/dist/features/admin/components/AdminAdEditorView.js +29 -15
  74. package/dist/features/admin/components/AdminAdsView.js +22 -28
  75. package/dist/features/admin/components/AdminAllEventEntriesView.js +6 -1
  76. package/dist/features/admin/components/AdminBidsView.js +10 -3
  77. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  78. package/dist/features/admin/components/AdminBlogView.js +8 -3
  79. package/dist/features/admin/components/AdminBrandsView.js +7 -2
  80. package/dist/features/admin/components/AdminBundleEditorView.js +5 -2
  81. package/dist/features/admin/components/AdminCarouselEditorView.js +6 -3
  82. package/dist/features/admin/components/AdminCarouselView.js +13 -7
  83. package/dist/features/admin/components/AdminCategoriesView.js +8 -3
  84. package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
  85. package/dist/features/admin/components/AdminContactView.js +6 -1
  86. package/dist/features/admin/components/AdminCouponEditorView.js +8 -2
  87. package/dist/features/admin/components/AdminCouponsView.js +6 -1
  88. package/dist/features/admin/components/AdminEmployeeEditorView.js +12 -9
  89. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  90. package/dist/features/admin/components/AdminFaqsView.js +7 -2
  91. package/dist/features/admin/components/AdminFeaturesView.js +4 -3
  92. package/dist/features/admin/components/AdminMediaView.js +9 -8
  93. package/dist/features/admin/components/AdminNavigationView.js +10 -17
  94. package/dist/features/admin/components/AdminNewsletterView.js +6 -1
  95. package/dist/features/admin/components/AdminNotificationsView.js +11 -3
  96. package/dist/features/admin/components/AdminOrdersView.js +6 -1
  97. package/dist/features/admin/components/AdminPayoutsView.js +10 -3
  98. package/dist/features/admin/components/AdminPrizeDrawsView.js +10 -3
  99. package/dist/features/admin/components/AdminProductsView.js +18 -12
  100. package/dist/features/admin/components/AdminReviewsView.js +33 -24
  101. package/dist/features/admin/components/AdminScammerEditorView.js +3 -2
  102. package/dist/features/admin/components/AdminScammersView.js +28 -26
  103. package/dist/features/admin/components/AdminSectionsView.js +112 -99
  104. package/dist/features/admin/components/AdminSessionsView.js +10 -3
  105. package/dist/features/admin/components/AdminSidebar.js +4 -4
  106. package/dist/features/admin/components/AdminSiteSettingsView.js +45 -4
  107. package/dist/features/admin/components/AdminStoresView.js +6 -1
  108. package/dist/features/admin/components/AdminSublistingCategoriesView.js +0 -1
  109. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  110. package/dist/features/admin/components/AdminSupportTicketsView.js +32 -34
  111. package/dist/features/admin/components/AdminTeamView.js +6 -1
  112. package/dist/features/admin/components/AdminUserEditorView.js +8 -15
  113. package/dist/features/admin/components/AdminUsersView.js +6 -1
  114. package/dist/features/admin/components/AdminViewCards.js +4 -3
  115. package/dist/features/admin/components/DashboardStats.js +2 -2
  116. package/dist/features/admin/components/DataTable.js +2 -2
  117. package/dist/features/admin/components/sections/adminSectionsTypes.js +4 -3
  118. package/dist/features/admin/repository/chat.repository.js +8 -8
  119. package/dist/features/admin/repository/site-settings.repository.d.ts +1 -0
  120. package/dist/features/admin/repository/site-settings.repository.js +11 -9
  121. package/dist/features/admin/schemas/firestore.d.ts +38 -0
  122. package/dist/features/admin/schemas/firestore.js +15 -0
  123. package/dist/features/auctions/components/AuctionDetailPageView.js +13 -19
  124. package/dist/features/auctions/components/MarketplaceAuctionCard.js +18 -9
  125. package/dist/features/auctions/components/MarketplaceAuctionGrid.js +3 -2
  126. package/dist/features/auctions/components/index.d.ts +0 -1
  127. package/dist/features/auctions/components/index.js +0 -1
  128. package/dist/features/auctions/schemas/index.d.ts +14 -14
  129. package/dist/features/auth/actions/realtime-token-actions.js +19 -4
  130. package/dist/features/auth/components/RegisterForm.js +8 -4
  131. package/dist/features/auth/consent-otp.d.ts +2 -0
  132. package/dist/features/auth/consent-otp.js +8 -0
  133. package/dist/features/auth/schemas/firestore.d.ts +2 -0
  134. package/dist/features/auth/schemas/firestore.js +18 -25
  135. package/dist/features/auth/schemas/index.d.ts +2 -2
  136. package/dist/features/before-after/api/[id]/route.js +8 -6
  137. package/dist/features/before-after/schemas/index.d.ts +2 -2
  138. package/dist/features/blog/actions/blog-actions.d.ts +3 -3
  139. package/dist/features/blog/components/BlogIndexListing.js +21 -2
  140. package/dist/features/blog/components/BlogPostForm.js +11 -5
  141. package/dist/features/blog/components/BlogPostView.js +10 -2
  142. package/dist/features/blog/schemas/index.d.ts +18 -18
  143. package/dist/features/cart/components/CartDrawer.js +3 -2
  144. package/dist/features/cart/repository/cart.repository.js +4 -3
  145. package/dist/features/cart/utils/pending-ops.d.ts +1 -1
  146. package/dist/features/categories/api/[id]/route.js +4 -3
  147. package/dist/features/categories/api/route.js +4 -3
  148. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  149. package/dist/features/categories/components/BrandDetailTabs.js +2 -2
  150. package/dist/features/categories/components/BundleCollage.d.ts +8 -0
  151. package/dist/features/categories/components/BundleCollage.js +50 -0
  152. package/dist/features/categories/components/BundleDetailView.js +2 -10
  153. package/dist/features/categories/components/BundleItemsPicker.js +20 -13
  154. package/dist/features/categories/components/CategoriesIndexListing.js +28 -9
  155. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  156. package/dist/features/categories/components/CategoryDetailTabs.js +2 -2
  157. package/dist/features/categories/components/CategoryProductsListing.js +52 -13
  158. package/dist/features/categories/components/index.d.ts +2 -0
  159. package/dist/features/categories/components/index.js +1 -0
  160. package/dist/features/categories/schemas/index.d.ts +6 -6
  161. package/dist/features/checkout/actions/checkout-actions.js +4 -3
  162. package/dist/features/checkout/hooks/usePaymentCheckout.js +31 -22
  163. package/dist/features/classified/components/ClassifiedFilters.d.ts +8 -0
  164. package/dist/features/classified/components/ClassifiedFilters.js +19 -0
  165. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +4 -0
  166. package/dist/features/classified/components/ClassifiedIndexListing.js +110 -0
  167. package/dist/features/classified/components/ClassifiedListView.d.ts +6 -0
  168. package/dist/features/classified/components/ClassifiedListView.js +43 -0
  169. package/dist/features/classified/components/index.d.ts +6 -0
  170. package/dist/features/classified/components/index.js +3 -0
  171. package/dist/features/collections/api/[slug]/route.js +16 -12
  172. package/dist/features/collections/schemas/index.d.ts +5 -5
  173. package/dist/features/consultation/api/[id]/route.js +12 -9
  174. package/dist/features/consultation/components/ConsultationForm.js +3 -2
  175. package/dist/features/consultation/schemas/index.d.ts +6 -6
  176. package/dist/features/contact/email.js +77 -116
  177. package/dist/features/copilot/components/AdminCopilotView.js +12 -2
  178. package/dist/features/corporate/api/[id]/route.js +12 -9
  179. package/dist/features/corporate/components/CorporateInquiryForm.js +2 -1
  180. package/dist/features/digital-codes/components/DigitalCodeFilters.d.ts +8 -0
  181. package/dist/features/digital-codes/components/DigitalCodeFilters.js +16 -0
  182. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +4 -0
  183. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +107 -0
  184. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +6 -0
  185. package/dist/features/digital-codes/components/DigitalCodesListView.js +37 -0
  186. package/dist/features/digital-codes/components/index.d.ts +6 -0
  187. package/dist/features/digital-codes/components/index.js +3 -0
  188. package/dist/features/events/actions/event-actions.js +4 -3
  189. package/dist/features/events/components/AdminEventEditorView.js +3 -2
  190. package/dist/features/events/components/AdminEventsView.js +4 -2
  191. package/dist/features/events/components/EventPollWidget.js +5 -5
  192. package/dist/features/events/components/EventsIndexListing.js +2 -2
  193. package/dist/features/events/schemas/index.d.ts +20 -20
  194. package/dist/features/faq/actions/faq-actions.d.ts +8 -8
  195. package/dist/features/faq/components/ContactCTA.js +3 -1
  196. package/dist/features/faq/components/FAQAccordion.js +1 -1
  197. package/dist/features/faq/schemas/index.d.ts +4 -4
  198. package/dist/features/filters/AsyncFacetSection.d.ts +18 -0
  199. package/dist/features/filters/AsyncFacetSection.js +65 -0
  200. package/dist/features/forms/Checkbox.js +2 -2
  201. package/dist/features/forms/Input.js +2 -2
  202. package/dist/features/forms/Radio.js +5 -5
  203. package/dist/features/forms/Select.js +2 -2
  204. package/dist/features/forms/Slider.js +2 -2
  205. package/dist/features/forms/Textarea.js +2 -2
  206. package/dist/features/homepage/api/[id]/route.js +4 -3
  207. package/dist/features/homepage/api/sections/[id]/route.js +12 -9
  208. package/dist/features/homepage/components/AdSlot.js +2 -1
  209. package/dist/features/homepage/components/AdvertisementBanner.js +5 -3
  210. package/dist/features/homepage/components/BrandsSection.js +2 -2
  211. package/dist/features/homepage/components/CTABannerSection.js +2 -2
  212. package/dist/features/homepage/components/CharacterHotspot.d.ts +2 -2
  213. package/dist/features/homepage/components/CharacterHotspot.js +119 -157
  214. package/dist/features/homepage/components/CharacterHotspotForm.js +25 -20
  215. package/dist/features/homepage/components/CustomCardsSection.js +8 -6
  216. package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
  217. package/dist/features/homepage/components/GoogleReviewsSection.js +3 -3
  218. package/dist/features/homepage/components/HeroBanner.js +5 -4
  219. package/dist/features/homepage/components/HeroCarousel.js +13 -26
  220. package/dist/features/homepage/components/HowItWorksSection.js +4 -2
  221. package/dist/features/homepage/components/SocialFeedSection.js +2 -2
  222. package/dist/features/homepage/components/SocialPostCard.js +3 -2
  223. package/dist/features/homepage/lib/social-feed-fetcher.js +6 -3
  224. package/dist/features/layout/AppLayoutShell.d.ts +8 -2
  225. package/dist/features/layout/AppLayoutShell.js +54 -78
  226. package/dist/features/layout/BottomActions.js +43 -48
  227. package/dist/features/live/components/LiveItemFilters.d.ts +8 -0
  228. package/dist/features/live/components/LiveItemFilters.js +23 -0
  229. package/dist/features/live/components/LiveItemsIndexListing.d.ts +4 -0
  230. package/dist/features/live/components/LiveItemsIndexListing.js +108 -0
  231. package/dist/features/live/components/LiveItemsListView.d.ts +6 -0
  232. package/dist/features/live/components/LiveItemsListView.js +43 -0
  233. package/dist/features/live/components/index.d.ts +6 -0
  234. package/dist/features/live/components/index.js +3 -0
  235. package/dist/features/media/MediaLightbox.js +8 -14
  236. package/dist/features/media/types/index.d.ts +2 -2
  237. package/dist/features/media/upload/CameraCapture.js +2 -2
  238. package/dist/features/media/upload/ImageUpload.js +4 -2
  239. package/dist/features/media/upload/MediaUploadField.js +24 -8
  240. package/dist/features/media/upload/MediaUploadList.js +1 -1
  241. package/dist/features/orders/actions/order-actions.js +4 -3
  242. package/dist/features/orders/components/MarketplaceOrderCard.js +2 -1
  243. package/dist/features/orders/components/OrderFilters.js +12 -9
  244. package/dist/features/orders/schemas/index.d.ts +6 -6
  245. package/dist/features/pre-orders/api/[id]/route.js +12 -9
  246. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +5 -4
  247. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +27 -20
  248. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +17 -52
  249. package/dist/features/pre-orders/components/PreorderCard.d.ts +0 -9
  250. package/dist/features/pre-orders/components/PreorderCard.js +2 -13
  251. package/dist/features/pre-orders/components/index.d.ts +1 -1
  252. package/dist/features/pre-orders/components/index.js +1 -1
  253. package/dist/features/pre-orders/schemas/index.d.ts +10 -10
  254. package/dist/features/products/api/[id]/route.js +9 -7
  255. package/dist/features/products/api/route.js +1 -1
  256. package/dist/features/products/columns/productTableColumns.js +2 -2
  257. package/dist/features/products/components/AuctionsIndexListing.js +15 -51
  258. package/dist/features/products/components/BidHistory.js +2 -2
  259. package/dist/features/products/components/CompareOverlay.js +3 -2
  260. package/dist/features/products/components/MakeOfferButton.js +20 -14
  261. package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -1
  262. package/dist/features/products/components/PrizeDrawCollage.d.ts +1 -15
  263. package/dist/features/products/components/PrizeDrawCollage.js +54 -14
  264. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -1
  265. package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
  266. package/dist/features/products/components/PrizeDrawItemsEditor.js +9 -7
  267. package/dist/features/products/components/PrizeDrawsIndexListing.js +18 -51
  268. package/dist/features/products/components/PrizeRevealModal.js +24 -14
  269. package/dist/features/products/components/ProductDetailPageView.js +2 -1
  270. package/dist/features/products/components/ProductFilters.js +8 -6
  271. package/dist/features/products/components/ProductForm.js +12 -10
  272. package/dist/features/products/components/ProductGalleryClient.js +2 -1
  273. package/dist/features/products/components/ProductGrid.js +11 -14
  274. package/dist/features/products/components/ProductsIndexListing.js +16 -52
  275. package/dist/features/products/components/ProductsIndexPageView.js +2 -2
  276. package/dist/features/products/schemas/index.d.ts +56 -56
  277. package/dist/features/products/schemas/product-features.validators.d.ts +14 -14
  278. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  279. package/dist/features/promotions/schemas/index.d.ts +6 -6
  280. package/dist/features/reviews/actions/review-actions.js +6 -5
  281. package/dist/features/reviews/api/[id]/route.js +4 -3
  282. package/dist/features/reviews/components/ReviewDetailPageView.js +2 -2
  283. package/dist/features/reviews/components/ReviewDetailShell.js +6 -3
  284. package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
  285. package/dist/features/reviews/schemas/index.d.ts +6 -6
  286. package/dist/features/scams/components/ScamProfileView.js +19 -10
  287. package/dist/features/scams/components/ScamRegistryView.js +2 -1
  288. package/dist/features/search/components/Search.d.ts +1 -1
  289. package/dist/features/search/components/Search.js +56 -49
  290. package/dist/features/search/schemas/index.d.ts +5 -5
  291. package/dist/features/seller/actions/offer-actions.js +11 -9
  292. package/dist/features/seller/components/SellerAddressesView.js +6 -4
  293. package/dist/features/seller/components/SellerBidsView.js +2 -2
  294. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  295. package/dist/features/seller/components/SellerOffersPanel.js +6 -1
  296. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  297. package/dist/features/seller/components/SellerPayoutSettingsView.js +3 -3
  298. package/dist/features/seller/components/SellerPreOrdersView.js +5 -4
  299. package/dist/features/seller/components/SellerPrizeDrawsView.js +5 -4
  300. package/dist/features/seller/components/SellerProductShell.d.ts +19 -1
  301. package/dist/features/seller/components/SellerProductShell.js +75 -9
  302. package/dist/features/seller/components/SellerProductsView.js +10 -11
  303. package/dist/features/seller/components/SellerShippingView.js +3 -3
  304. package/dist/features/seller/components/SellerSidebar.js +10 -7
  305. package/dist/features/seller/components/SellerStorefrontView.js +3 -3
  306. package/dist/features/seller/schemas/index.d.ts +4 -4
  307. package/dist/features/shell/FormShell.js +2 -1
  308. package/dist/features/shell/QuickFormDrawer.js +4 -3
  309. package/dist/features/shell/StepForm.js +4 -3
  310. package/dist/features/stores/actions/store-query-actions.js +4 -3
  311. package/dist/features/stores/components/StoreAboutView.js +2 -1
  312. package/dist/features/stores/components/StoreAuctionsListing.js +43 -64
  313. package/dist/features/stores/components/StoreDetailLayoutView.js +2 -2
  314. package/dist/features/stores/components/StorePreOrdersListing.js +33 -57
  315. package/dist/features/stores/components/StoreProductsListing.js +16 -49
  316. package/dist/features/stores/components/StoresIndexListing.js +2 -2
  317. package/dist/features/stores/schemas/index.d.ts +2 -2
  318. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +8 -7
  319. package/dist/features/wishlist/hooks/useGuestWishlist.d.ts +5 -5
  320. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +6 -6
  321. package/dist/features/wishlist/utils/guest-wishlist.d.ts +5 -5
  322. package/dist/index.d.ts +21 -3
  323. package/dist/index.js +1737 -1725
  324. package/dist/monitoring/server-logger.js +5 -9
  325. package/dist/next/api/apiHandler.js +56 -37
  326. package/dist/next/components/GlobalError.js +2 -1
  327. package/dist/next/routing/route-map.d.ts +27 -0
  328. package/dist/next/routing/route-map.js +10 -0
  329. package/dist/react/ErrorBoundary.js +2 -1
  330. package/dist/react/contexts/SessionContext.js +38 -28
  331. package/dist/react/hooks/useCamera.d.ts +1 -1
  332. package/dist/react/hooks/useCamera.js +8 -7
  333. package/dist/seed/site-settings-seed-data.js +6 -0
  334. package/dist/server-entry.d.ts +3 -0
  335. package/dist/server-entry.js +3 -0
  336. package/dist/server.d.ts +12 -0
  337. package/dist/server.js +15 -0
  338. package/dist/tailwind-utilities.css +1 -1
  339. package/dist/ui/DataTable.js +28 -7
  340. package/dist/ui/components/Button.d.ts +8 -1
  341. package/dist/ui/components/Button.js +47 -8
  342. package/dist/ui/components/CollageLightbox.d.ts +18 -0
  343. package/dist/ui/components/CollageLightbox.js +41 -0
  344. package/dist/ui/components/Dropdown.js +7 -6
  345. package/dist/ui/components/ImageLightbox.d.ts +7 -1
  346. package/dist/ui/components/ImageLightbox.js +15 -5
  347. package/dist/ui/components/PageLoader.js +4 -3
  348. package/dist/ui/components/PaginatedMultiSelect.d.ts +28 -0
  349. package/dist/ui/components/PaginatedMultiSelect.js +141 -0
  350. package/dist/ui/components/PaginatedMultiSelect.style.css +272 -0
  351. package/dist/ui/components/Semantic.js +1 -1
  352. package/dist/ui/components/SideDrawer.js +17 -15
  353. package/dist/ui/components/index.style.css +1 -0
  354. package/dist/ui/index.d.ts +2 -0
  355. package/dist/ui/index.js +1 -0
  356. package/dist/utils/number.formatter.js +5 -13
  357. package/dist/utils/string.formatter.js +21 -18
  358. package/dist/validation/schemas.d.ts +2 -2
  359. package/package.json +1 -1
@@ -6,6 +6,11 @@ import { Button, Div, Heading, Input, Label, Li, Row, Span, Text, Textarea, Ul,
6
6
  function randomId() {
7
7
  return Math.random().toString(36).slice(2, 9);
8
8
  }
9
+ const CLS_STEP = "space-y-4 rounded-lg border-2 p-6";
10
+ const CLS_IMG_WRAP = "relative w-full overflow-hidden rounded-lg";
11
+ const CLS_INPUT = "rounded border-2 px-3 py-2 text-sm outline-none";
12
+ const STY_BORDER_INK = "2px solid var(--border-ink)";
13
+ const STY_CENTERED = "translate(-50%, -50%)";
9
14
  export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSave, }) {
10
15
  const [imageUrl, setImageUrl] = useState(initial?.imageUrl ?? "");
11
16
  const [imageAlt, setImageAlt] = useState(initial?.imageAlt ?? "DC, Marvel and Anime characters");
@@ -120,15 +125,15 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
120
125
  : "var(--color-black)",
121
126
  border: i <= stepIndex
122
127
  ? "2px solid var(--color-black)"
123
- : "2px solid var(--border-ink)",
128
+ : STY_BORDER_INK,
124
129
  }, children: i < stepIndex ? "✓" : i + 1 }), i < STEPS.length - 1 && (_jsx(Div, { className: "h-0.5 flex-1", style: {
125
130
  background: i < stepIndex
126
131
  ? "var(--color-black)"
127
132
  : "var(--border-ink)",
128
- } }))] }), _jsx(Span, { className: "mt-1 text-center text-[10px] font-medium", style: { color: "var(--color-muted)" }, children: s.label })] }, s.key))) }), step === "image" && (_jsxs(Div, { className: "space-y-4 rounded-lg border-2 p-6", style: {
133
+ } }))] }), _jsx(Span, { className: "mt-1 text-center text-[10px] font-medium", style: { color: "var(--color-muted)" }, children: s.label })] }, s.key))) }), step === "image" && (_jsxs(Div, { className: CLS_STEP, style: {
129
134
  borderColor: "var(--border-ink)",
130
135
  background: "var(--surface-elevated)",
131
- }, children: [_jsx(Heading, { level: 2, className: "text-lg font-bold", children: "Upload Background Image" }), _jsx(Text, { className: "text-sm", style: { color: "var(--color-muted)" }, children: "Choose a wide panoramic image that shows all the characters. You will place pins on it in the next step." }), _jsxs(Label, { className: "flex cursor-pointer flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed p-8 transition-colors hover:opacity-80", style: { borderColor: "var(--border-ink)" }, children: [_jsx(Span, { className: "text-3xl", children: "\uD83D\uDDBC" }), _jsx(Span, { className: "font-medium", children: uploading ? "Uploading…" : "Click to choose image" }), _jsx(Span, { className: "text-xs", style: { color: "var(--color-muted)" }, children: "JPG, PNG, WebP \u2014 wide landscape images work best" }), _jsx(Input, { type: "file", accept: "image/*", className: "hidden", onChange: handleImageUpload, disabled: uploading })] }), imageUrl && (_jsxs(Div, { className: "space-y-1", children: [_jsx(Div, { className: "relative w-full overflow-hidden rounded-lg", style: { paddingTop: "37.5%" }, children: _jsx(Image, { src: imageUrl, alt: imageAlt, fill: true, className: "object-cover", sizes: "680px" }) }), _jsx(Text, { className: "text-xs font-medium text-green-700", children: "\u2713 Image uploaded" })] })), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: "Image Alt Text" }), _jsx(Input, { type: "text", value: imageAlt, onChange: (e) => setImageAlt(e.target.value), placeholder: "DC, Marvel and Anime characters", className: "rounded border-2 px-3 py-2 text-sm outline-none", style: {
136
+ }, children: [_jsx(Heading, { level: 2, className: "text-lg font-bold", children: "Upload Background Image" }), _jsx(Text, { className: "text-sm", style: { color: "var(--color-muted)" }, children: "Choose a wide panoramic image that shows all the characters. You will place pins on it in the next step." }), _jsxs(Label, { className: "flex cursor-pointer flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed p-8 transition-colors hover:opacity-80", style: { borderColor: "var(--border-ink)" }, children: [_jsx(Span, { className: "text-3xl", children: "\uD83D\uDDBC" }), _jsx(Span, { className: "font-medium", children: uploading ? "Uploading…" : "Click to choose image" }), _jsx(Span, { className: "text-xs", style: { color: "var(--color-muted)" }, children: "JPG, PNG, WebP \u2014 wide landscape images work best" }), _jsx(Input, { type: "file", accept: "image/*", className: "hidden", onChange: handleImageUpload, disabled: uploading })] }), imageUrl && (_jsxs(Div, { className: "space-y-1", children: [_jsx(Div, { className: CLS_IMG_WRAP, style: { paddingTop: "37.5%" }, children: _jsx(Image, { src: imageUrl, alt: imageAlt, fill: true, className: "object-cover", sizes: "680px" }) }), _jsx(Text, { className: "text-xs font-medium text-green-700", children: "\u2713 Image uploaded" })] })), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: "Image Alt Text" }), _jsx(Input, { type: "text", value: imageAlt, onChange: (e) => setImageAlt(e.target.value), placeholder: "DC, Marvel and Anime characters", className: CLS_INPUT, style: {
132
137
  borderColor: "var(--border-ink)",
133
138
  background: "var(--surface-elevated)",
134
139
  color: "var(--color-black)",
@@ -138,17 +143,17 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
138
143
  }, children: "Skip to Review" })), _jsx(Button, { type: "button", onClick: () => setStep("place"), disabled: !imageUrl || uploading, variant: "primary", className: "px-4 py-2 text-sm font-bold rounded transition-opacity disabled:opacity-50", style: {
139
144
  background: "var(--color-black)",
140
145
  color: "var(--color-yellow)",
141
- }, children: "Next: Place Pins \u2192" })] })] })), step === "place" && (_jsxs(Div, { className: "space-y-4 rounded-lg border-2 p-6", style: {
146
+ }, children: "Next: Place Pins \u2192" })] })] })), step === "place" && (_jsxs(Div, { className: CLS_STEP, style: {
142
147
  borderColor: "var(--border-ink)",
143
148
  background: "var(--surface-elevated)",
144
- }, children: [_jsx(Heading, { level: 2, className: "text-lg font-bold", children: "Place a Pin" }), _jsxs(Text, { className: "text-sm", style: { color: "var(--color-muted)" }, children: [_jsx("strong", { children: "Click anywhere on the image" }), " to drop a pin, or enter exact coordinates below."] }), _jsxs(Div, { ref: containerRef, className: "relative w-full overflow-hidden rounded-lg", style: {
149
+ }, children: [_jsx(Heading, { level: 2, className: "text-lg font-bold", children: "Place a Pin" }), _jsxs(Text, { className: "text-sm", style: { color: "var(--color-muted)" }, children: [_jsx("strong", { children: "Click anywhere on the image" }), " to drop a pin, or enter exact coordinates below."] }), _jsxs(Div, { ref: containerRef, className: CLS_IMG_WRAP, style: {
145
150
  paddingTop: "56.25%",
146
151
  cursor: "crosshair",
147
152
  background: "#111",
148
153
  }, onClick: handleImageClick, children: [_jsx(Image, { src: imageUrl, alt: imageAlt, fill: true, className: "object-cover", sizes: "(max-width: 680px) 100vw" }), pins.map((pin) => (_jsxs(Div, { className: "pointer-events-none absolute", style: {
149
154
  left: `${pin.xPct}%`,
150
155
  top: `${pin.yPct}%`,
151
- transform: "translate(-50%, -50%)",
156
+ transform: STY_CENTERED,
152
157
  zIndex: 10,
153
158
  }, children: [_jsx(Div, { className: "flex items-center justify-center rounded-full text-xs font-bold text-white", style: {
154
159
  width: 24,
@@ -159,7 +164,7 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
159
164
  }, children: "+" }), pin.name && (_jsx(Div, { className: "pointer-events-none absolute left-7 top-1/2 -translate-y-1/2 whitespace-nowrap rounded px-1.5 py-0.5 text-[9px] font-bold text-white", style: { background: "#0D0D0D" }, children: pin.name }))] }, pin.id))), draftPos && (_jsxs(Div, { className: "absolute", style: {
160
165
  left: `${draftPos.xPct}%`,
161
166
  top: `${draftPos.yPct}%`,
162
- transform: "translate(-50%, -50%)",
167
+ transform: STY_CENTERED,
163
168
  zIndex: 20,
164
169
  }, children: [_jsx(Span, { className: "absolute animate-ping rounded-full", style: {
165
170
  inset: -6,
@@ -201,18 +206,18 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
201
206
  const v = parseFloat(e.target.value);
202
207
  if (!isNaN(v))
203
208
  onChange(v);
204
- }, className: "rounded border-2 px-3 py-2 text-sm outline-none", style: {
209
+ }, className: CLS_INPUT, style: {
205
210
  borderColor: "var(--border-ink)",
206
211
  background: "var(--surface-elevated)",
207
212
  color: "var(--color-black)",
208
213
  } })] }, field))) }), pins.length > 0 && (_jsxs(Text, { className: "text-xs", style: { color: "var(--color-muted)" }, children: [pins.length, " pin", pins.length !== 1 ? "s" : "", " already placed on this image."] })), _jsxs(Row, { justify: "between", className: "border-t pt-4", style: { borderColor: "var(--border-ink)" }, children: [_jsx(Button, { type: "button", onClick: () => setStep("image"), variant: "ghost", className: "px-4 py-2 text-sm font-medium rounded transition-opacity hover:opacity-70", style: { background: "transparent", color: "var(--color-black)" }, children: "\u2190 Back" }), _jsxs(Row, { gap: "sm", children: [pins.length > 0 && (_jsx(Button, { type: "button", onClick: () => setStep("review"), variant: "outline", className: "px-4 py-2 text-sm font-bold rounded transition-opacity", style: {
209
214
  background: "var(--surface-warm)",
210
215
  color: "var(--color-black)",
211
- border: "2px solid var(--border-ink)",
216
+ border: STY_BORDER_INK,
212
217
  }, children: "Done Adding Pins" })), _jsx(Button, { type: "button", onClick: () => setStep("details"), disabled: !draftPos, variant: "primary", className: "px-4 py-2 text-sm font-bold rounded transition-opacity disabled:opacity-50", style: {
213
218
  background: "var(--color-black)",
214
219
  color: "var(--color-yellow)",
215
- }, children: "Continue: Add Details \u2192" })] })] })] })), step === "details" && (_jsxs(Div, { className: "space-y-4 rounded-lg border-2 p-6", style: {
220
+ }, children: "Continue: Add Details \u2192" })] })] })] })), step === "details" && (_jsxs(Div, { className: CLS_STEP, style: {
216
221
  borderColor: "var(--border-ink)",
217
222
  background: "var(--surface-elevated)",
218
223
  }, children: [_jsx(Heading, { level: 2, className: "text-lg font-bold", children: "Pin Details" }), _jsxs(Text, { className: "text-sm", style: { color: "var(--color-muted)" }, children: ["Fill in the details for the pin at", " ", _jsxs("strong", { children: [draftPos?.xPct, "%, ", draftPos?.yPct, "%"] }), "."] }), _jsx(Div, { className: "grid grid-cols-2 gap-3", children: [
@@ -228,7 +233,7 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
228
233
  onChange: setDraftUniverse,
229
234
  placeholder: "Marvel",
230
235
  },
231
- ].map(({ label, value, onChange, placeholder }) => (_jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: label }), _jsx(Input, { type: "text", value: value, onChange: (e) => onChange(e.target.value), placeholder: placeholder, className: "rounded border-2 px-3 py-2 text-sm outline-none", style: {
236
+ ].map(({ label, value, onChange, placeholder }) => (_jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: label }), _jsx(Input, { type: "text", value: value, onChange: (e) => onChange(e.target.value), placeholder: placeholder, className: CLS_INPUT, style: {
232
237
  borderColor: "var(--border-ink)",
233
238
  background: "var(--surface-elevated)",
234
239
  color: "var(--color-black)",
@@ -236,7 +241,7 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
236
241
  borderColor: "var(--border-ink)",
237
242
  background: "var(--surface-elevated)",
238
243
  color: "var(--color-black)",
239
- }, placeholder: "Short description shown in the popup\u2026" })] }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: "Link (href) *" }), _jsx(Input, { type: "text", value: draftHref, onChange: (e) => setDraftHref(e.target.value), placeholder: "/franchise/marvel", className: "rounded border-2 px-3 py-2 text-sm outline-none", style: {
244
+ }, placeholder: "Short description shown in the popup\u2026" })] }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: "Link (href) *" }), _jsx(Input, { type: "text", value: draftHref, onChange: (e) => setDraftHref(e.target.value), placeholder: "/franchise/marvel", className: CLS_INPUT, style: {
240
245
  borderColor: "var(--border-ink)",
241
246
  background: "var(--surface-elevated)",
242
247
  color: "var(--color-black)",
@@ -253,11 +258,11 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
253
258
  onChange: setDraftBadge,
254
259
  placeholder: "MARVEL",
255
260
  },
256
- ].map(({ label, value, onChange, placeholder }) => (_jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: label }), _jsx(Input, { type: "text", value: value, onChange: (e) => onChange(e.target.value), placeholder: placeholder, className: "rounded border-2 px-3 py-2 text-sm outline-none", style: {
261
+ ].map(({ label, value, onChange, placeholder }) => (_jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: label }), _jsx(Input, { type: "text", value: value, onChange: (e) => onChange(e.target.value), placeholder: placeholder, className: CLS_INPUT, style: {
257
262
  borderColor: "var(--border-ink)",
258
263
  background: "var(--surface-elevated)",
259
264
  color: "var(--color-black)",
260
- } })] }, label))) }), _jsxs(Row, { align: "end", gap: "sm", children: [_jsxs(Div, { className: "flex-1 flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: "Accent Colour (hex)" }), _jsx(Input, { type: "text", value: draftAccent, onChange: (e) => setDraftAccent(e.target.value), placeholder: "#E8001C", className: "rounded border-2 px-3 py-2 text-sm outline-none", style: {
265
+ } })] }, label))) }), _jsxs(Row, { align: "end", gap: "sm", children: [_jsxs(Div, { className: "flex-1 flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: "Accent Colour (hex)" }), _jsx(Input, { type: "text", value: draftAccent, onChange: (e) => setDraftAccent(e.target.value), placeholder: "#E8001C", className: CLS_INPUT, style: {
261
266
  borderColor: "var(--border-ink)",
262
267
  background: "var(--surface-elevated)",
263
268
  color: "var(--color-black)",
@@ -267,20 +272,20 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
267
272
  }, variant: "outline", className: "px-4 py-2 text-sm font-bold rounded transition-opacity disabled:opacity-50", style: {
268
273
  background: "var(--surface-warm)",
269
274
  color: "var(--color-black)",
270
- border: "2px solid var(--border-ink)",
275
+ border: STY_BORDER_INK,
271
276
  }, children: "Save Pin & Finish" }), _jsx(Button, { type: "button", disabled: !draftName || !draftHref, onClick: () => {
272
277
  commitDraftPin();
273
278
  setStep("place");
274
279
  }, variant: "primary", className: "px-4 py-2 text-sm font-bold rounded transition-opacity disabled:opacity-50", style: {
275
280
  background: "var(--color-black)",
276
281
  color: "var(--color-yellow)",
277
- }, children: "Save Pin & Add Another \u2192" })] })] })] })), step === "review" && (_jsxs(Div, { className: "space-y-4 rounded-lg border-2 p-6", style: {
282
+ }, children: "Save Pin & Add Another \u2192" })] })] })] })), step === "review" && (_jsxs(Div, { className: CLS_STEP, style: {
278
283
  borderColor: "var(--border-ink)",
279
284
  background: "var(--surface-elevated)",
280
- }, children: [_jsxs(Row, { justify: "between", children: [_jsx(Heading, { level: 2, className: "text-lg font-bold", children: "Review & Save" }), _jsxs(Span, { className: "rounded-full px-3 py-1 text-sm font-bold", style: { background: "var(--surface-warm)" }, children: [pins.length, " pin", pins.length !== 1 ? "s" : ""] })] }), _jsxs(Div, { className: "relative w-full overflow-hidden rounded-lg", style: { paddingTop: "56.25%", background: "#111" }, children: [_jsx(Image, { src: imageUrl, alt: imageAlt, fill: true, className: "object-cover", sizes: "(max-width: 680px) 100vw" }), pins.map((pin) => (_jsxs(Div, { className: "pointer-events-none absolute", style: {
285
+ }, children: [_jsxs(Row, { justify: "between", children: [_jsx(Heading, { level: 2, className: "text-lg font-bold", children: "Review & Save" }), _jsxs(Span, { className: "rounded-full px-3 py-1 text-sm font-bold", style: { background: "var(--surface-warm)" }, children: [pins.length, " pin", pins.length !== 1 ? "s" : ""] })] }), _jsxs(Div, { className: CLS_IMG_WRAP, style: { paddingTop: "56.25%", background: "#111" }, children: [_jsx(Image, { src: imageUrl, alt: imageAlt, fill: true, className: "object-cover", sizes: "(max-width: 680px) 100vw" }), pins.map((pin) => (_jsxs(Div, { className: "pointer-events-none absolute", style: {
281
286
  left: `${pin.xPct}%`,
282
287
  top: `${pin.yPct}%`,
283
- transform: "translate(-50%, -50%)",
288
+ transform: STY_CENTERED,
284
289
  zIndex: 10,
285
290
  }, children: [_jsx(Div, { className: "flex items-center justify-center rounded-full text-xs font-bold text-white", style: {
286
291
  width: 28,
@@ -291,7 +296,7 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
291
296
  }, children: "+" }), pin.name && (_jsx(Div, { className: "pointer-events-none absolute left-8 top-1/2 -translate-y-1/2 whitespace-nowrap rounded px-1.5 py-0.5 text-[9px] font-bold text-white", style: { background: "#0D0D0D" }, children: pin.name }))] }, pin.id)))] }), pins.length > 0 ? (_jsx(Ul, { className: "divide-y rounded-lg border", style: { borderColor: "var(--border-ink)" }, children: pins.map((pin, i) => (_jsx(Li, { className: "px-4 py-3", children: _jsxs(Row, { gap: "sm", children: [_jsx(Span, { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-xs font-bold text-white", style: { background: pin.accent || "#E8001C" }, children: i + 1 }), _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { className: "truncate text-sm font-bold", children: pin.name || (_jsx(Span, { className: "italic", style: { color: "var(--color-muted)" }, children: "Unnamed" })) }), _jsxs(Text, { className: "text-xs", style: { color: "var(--color-muted)" }, children: [pin.universe, " \u00B7 ", pin.xPct.toFixed(0), "%,", " ", pin.yPct.toFixed(0), "%"] })] }), _jsx(Button, { type: "button", onClick: () => deletePin(pin.id), variant: "ghost", className: "shrink-0 rounded p-1 text-xs text-red-500 hover:bg-red-50", title: "Remove pin", children: "\u2715" })] }) }, pin.id))) })) : (_jsx(Div, { className: "rounded-lg border-2 border-dashed p-8 text-center text-sm", style: {
292
297
  borderColor: "var(--border-ink)",
293
298
  color: "var(--color-muted)",
294
- }, children: "No pins yet \u2014 add some using the button below." })), _jsxs("details", { className: "rounded-lg border p-3", style: { borderColor: "var(--border-ink)" }, children: [_jsx("summary", { className: "cursor-pointer text-sm font-medium", children: "Image Settings" }), _jsxs(Div, { className: "mt-3 space-y-3", children: [_jsxs(Label, { className: "inline-flex cursor-pointer items-center gap-2 rounded border-2 border-dashed px-3 py-1.5 text-sm font-medium transition-colors hover:opacity-80", style: { borderColor: "var(--border-ink)" }, children: [uploading ? "Uploading…" : "Replace Image", _jsx(Input, { type: "file", accept: "image/*", className: "hidden", onChange: handleImageUpload, disabled: uploading })] }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: "Image Alt Text" }), _jsx(Input, { type: "text", value: imageAlt, onChange: (e) => setImageAlt(e.target.value), className: "rounded border-2 px-3 py-2 text-sm outline-none", style: {
299
+ }, children: "No pins yet \u2014 add some using the button below." })), _jsxs("details", { className: "rounded-lg border p-3", style: { borderColor: "var(--border-ink)" }, children: [_jsx("summary", { className: "cursor-pointer text-sm font-medium", children: "Image Settings" }), _jsxs(Div, { className: "mt-3 space-y-3", children: [_jsxs(Label, { className: "inline-flex cursor-pointer items-center gap-2 rounded border-2 border-dashed px-3 py-1.5 text-sm font-medium transition-colors hover:opacity-80", style: { borderColor: "var(--border-ink)" }, children: [uploading ? "Uploading…" : "Replace Image", _jsx(Input, { type: "file", accept: "image/*", className: "hidden", onChange: handleImageUpload, disabled: uploading })] }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-sm font-bold", children: "Image Alt Text" }), _jsx(Input, { type: "text", value: imageAlt, onChange: (e) => setImageAlt(e.target.value), className: CLS_INPUT, style: {
295
300
  borderColor: "var(--border-ink)",
296
301
  background: "var(--surface-elevated)",
297
302
  color: "var(--color-black)",
@@ -301,7 +306,7 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
301
306
  }, variant: "outline", className: "px-4 py-2 text-sm font-bold rounded transition-opacity", style: {
302
307
  background: "var(--surface-warm)",
303
308
  color: "var(--color-black)",
304
- border: "2px solid var(--border-ink)",
309
+ border: STY_BORDER_INK,
305
310
  }, children: "+ Add Another Pin" }), _jsx(Button, { type: "button", onClick: handleSave, disabled: !imageUrl || saving || pins.length === 0, variant: "primary", className: "px-4 py-2 text-sm font-bold rounded transition-opacity disabled:opacity-50", style: {
306
311
  background: "var(--color-black)",
307
312
  color: "var(--color-yellow)",
@@ -1,8 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Heading, HorizontalScroller, Section } from "../../../ui";
2
+ import { Heading, HorizontalScroller, Section, Text } from "../../../ui";
3
3
  import { THEME_CONSTANTS } from "../../../tokens";
4
4
  import { SectionCarousel } from "./SectionCarousel";
5
5
  import { MediaImage } from "../../media/MediaImage";
6
+ // --- Constants ---------------------------------------------------------------
7
+ const CLS_CONTAINER = "w-full max-w-7xl mx-auto px-4";
6
8
  // --- Helpers -----------------------------------------------------------------
7
9
  const COLS_CLASS = {
8
10
  1: "grid-cols-1",
@@ -39,7 +41,7 @@ function CardItem({ card }) {
39
41
  cardStyle.backgroundColor = card.backgroundColor;
40
42
  if (card.textColor)
41
43
  cardStyle.color = card.textColor;
42
- return (_jsxs("div", { className: `flex flex-col overflow-hidden ${radiusClass} ${shadowClass} bg-[var(--appkit-color-surface)]`, style: cardStyle, children: [card.image && (_jsx("div", { className: "relative w-full aspect-video overflow-hidden", children: _jsx(MediaImage, { src: card.image, alt: card.imageAlt ?? "", size: "card" }) })), _jsxs("div", { className: "flex flex-col gap-2 p-4 flex-1", children: [card.eyebrow && (_jsx("p", { className: "text-xs font-semibold uppercase tracking-widest opacity-70", children: card.eyebrow })), card.title && (_jsx("p", { className: "text-base font-bold leading-snug", children: card.title })), card.body && (_jsx("p", { className: "text-sm opacity-80 leading-relaxed flex-1", children: card.body })), card.buttons && card.buttons.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: card.buttons.map((btn, i) => (_jsx("a", { href: btn.href, target: btn.target ?? "_self", rel: btn.target === "_blank" ? "noopener noreferrer" : undefined, className: VARIANT_CLASS[btn.variant] ?? VARIANT_CLASS.primary, children: btn.label }, i))) })), card.formEmbed && (_jsx("iframe", { src: card.formEmbed, sandbox: "allow-forms allow-scripts", loading: "lazy", className: "w-full mt-3 border-0 rounded-md", style: { minHeight: "200px" }, title: "Embedded form" }))] })] }));
44
+ return (_jsxs("div", { className: `flex flex-col overflow-hidden ${radiusClass} ${shadowClass} bg-[var(--appkit-color-surface)]`, style: cardStyle, children: [card.image && (_jsx("div", { className: "relative w-full aspect-video overflow-hidden", children: _jsx(MediaImage, { src: card.image, alt: card.imageAlt ?? "", size: "card" }) })), _jsxs("div", { className: "flex flex-col gap-2 p-4 flex-1", children: [card.eyebrow && (_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest opacity-70", children: card.eyebrow })), card.title && (_jsx(Text, { className: "text-base font-bold leading-snug", children: card.title })), card.body && (_jsx(Text, { className: "text-sm opacity-80 leading-relaxed flex-1", children: card.body })), card.buttons && card.buttons.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: card.buttons.map((btn, i) => (_jsx("a", { href: btn.href, target: btn.target ?? "_self", rel: btn.target === "_blank" ? "noopener noreferrer" : undefined, className: VARIANT_CLASS[btn.variant] ?? VARIANT_CLASS.primary, children: btn.label }, i))) })), card.formEmbed && (_jsx("iframe", { src: card.formEmbed, sandbox: "allow-forms allow-scripts", loading: "lazy", className: "w-full mt-3 border-0 rounded-md", style: { minHeight: "200px" }, title: "Embedded form" }))] })] }));
43
45
  }
44
46
  // --- Section Header ----------------------------------------------------------
45
47
  function SectionHeader({ title }) {
@@ -55,11 +57,11 @@ export function CustomCardsSection(config) {
55
57
  return null;
56
58
  // autoScroll: wrap all cards in SectionCarousel (client carousel)
57
59
  if (autoScroll) {
58
- return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsx("div", { className: "w-full max-w-7xl mx-auto px-4", children: _jsx(SectionCarousel, { title: title ?? "", items: cards, renderItem: (card) => _jsx(CardItem, { card: card }), keyExtractor: (card) => card.id, autoScroll: true, autoScrollInterval: scrollIntervalMs ?? 3500, perView: { base: 1, sm: 2, md: columns } }) }) }));
60
+ return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsx("div", { className: CLS_CONTAINER, children: _jsx(SectionCarousel, { title: title ?? "", items: cards, renderItem: (card) => _jsx(CardItem, { card: card }), keyExtractor: (card) => card.id, autoScroll: true, autoScrollInterval: scrollIntervalMs ?? 3500, perView: { base: 1, sm: 2, md: columns } }) }) }));
59
61
  }
60
62
  // Row layout: horizontal scroller
61
63
  if (layout === "row") {
62
- return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsxs("div", { className: "w-full max-w-7xl mx-auto px-4", children: [_jsx(SectionHeader, { title: title }), _jsx(HorizontalScroller, { gap: 16, showArrows: true, snapToItems: true, children: cards.map((card) => (_jsx("div", { className: "w-72 flex-shrink-0", children: _jsx(CardItem, { card: card }) }, card.id))) })] }) }));
64
+ return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsxs("div", { className: CLS_CONTAINER, children: [_jsx(SectionHeader, { title: title }), _jsx(HorizontalScroller, { gap: 16, showArrows: true, snapToItems: true, children: cards.map((card) => (_jsx("div", { className: "w-72 flex-shrink-0", children: _jsx(CardItem, { card: card }) }, card.id))) })] }) }));
63
65
  }
64
66
  // Masonry layout: CSS columns
65
67
  if (layout === "masonry") {
@@ -68,9 +70,9 @@ export function CustomCardsSection(config) {
68
70
  .split(" ")
69
71
  .map((c) => c.replace("grid-cols-", "columns-"))
70
72
  .join(" ");
71
- return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsxs("div", { className: "w-full max-w-7xl mx-auto px-4", children: [_jsx(SectionHeader, { title: title }), _jsx("div", { className: `${masonryClass} gap-4 space-y-4`, children: cards.map((card) => (_jsx("div", { className: "break-inside-avoid", children: _jsx(CardItem, { card: card }) }, card.id))) })] }) }));
73
+ return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsxs("div", { className: CLS_CONTAINER, children: [_jsx(SectionHeader, { title: title }), _jsx("div", { className: `${masonryClass} gap-4 space-y-4`, children: cards.map((card) => (_jsx("div", { className: "break-inside-avoid", children: _jsx(CardItem, { card: card }) }, card.id))) })] }) }));
72
74
  }
73
75
  // Grid layout (default)
74
76
  const colsClass = COLS_CLASS[columns] ?? COLS_CLASS[3];
75
- return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsxs("div", { className: "w-full max-w-7xl mx-auto px-4", children: [_jsx(SectionHeader, { title: title }), _jsx("div", { className: `grid ${colsClass} gap-4`, children: cards.map((card) => (_jsx(CardItem, { card: card }, card.id))) })] }) }));
77
+ return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsxs("div", { className: CLS_CONTAINER, children: [_jsx(SectionHeader, { title: title }), _jsx("div", { className: `grid ${colsClass} gap-4`, children: cards.map((card) => (_jsx(CardItem, { card: card }, card.id))) })] }) }));
76
78
  }
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useRef } from "react";
4
4
  import Link from "next/link";
5
5
  import { SectionCarousel } from "./SectionCarousel";
@@ -32,7 +32,7 @@ function ProductGrid({ items, rows, maxItems, pagination, viewMoreHref, viewMore
32
32
  return () => { if (timerRef.current)
33
33
  clearInterval(timerRef.current); };
34
34
  }, [pagination, pageSize, totalItems]);
35
- return (_jsx(Section, { className: `py-12 px-4 ${themed.bgPrimary}`, children: _jsxs("div", { className: "mx-auto max-w-7xl", children: [_jsxs("div", { className: "mb-6 flex items-end justify-between gap-4", children: [_jsxs("div", { children: [_jsx(Heading, { level: 2, className: `text-2xl font-bold md:text-3xl ${themed.textPrimary}`, children: title }), description && (_jsx(Text, { variant: "muted", className: "mt-1 text-sm", children: description }))] }), pagination === "arrows" && (_jsxs("div", { className: "flex shrink-0 gap-2", children: [_jsx("button", { type: "button", onClick: prev, disabled: !canPrev, "aria-label": "Previous", className: "flex h-9 w-9 items-center justify-center rounded-full border border-zinc-200 bg-white text-zinc-600 hover:border-[var(--appkit-color-primary)] hover:text-[var(--appkit-color-primary)] disabled:opacity-40 dark:border-slate-700 dark:bg-slate-900 dark:text-zinc-300", children: "\u2039" }), _jsx("button", { type: "button", onClick: next, disabled: !canNext, "aria-label": "Next", className: "flex h-9 w-9 items-center justify-center rounded-full border border-zinc-200 bg-white text-zinc-600 hover:border-[var(--appkit-color-primary)] hover:text-[var(--appkit-color-primary)] disabled:opacity-40 dark:border-slate-700 dark:bg-slate-900 dark:text-zinc-300", children: "\u203A" })] }))] }), _jsx("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5", children: visible.map((product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(product.slug ?? product.id ?? "")) }, product.id))) }), pagination === "load-more" && canNext && (_jsx("div", { className: "mt-8 text-center", children: _jsx("button", { type: "button", onClick: next, className: "rounded-lg border border-zinc-300 bg-white px-6 py-2 text-sm font-medium text-zinc-700 hover:border-[var(--appkit-color-primary)] hover:text-[var(--appkit-color-primary)] dark:border-slate-600 dark:bg-slate-900 dark:text-zinc-300", children: "Load more" }) })), viewMoreHref && (_jsx("div", { className: "mt-6 text-center", children: _jsx(Link, { href: viewMoreHref, className: "text-sm font-medium text-[var(--appkit-color-primary)] hover:opacity-80", children: viewMoreLabel }) }))] }) }));
35
+ return (_jsx(Section, { className: `py-12 px-4 ${themed.bgPrimary}`, children: _jsxs("div", { className: "mx-auto max-w-7xl", children: [_jsxs("div", { className: "mb-6 flex items-end justify-between gap-4", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 2, className: `text-2xl font-bold md:text-3xl ${themed.textPrimary}`, children: title }), description && (_jsx(Text, { variant: "muted", className: "mt-1 text-sm", children: description }))] }), pagination === "arrows" && (_jsxs("div", { className: "flex shrink-0 gap-2", children: [_jsx("button", { type: "button", onClick: prev, disabled: !canPrev, "aria-label": "Previous", className: "flex h-9 w-9 items-center justify-center rounded-full border border-zinc-200 bg-white text-zinc-600 hover:border-[var(--appkit-color-primary)] hover:text-[var(--appkit-color-primary)] disabled:opacity-40 dark:border-slate-700 dark:bg-slate-900 dark:text-zinc-300", children: "\u2039" }), _jsx("button", { type: "button", onClick: next, disabled: !canNext, "aria-label": "Next", className: "flex h-9 w-9 items-center justify-center rounded-full border border-zinc-200 bg-white text-zinc-600 hover:border-[var(--appkit-color-primary)] hover:text-[var(--appkit-color-primary)] disabled:opacity-40 dark:border-slate-700 dark:bg-slate-900 dark:text-zinc-300", children: "\u203A" })] }))] }), _jsx("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5", children: visible.map((product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(product.slug ?? product.id ?? "")) }, product.id))) }), pagination === "load-more" && canNext && (_jsx("div", { className: "mt-8 text-center", children: _jsx("button", { type: "button", onClick: next, className: "rounded-lg border border-zinc-300 bg-white px-6 py-2 text-sm font-medium text-zinc-700 hover:border-[var(--appkit-color-primary)] hover:text-[var(--appkit-color-primary)] dark:border-slate-600 dark:bg-slate-900 dark:text-zinc-300", children: "Load more" }) })), viewMoreHref && (_jsx("div", { className: "mt-6 text-center", children: _jsx(Link, { href: viewMoreHref, className: "text-sm font-medium text-[var(--appkit-color-primary)] hover:opacity-80", children: viewMoreLabel }) }))] }) }));
36
36
  }
37
37
  // --- Main component -----------------------------------------------------------
38
38
  export function FeaturedProductsSection({ title = "Featured Products", description, viewMoreHref, viewMoreLabel = "View all products →", className = "", filterByBrand, initialItems, rows = 1, maxItems, pagination = "load-more", }) {
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Heading, Section, Text } from "../../../ui";
3
3
  import { THEME_CONSTANTS } from "../../../tokens";
4
4
  import { siteSettingsRepository } from "../../admin/repository/site-settings.repository";
@@ -20,7 +20,7 @@ function ReviewCard({ review, showRating, showDate, }) {
20
20
  day: "numeric",
21
21
  })
22
22
  : null;
23
- return (_jsxs("div", { className: "flex flex-col gap-3 p-4 rounded-xl bg-[var(--appkit-color-surface)] shadow-sm border border-[var(--appkit-color-border)]", children: [_jsxs("div", { className: "flex items-center gap-3", children: [review.authorPhotoUrl ? (_jsx("img", { src: review.authorPhotoUrl, alt: review.authorName, className: "w-9 h-9 rounded-full object-cover flex-shrink-0", loading: "lazy", referrerPolicy: "no-referrer" })) : (_jsx("div", { className: "w-9 h-9 rounded-full bg-[var(--appkit-color-primary)] flex items-center justify-center flex-shrink-0", children: _jsx("span", { className: "text-white text-sm font-bold", children: review.authorName.charAt(0).toUpperCase() }) })), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-semibold truncate", children: review.authorName }), dateStr && (_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: dateStr }))] })] }), showRating && _jsx(StarRating, { rating: review.rating }), review.text && (_jsx("p", { className: "text-sm leading-relaxed text-zinc-700 dark:text-zinc-300 line-clamp-4", children: review.text })), _jsx(GoogleBadge, {})] }));
23
+ return (_jsxs("div", { className: "flex flex-col gap-3 p-4 rounded-xl bg-[var(--appkit-color-surface)] shadow-sm border border-[var(--appkit-color-border)]", children: [_jsxs("div", { className: "flex items-center gap-3", children: [review.authorPhotoUrl ? (_jsx("img", { src: review.authorPhotoUrl, alt: review.authorName, className: "w-9 h-9 rounded-full object-cover flex-shrink-0", loading: "lazy", referrerPolicy: "no-referrer" })) : (_jsx("div", { className: "w-9 h-9 rounded-full bg-[var(--appkit-color-primary)] flex items-center justify-center flex-shrink-0", children: _jsx("span", { className: "text-white text-sm font-bold", children: review.authorName.charAt(0).toUpperCase() }) })), _jsxs("div", { className: "min-w-0", children: [_jsx(Text, { className: "text-sm font-semibold truncate", children: review.authorName }), dateStr && (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: dateStr }))] })] }), showRating && _jsx(StarRating, { rating: review.rating }), review.text && (_jsx(Text, { className: "text-sm leading-relaxed text-zinc-700 dark:text-zinc-300 line-clamp-4", children: review.text })), _jsx(GoogleBadge, {})] }));
24
24
  }
25
25
  // --- Empty / Not Configured States -------------------------------------------
26
26
  function NotConfiguredState() {
@@ -61,5 +61,5 @@ export async function GoogleReviewsSection(config) {
61
61
  : "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4";
62
62
  const cardClass = layout === "carousel" ? "flex-shrink-0 w-72 snap-start" : "";
63
63
  const mapsHref = googleMapsUrl || (placeId ? `https://search.google.com/local/reviews?placeid=${placeId}` : "");
64
- return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsxs("div", { className: "w-full max-w-7xl mx-auto px-4", children: [_jsxs("div", { className: "mb-8 flex items-end justify-between gap-4", children: [_jsxs("div", { children: [_jsx(Heading, { level: 2, children: "What Our Customers Say" }), aggregateRating > 0 && (_jsxs("div", { className: "flex items-center gap-2 mt-1", children: [_jsx(StarRating, { rating: Math.round(aggregateRating) }), _jsxs(Text, { size: "sm", variant: "muted", children: [aggregateRating.toFixed(1), " \u00B7 ", totalRatings.toLocaleString(), " reviews"] })] }))] }), linkToGoogleMaps && mapsHref && (_jsx("a", { href: mapsHref, target: "_blank", rel: "noopener noreferrer", className: "shrink-0 text-sm font-medium text-blue-600 dark:text-blue-400 hover:underline", children: "View on Google \u2192" }))] }), reviews.length === 0 ? (_jsx(EmptyState, {})) : (_jsx("div", { className: gridClass, children: reviews.map((review, i) => (_jsx("div", { className: cardClass, children: _jsx(ReviewCard, { review: review, showRating: showRating, showDate: showDate }) }, `${review.authorName}-${i}`))) }))] }) }));
64
+ return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsxs("div", { className: "w-full max-w-7xl mx-auto px-4", children: [_jsxs("div", { className: "mb-8 flex items-end justify-between gap-4", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 2, children: "What Our Customers Say" }), aggregateRating > 0 && (_jsxs("div", { className: "flex items-center gap-2 mt-1", children: [_jsx(StarRating, { rating: Math.round(aggregateRating) }), _jsxs(Text, { size: "sm", variant: "muted", children: [aggregateRating.toFixed(1), " \u00B7 ", totalRatings.toLocaleString(), " reviews"] })] }))] }), linkToGoogleMaps && mapsHref && (_jsx("a", { href: mapsHref, target: "_blank", rel: "noopener noreferrer", className: "shrink-0 text-sm font-medium text-blue-600 dark:text-blue-400 hover:underline", children: "View on Google \u2192" }))] }), reviews.length === 0 ? (_jsx(EmptyState, {})) : (_jsx("div", { className: gridClass, children: reviews.map((review, i) => (_jsx("div", { className: cardClass, children: _jsx(ReviewCard, { review: review, showRating: showRating, showDate: showDate }) }, `${review.authorName}-${i}`))) }))] }) }));
65
65
  }
@@ -4,6 +4,7 @@ import { useState, useEffect, useCallback } from "react";
4
4
  import Image from "next/image";
5
5
  import Link from "next/link";
6
6
  import { Button, Div, Heading, Row, Span } from "../../../ui";
7
+ const FONT_BANGERS = "var(--font-bangers, Bangers, cursive)";
7
8
  export function HeroBanner({ banners, autoplayMs = 5000 }) {
8
9
  const [current, setCurrent] = useState(0);
9
10
  const [animating, setAnimating] = useState(false);
@@ -45,16 +46,16 @@ export function HeroBanner({ banners, autoplayMs = 5000 }) {
45
46
  }, children: [banner.subtitle && (_jsx(Div, { className: "mb-3", children: _jsx(Span, { className: "inline-block px-3 py-1 text-xs font-black uppercase tracking-[0.18em]", style: {
46
47
  background: "var(--color-red)",
47
48
  color: "#FFFFFF",
48
- fontFamily: "var(--font-bangers, Bangers, cursive)",
49
+ fontFamily: FONT_BANGERS,
49
50
  letterSpacing: "0.16em",
50
51
  }, children: banner.subtitle }) })), _jsx(Heading, { level: 2, className: "mb-5 max-w-xl leading-none", style: {
51
- fontFamily: "var(--font-bangers, Bangers, cursive)",
52
+ fontFamily: FONT_BANGERS,
52
53
  fontSize: "clamp(3rem, 9vw, 6.5rem)",
53
54
  letterSpacing: "0.04em",
54
55
  color: banner.textColor ?? "#FFFFFF",
55
56
  textShadow: "0 4px 32px rgba(0,0,0,0.5)",
56
57
  }, children: banner.title }), banner.ctaLabel && banner.ctaUrl && (_jsx(Row, { wrap: true, gap: "md", children: _jsxs(Link, { href: banner.ctaUrl, className: "inline-flex items-center gap-2 sm:gap-3 px-5 py-3 sm:px-8 sm:py-3.5 font-black uppercase transition-all hover:-translate-y-0.5", style: {
57
- fontFamily: "var(--font-bangers, Bangers, cursive)",
58
+ fontFamily: FONT_BANGERS,
58
59
  letterSpacing: "0.1em",
59
60
  fontSize: "clamp(0.85rem, 2.5vw, 1.1rem)",
60
61
  background: "var(--color-yellow)",
@@ -78,7 +79,7 @@ export function HeroBanner({ banners, autoplayMs = 5000 }) {
78
79
  backdropFilter: "blur(6px)",
79
80
  }, children: _jsx("svg", { className: "h-4 w-4 sm:h-5 sm:w-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M9 5l7 7-7 7" }) }) })] })), banners.length > 1 && (_jsxs(Row, { className: "absolute bottom-8 right-6 sm:right-12 gap-3", style: { zIndex: 15 }, children: [_jsxs(Span, { className: "text-xs font-black tabular-nums", style: {
80
81
  color: "rgba(255,255,255,0.6)",
81
- fontFamily: "var(--font-bangers, Bangers, cursive)",
82
+ fontFamily: FONT_BANGERS,
82
83
  letterSpacing: "0.1em",
83
84
  }, children: [String(current + 1).padStart(2, "0"), " /", " ", String(banners.length).padStart(2, "0")] }), _jsx(Div, { className: "flex gap-1.5", children: banners.map((_, i) => (_jsx(Button, { onClick: () => goTo(i), "aria-label": `Go to slide ${i + 1}`, variant: "ghost", size: "sm", className: "transition-all", style: {
84
85
  height: 3,
@@ -8,6 +8,16 @@ import { MediaImage } from "../../media/MediaImage";
8
8
  import { MediaVideo } from "../../media/MediaVideo";
9
9
  import { useHeroCarousel } from "../hooks/useHeroCarousel";
10
10
  const { flex, position } = THEME_CONSTANTS;
11
+ function makeButtonClickHandler(href, openInNewTab, push) {
12
+ return () => {
13
+ if (openInNewTab) {
14
+ window.open(href, "_blank", "noopener,noreferrer");
15
+ }
16
+ else {
17
+ navigate(href, push);
18
+ }
19
+ };
20
+ }
11
21
  function navigate(href, push) {
12
22
  if (push) {
13
23
  push(href);
@@ -94,23 +104,8 @@ function CarouselCardRenderer({ card, isMobile, push, }) {
94
104
  const variant = btn.variant === "link" || btn.variant === "ghost"
95
105
  ? "ghost"
96
106
  : btn.variant;
97
- return (_jsx(Button, { variant: variant, size: "sm", onClick: () => {
98
- if (btn.openInNewTab) {
99
- window.open(btn.href, "_blank", "noopener,noreferrer");
100
- }
101
- else {
102
- navigate(btn.href, push);
103
- }
104
- }, children: btn.text }, btn.id ?? i));
105
- }) }))] })), card.isButtonOnly && card.buttons?.[0] && (_jsx(Button, { variant: "ghost", className: `${position.fill} ${flex.center} font-semibold text-white hover:bg-black/20 transition-colors rounded-none p-0`, onClick: () => {
106
- const btn = card.buttons[0];
107
- if (btn.openInNewTab) {
108
- window.open(btn.href, "_blank", "noopener,noreferrer");
109
- }
110
- else {
111
- navigate(btn.href, push);
112
- }
113
- }, children: _jsx(Span, { className: "text-lg md:text-2xl", children: card.buttons[0].text }) }))] }));
107
+ return (_jsx(Button, { variant: variant, size: "sm", onClick: makeButtonClickHandler(btn.href, btn.openInNewTab, push), children: btn.text }, btn.id ?? i));
108
+ }) }))] })), card.isButtonOnly && card.buttons?.[0] && (_jsx(Button, { variant: "ghost", className: `${position.fill} ${flex.center} font-semibold text-white hover:bg-black/20 transition-colors rounded-none p-0`, onClick: makeButtonClickHandler(card.buttons[0].href, card.buttons[0].openInNewTab, push), children: _jsx(Span, { className: "text-lg md:text-2xl", children: card.buttons[0].text }) }))] }));
114
109
  }
115
110
  export function HeroCarousel({ initialSlides, push } = {}) {
116
111
  const [currentSlide, setCurrentSlide] = useState(0);
@@ -174,15 +169,7 @@ export function HeroCarousel({ initialSlides, push } = {}) {
174
169
  return (_jsxs(Section, { ref: sectionRef, className: `relative w-full ${heightClass} overflow-hidden`, "aria-roledescription": "carousel", "aria-label": "Hero carousel", onKeyDown: handleKeyDown, onMouseEnter: () => setIsPaused(true), onMouseLeave: () => setIsPaused(false), onFocus: () => setIsPaused(true), onBlur: () => setIsPaused(false), tabIndex: 0, children: [_jsx(Div, { "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: `Slide ${currentSlide + 1} of ${slides.length}` }), _jsx(Div, { className: position.fill, children: _jsx(Div, { ref: slidesRef, onScroll: handleSlidesScroll, className: "flex h-full overflow-x-auto scroll-smooth snap-x snap-mandatory [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", style: { gap: 0 }, children: slides.map((slide, slideIndex) => {
175
170
  const slideHeightClass = getSlideHeightClass(slide.settings?.height);
176
171
  const hasCards = slide.cards && slide.cards.length > 0;
177
- return (_jsxs(Div, { className: `snap-start flex-none w-full relative self-stretch bg-zinc-900 ${slideHeightClass}`, children: [_jsx(Div, { className: position.fill, children: _jsx(SlideBackground, { bg: slide.background, legacy: slide.media, mobileLegacy: slide.mobileMedia, isMobile: isMobile, priority: slideIndex === 0 }) }), slide.overlay && (_jsxs(Div, { className: `${position.fill} ${flex.center} flex-col text-center px-6 md:px-16 lg:px-32`, children: [slide.overlay.subtitle && (_jsx(Text, { className: "stagger-1 text-xs md:text-sm !text-white/80 mb-1 md:mb-2 drop-shadow-sm uppercase tracking-widest", children: slide.overlay.subtitle })), slide.overlay.title && (_jsx(Heading, { level: 1, className: "stagger-2 font-display text-4xl md:text-6xl lg:text-8xl !text-white drop-shadow-2xl mb-2 md:mb-4", children: slide.overlay.title })), slide.overlay.description && (_jsx(Text, { className: "stagger-3 text-sm md:text-lg lg:text-xl !text-white/90 mb-4 md:mb-8 drop-shadow-sm max-w-2xl mx-auto", children: slide.overlay.description })), slide.overlay.button && (_jsx(Div, { className: "stagger-4", children: _jsx(Button, { variant: slide.overlay.button.variant, size: "sm", onClick: () => {
178
- const btn = slide.overlay.button;
179
- if (btn.openInNewTab) {
180
- window.open(btn.link, "_blank", "noopener,noreferrer");
181
- }
182
- else {
183
- navigate(btn.link, push);
184
- }
185
- }, children: slide.overlay.button.text }) }))] })), hasCards && (_jsx(Div, { className: `${position.fill} grid gap-2 md:gap-4 p-4 md:p-8`, style: {
172
+ return (_jsxs(Div, { className: `snap-start flex-none w-full relative self-stretch bg-zinc-900 ${slideHeightClass}`, children: [_jsx(Div, { className: position.fill, children: _jsx(SlideBackground, { bg: slide.background, legacy: slide.media, mobileLegacy: slide.mobileMedia, isMobile: isMobile, priority: slideIndex === 0 }) }), slide.overlay && (_jsxs(Div, { className: `${position.fill} ${flex.center} flex-col text-center px-6 md:px-16 lg:px-32`, children: [slide.overlay.subtitle && (_jsx(Text, { className: "stagger-1 text-xs md:text-sm !text-white/80 mb-1 md:mb-2 drop-shadow-sm uppercase tracking-widest", children: slide.overlay.subtitle })), slide.overlay.title && (_jsx(Heading, { level: 1, className: "stagger-2 font-display text-4xl md:text-6xl lg:text-8xl !text-white drop-shadow-2xl mb-2 md:mb-4", children: slide.overlay.title })), slide.overlay.description && (_jsx(Text, { className: "stagger-3 text-sm md:text-lg lg:text-xl !text-white/90 mb-4 md:mb-8 drop-shadow-sm max-w-2xl mx-auto", children: slide.overlay.description })), slide.overlay.button && (_jsx(Div, { className: "stagger-4", children: _jsx(Button, { variant: slide.overlay.button.variant, size: "sm", onClick: makeButtonClickHandler(slide.overlay.button.link, slide.overlay.button.openInNewTab, push), children: slide.overlay.button.text }) }))] })), hasCards && (_jsx(Div, { className: `${position.fill} grid gap-2 md:gap-4 p-4 md:p-8`, style: {
186
173
  gridTemplateRows: "repeat(2, 1fr)",
187
174
  gridTemplateColumns: isMobile ? "1fr" : "repeat(3, 1fr)",
188
175
  alignContent: "center",
@@ -3,6 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import { THEME_CONSTANTS } from "../../../tokens";
5
5
  import { Button, Div, Grid, Heading, Section, Text } from "../../../ui";
6
+ // --- Constants ---------------------------------------------------------------
7
+ const CLS_VISIBLE = "opacity-100 translate-y-0";
6
8
  // --- Single step card ---------------------------------------------------------
7
9
  function StepCard({ step, visible, delay, }) {
8
10
  const { badgeBg = "bg-primary", iconColor = "text-primary", iconBg = "bg-primary/5 dark:bg-primary/10", } = step;
@@ -11,7 +13,7 @@ function StepCard({ step, visible, delay, }) {
11
13
  "bg-white dark:bg-slate-900",
12
14
  "shadow-md group hover:-translate-y-2 hover:shadow-xl",
13
15
  "transition-all duration-300",
14
- visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8",
16
+ visible ? CLS_VISIBLE : "opacity-0 translate-y-8",
15
17
  ].join(" "), style: { transitionDelay: `${delay}ms` }, "data-section": "howitworkssection-div-340", children: [_jsx("div", { className: "absolute top-4 right-5 font-display text-7xl bg-gradient-to-br from-primary to-cobalt opacity-10 bg-clip-text text-transparent select-none pointer-events-none leading-none", "aria-hidden": "true", "data-section": "howitworkssection-div-341", children: step.number }), _jsx("div", { className: `relative z-10 w-10 h-10 rounded-full ${badgeBg} text-white font-bold text-sm flex items-center justify-center mb-5 shadow-md`, "data-section": "howitworkssection-div-342", children: step.number }), step.renderIcon && (_jsx("div", { className: `relative z-10 w-14 h-14 rounded-2xl ${iconBg} flex items-center justify-center mb-4 border border-white/80 dark:border-slate-700/50`, "data-section": "howitworkssection-div-343", children: _jsx("span", { className: `${iconColor}`, "aria-hidden": "true", children: step.renderIcon({ className: "w-6 h-6" }) }) })), _jsx(Heading, { level: 3, className: `relative z-10 text-base font-semibold ${THEME_CONSTANTS.themed.textPrimary} mb-2`, children: step.title }), _jsx(Text, { className: `relative z-10 text-sm ${THEME_CONSTANTS.themed.textSecondary} leading-relaxed`, children: step.desc })] }));
16
18
  }
17
19
  // --- Section -----------------------------------------------------------------
@@ -32,5 +34,5 @@ export function HowItWorksSection({ title, subtitle, pillLabel, ctaLabel, onCtaC
32
34
  observer.observe(el);
33
35
  return () => observer.disconnect();
34
36
  }, []);
35
- return (_jsx(Section, { ref: sectionRef, className: `p-8 ${themed.bgPrimary} ${className}`, children: _jsxs(Div, { className: "max-w-6xl mx-auto", children: [_jsxs("div", { className: `text-center mb-12 transition-all duration-700 ${visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`, "data-section": "howitworkssection-div-344", children: [pillLabel && (_jsx("div", { className: "inline-block mb-4 inline-flex items-center gap-2 rounded-full border border-primary-500/30 bg-primary-500/10 px-5 py-1.5 text-xs font-medium tracking-[0.2em] uppercase text-primary-700 dark:text-primary-400 backdrop-blur-sm", "data-section": "howitworkssection-div-345", children: pillLabel })), _jsx(Heading, { level: 2, className: `text-3xl md:text-4xl font-bold ${themed.textPrimary} mb-3`, children: title }), subtitle && (_jsx(Text, { className: `text-base ${themed.textSecondary} max-w-xl mx-auto`, children: subtitle }))] }), _jsx(Grid, { gap: "lg", className: "grid-cols-1 md:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-3 mb-10", children: steps.map((step, i) => (_jsx(StepCard, { step: step, visible: visible, delay: i * 150 }, step.number))) }), ctaLabel && onCtaClick && (_jsx("div", { className: `text-center transition-all duration-700 delay-500 ${visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`, "data-section": "howitworkssection-div-346", children: _jsx(Button, { variant: "primary", size: "lg", onClick: onCtaClick, children: ctaLabel }) }))] }) }));
37
+ return (_jsx(Section, { ref: sectionRef, className: `p-8 ${themed.bgPrimary} ${className}`, children: _jsxs(Div, { className: "max-w-6xl mx-auto", children: [_jsxs("div", { className: `text-center mb-12 transition-all duration-700 ${visible ? CLS_VISIBLE : "opacity-0 translate-y-4"}`, "data-section": "howitworkssection-div-344", children: [pillLabel && (_jsx("div", { className: "inline-block mb-4 inline-flex items-center gap-2 rounded-full border border-primary-500/30 bg-primary-500/10 px-5 py-1.5 text-xs font-medium tracking-[0.2em] uppercase text-primary-700 dark:text-primary-400 backdrop-blur-sm", "data-section": "howitworkssection-div-345", children: pillLabel })), _jsx(Heading, { level: 2, className: `text-3xl md:text-4xl font-bold ${themed.textPrimary} mb-3`, children: title }), subtitle && (_jsx(Text, { className: `text-base ${themed.textSecondary} max-w-xl mx-auto`, children: subtitle }))] }), _jsx(Grid, { gap: "lg", className: "grid-cols-1 md:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-3 mb-10", children: steps.map((step, i) => (_jsx(StepCard, { step: step, visible: visible, delay: i * 150 }, step.number))) }), ctaLabel && onCtaClick && (_jsx("div", { className: `text-center transition-all duration-700 delay-500 ${visible ? CLS_VISIBLE : "opacity-0 translate-y-4"}`, "data-section": "howitworkssection-div-346", children: _jsx(Button, { variant: "primary", size: "lg", onClick: onCtaClick, children: ctaLabel }) }))] }) }));
36
38
  }
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Heading, Section, Text } from "../../../ui";
3
3
  import { THEME_CONSTANTS } from "../../../tokens";
4
4
  import { SocialPostCard } from "./SocialPostCard";
@@ -102,5 +102,5 @@ export async function SocialFeedSection(config) {
102
102
  : layout === "masonry"
103
103
  ? "break-inside-avoid"
104
104
  : "";
105
- return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsxs("div", { className: "w-full max-w-7xl mx-auto px-4", children: [_jsxs("div", { className: "mb-8 flex items-end justify-between gap-4", children: [_jsxs("div", { children: [_jsx(Heading, { level: 2, className: "mb-1", children: title || `${PLATFORM_LABELS[platform]} Feed` }), subtitle && (_jsx(Text, { size: "sm", variant: "muted", children: subtitle }))] }), profileUrl && (_jsxs("a", { href: profileUrl, target: "_blank", rel: "noopener noreferrer", className: "shrink-0 text-sm font-medium text-[var(--appkit-color-primary)] hover:opacity-80", children: ["Follow on ", PLATFORM_LABELS[platform], " \u2192"] }))] }), error ? (_jsx("div", { className: "py-12 text-center text-zinc-400 text-sm", children: error })) : posts.length === 0 ? (_jsx(SocialFeedEmpty, { platform: platform })) : (_jsx("div", { className: gridClass, children: posts.slice(0, count).map((post) => (_jsx("div", { className: cardClass, children: _jsx(SocialPostCard, { post: post, showCaption: showCaption, showStats: showStats }) }, post.id))) }))] }) }));
105
+ return (_jsx(Section, { className: `py-12 ${themed.bgPrimary}`, children: _jsxs("div", { className: "w-full max-w-7xl mx-auto px-4", children: [_jsxs("div", { className: "mb-8 flex items-end justify-between gap-4", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 2, className: "mb-1", children: title || `${PLATFORM_LABELS[platform]} Feed` }), subtitle && (_jsx(Text, { size: "sm", variant: "muted", children: subtitle }))] }), profileUrl && (_jsxs("a", { href: profileUrl, target: "_blank", rel: "noopener noreferrer", className: "shrink-0 text-sm font-medium text-[var(--appkit-color-primary)] hover:opacity-80", children: ["Follow on ", PLATFORM_LABELS[platform], " \u2192"] }))] }), error ? (_jsx("div", { className: "py-12 text-center text-zinc-400 text-sm", children: error })) : posts.length === 0 ? (_jsx(SocialFeedEmpty, { platform: platform })) : (_jsx("div", { className: gridClass, children: posts.slice(0, count).map((post) => (_jsx("div", { className: cardClass, children: _jsx(SocialPostCard, { post: post, showCaption: showCaption, showStats: showStats }) }, post.id))) }))] }) }));
106
106
  }
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Text } from "../../../ui";
3
4
  // --- Platform brand colours & icons ------------------------------------------
4
5
  const PLATFORM_META = {
5
6
  instagram: {
@@ -43,12 +44,12 @@ function YouTubeCard({ post, showCaption }) {
43
44
  const thumbnail = `https://img.youtube.com/vi/${post.videoId}/maxresdefault.jpg`;
44
45
  const href = `https://youtu.be/${post.videoId}`;
45
46
  const meta = PLATFORM_META.youtube;
46
- return (_jsxs("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: "group relative block aspect-video w-full overflow-hidden rounded-xl bg-zinc-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--appkit-color-youtube,#FF0000)]", "aria-label": post.caption ?? `Watch on YouTube`, children: [_jsx("img", { src: thumbnail, alt: post.caption ?? "YouTube video", className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105", loading: "lazy" }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx("div", { className: "flex h-14 w-14 items-center justify-center rounded-full bg-[var(--appkit-color-youtube,#FF0000)] shadow-lg transition-transform duration-200 group-hover:scale-110", children: _jsx("svg", { className: "ml-1 h-6 w-6 text-white", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M8 5v14l11-7z" }) }) }) }), _jsxs("div", { className: `absolute right-2 top-2 flex items-center gap-1 rounded px-1.5 py-1 text-xs font-medium text-white ${meta.colorClass}`, children: [meta.icon, _jsx("span", { className: "hidden sm:inline", children: meta.label })] }), post.channelName && (_jsx("div", { className: "absolute bottom-2 left-2 rounded bg-black/60 px-2 py-0.5 text-xs text-white backdrop-blur-sm", children: post.channelName })), showCaption && post.caption && (_jsx("div", { className: "absolute inset-0 flex flex-col justify-end bg-black/60 p-3 opacity-0 transition-opacity duration-200 group-hover:opacity-100", children: _jsx("p", { className: "line-clamp-2 text-xs text-white", children: post.caption }) }))] }));
47
+ return (_jsxs("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: "group relative block aspect-video w-full overflow-hidden rounded-xl bg-zinc-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--appkit-color-youtube,#FF0000)]", "aria-label": post.caption ?? `Watch on YouTube`, children: [_jsx("img", { src: thumbnail, alt: post.caption ?? "YouTube video", className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105", loading: "lazy" }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx("div", { className: "flex h-14 w-14 items-center justify-center rounded-full bg-[var(--appkit-color-youtube,#FF0000)] shadow-lg transition-transform duration-200 group-hover:scale-110", children: _jsx("svg", { className: "ml-1 h-6 w-6 text-white", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M8 5v14l11-7z" }) }) }) }), _jsxs("div", { className: `absolute right-2 top-2 flex items-center gap-1 rounded px-1.5 py-1 text-xs font-medium text-white ${meta.colorClass}`, children: [meta.icon, _jsx("span", { className: "hidden sm:inline", children: meta.label })] }), post.channelName && (_jsx("div", { className: "absolute bottom-2 left-2 rounded bg-black/60 px-2 py-0.5 text-xs text-white backdrop-blur-sm", children: post.channelName })), showCaption && post.caption && (_jsx("div", { className: "absolute inset-0 flex flex-col justify-end bg-black/60 p-3 opacity-0 transition-opacity duration-200 group-hover:opacity-100", children: _jsx(Text, { className: "line-clamp-2 text-xs text-white", children: post.caption }) }))] }));
47
48
  }
48
49
  export function SocialPostCard({ post, showCaption = true, showStats = true }) {
49
50
  if (post.platform === "youtube") {
50
51
  return _jsx(YouTubeCard, { post: post, showCaption: showCaption });
51
52
  }
52
53
  const meta = PLATFORM_META[post.platform];
53
- return (_jsxs("a", { href: post.permalink, target: "_blank", rel: "noopener noreferrer", className: "group relative block aspect-square overflow-hidden rounded-xl bg-zinc-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--appkit-color-primary)] dark:bg-zinc-800", "aria-label": post.caption ?? `View on ${meta.label}`, children: [post.imageUrl ? (_jsx("img", { src: post.imageUrl, alt: post.caption ?? `${meta.label} post`, className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105", loading: "lazy" })) : (_jsx("div", { className: "flex h-full w-full items-center justify-center bg-zinc-200 dark:bg-zinc-700", children: _jsx("span", { className: "text-sm text-zinc-400", children: "No preview" }) })), post.mediaType === "video" && (_jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: _jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-black/50 backdrop-blur-sm", children: _jsx("svg", { className: "ml-1 h-5 w-5 text-white", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M8 5v14l11-7z" }) }) }) })), post.mediaType === "carousel" && (_jsx("div", { className: "pointer-events-none absolute right-2 top-2", children: _jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded bg-black/50 backdrop-blur-sm", children: _jsxs("svg", { className: "h-3.5 w-3.5 text-white", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "2", y: "3", width: "15", height: "15", rx: "2" }), _jsx("path", { d: "M7 8h14M7 12h14M7 16h14" })] }) }) })), _jsxs("div", { className: `absolute left-2 top-2 flex items-center gap-1 rounded px-1.5 py-1 text-xs font-medium text-white ${meta.colorClass}`, children: [meta.icon, _jsx("span", { className: "hidden sm:inline", children: meta.label })] }), _jsxs("div", { className: "pointer-events-none absolute inset-0 flex flex-col justify-end bg-black/70 p-3 opacity-0 transition-opacity duration-200 group-hover:opacity-100", children: [showCaption && post.caption && (_jsx("p", { className: "mb-2 line-clamp-3 text-xs text-white", children: post.caption })), showStats && (_jsxs("div", { className: "flex items-center gap-3 text-xs text-white/80", children: [post.stats.likes != null && (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" }) }), formatCount(post.stats.likes)] })), post.stats.views != null && (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" }) }), formatCount(post.stats.views)] })), post.stats.comments != null && (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }), formatCount(post.stats.comments)] }))] }))] })] }));
54
+ return (_jsxs("a", { href: post.permalink, target: "_blank", rel: "noopener noreferrer", className: "group relative block aspect-square overflow-hidden rounded-xl bg-zinc-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--appkit-color-primary)] dark:bg-zinc-800", "aria-label": post.caption ?? `View on ${meta.label}`, children: [post.imageUrl ? (_jsx("img", { src: post.imageUrl, alt: post.caption ?? `${meta.label} post`, className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105", loading: "lazy" })) : (_jsx("div", { className: "flex h-full w-full items-center justify-center bg-zinc-200 dark:bg-zinc-700", children: _jsx("span", { className: "text-sm text-zinc-400", children: "No preview" }) })), post.mediaType === "video" && (_jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: _jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-black/50 backdrop-blur-sm", children: _jsx("svg", { className: "ml-1 h-5 w-5 text-white", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M8 5v14l11-7z" }) }) }) })), post.mediaType === "carousel" && (_jsx("div", { className: "pointer-events-none absolute right-2 top-2", children: _jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded bg-black/50 backdrop-blur-sm", children: _jsxs("svg", { className: "h-3.5 w-3.5 text-white", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "2", y: "3", width: "15", height: "15", rx: "2" }), _jsx("path", { d: "M7 8h14M7 12h14M7 16h14" })] }) }) })), _jsxs("div", { className: `absolute left-2 top-2 flex items-center gap-1 rounded px-1.5 py-1 text-xs font-medium text-white ${meta.colorClass}`, children: [meta.icon, _jsx("span", { className: "hidden sm:inline", children: meta.label })] }), _jsxs("div", { className: "pointer-events-none absolute inset-0 flex flex-col justify-end bg-black/70 p-3 opacity-0 transition-opacity duration-200 group-hover:opacity-100", children: [showCaption && post.caption && (_jsx(Text, { className: "mb-2 line-clamp-3 text-xs text-white", children: post.caption })), showStats && (_jsxs("div", { className: "flex items-center gap-3 text-xs text-white/80", children: [post.stats.likes != null && (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" }) }), formatCount(post.stats.likes)] })), post.stats.views != null && (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" }) }), formatCount(post.stats.views)] })), post.stats.comments != null && (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }), formatCount(post.stats.comments)] }))] }))] })] }));
54
55
  }
@@ -2,6 +2,9 @@
2
2
  function clamp(n, min, max) {
3
3
  return Math.max(min, Math.min(max, n));
4
4
  }
5
+ function isVideoOrReels(postType) {
6
+ return ["videos", "reels"].includes(postType);
7
+ }
5
8
  // --- Instagram (Meta Graph API v19) ------------------------------------------
6
9
  export async function fetchInstagramPosts(handle, postType, count, accessToken) {
7
10
  const fields = "id,caption,media_type,media_url,thumbnail_url,permalink,timestamp,like_count,comments_count";
@@ -19,7 +22,7 @@ export async function fetchInstagramPosts(handle, postType, count, accessToken)
19
22
  const m = item;
20
23
  if (postType === "images")
21
24
  return m.media_type === "IMAGE";
22
- if (postType === "videos" || postType === "reels")
25
+ if (isVideoOrReels(postType))
23
26
  return m.media_type === "VIDEO";
24
27
  return true;
25
28
  })
@@ -65,7 +68,7 @@ export async function fetchFacebookPosts(handle, postType, count, accessToken) {
65
68
  const mtype = attachType?.media_type;
66
69
  if (postType === "images")
67
70
  return mtype !== "video";
68
- if (postType === "videos" || postType === "reels")
71
+ if (isVideoOrReels(postType))
69
72
  return mtype === "video";
70
73
  return true;
71
74
  })
@@ -155,7 +158,7 @@ async function getDeviantArtToken(clientId, clientSecret) {
155
158
  return json.access_token;
156
159
  }
157
160
  export async function fetchDeviantArtPosts(handle, postType, count, clientId, clientSecret) {
158
- if (postType === "videos" || postType === "reels")
161
+ if (isVideoOrReels(postType))
159
162
  return [];
160
163
  const token = await getDeviantArtToken(clientId, clientSecret);
161
164
  const url = new URL("https://www.deviantart.com/api/v1/oauth2/gallery/all");
@@ -18,7 +18,7 @@ export interface AppLayoutShellSidebarSection {
18
18
  export interface AppLayoutShellSidebarAction {
19
19
  href: string;
20
20
  label: string;
21
- variant?: "solid" | "outline";
21
+ variant?: "primary" | "outline";
22
22
  }
23
23
  export interface AppLayoutShellProps {
24
24
  children: React.ReactNode;
@@ -88,6 +88,12 @@ export interface AppLayoutShellProps {
88
88
  logout?: string;
89
89
  };
90
90
  eventBannerSlot?: React.ReactNode;
91
+ /**
92
+ * Override className for the main content wrapper div.
93
+ * When set, replaces the default `container mx-auto w-full max-w-screen-2xl px-4 py-6 …`
94
+ * Use `"w-full"` for dashboard layouts that provide their own padding via DashboardLayoutClient.
95
+ */
96
+ contentClassName?: string;
91
97
  lightBackground?: {
92
98
  type: "color" | "image" | "gradient" | "video";
93
99
  value: string;
@@ -107,4 +113,4 @@ export interface AppLayoutShellProps {
107
113
  };
108
114
  };
109
115
  }
110
- export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, lightBackground, darkBackground, }: AppLayoutShellProps): import("react/jsx-runtime").JSX.Element;
116
+ export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, contentClassName, lightBackground, darkBackground, }: AppLayoutShellProps): import("react/jsx-runtime").JSX.Element;