@mohasinac/appkit 2.7.56 → 2.7.57

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 (276) hide show
  1. package/dist/_internal/shared/actions/action-registry.js +31 -5
  2. package/dist/client.d.ts +1 -3
  3. package/dist/client.js +1 -2
  4. package/dist/constants/field-names.d.ts +3 -0
  5. package/dist/constants/field-names.js +3 -0
  6. package/dist/features/about/components/FAQPageView.js +2 -2
  7. package/dist/features/about/components/FeesView.js +1 -1
  8. package/dist/features/about/components/PolicyPageView.js +1 -1
  9. package/dist/features/about/components/PublicProfileView.js +1 -1
  10. package/dist/features/account/components/AddressesIndexListing.js +1 -1
  11. package/dist/features/account/components/MessagesView.js +1 -1
  12. package/dist/features/account/components/NotificationPreferencesPanel.js +12 -3
  13. package/dist/features/account/components/UserOffersPanel.js +2 -2
  14. package/dist/features/account/components/UserSidebar.js +3 -3
  15. package/dist/features/account/components/UserSupportView.js +1 -1
  16. package/dist/features/admin/components/AdminAllEventEntriesView.js +1 -1
  17. package/dist/features/admin/components/AdminBidsView.js +5 -1
  18. package/dist/features/admin/components/AdminBlogView.js +6 -1
  19. package/dist/features/admin/components/AdminBrandsView.js +6 -1
  20. package/dist/features/admin/components/AdminBundleEditorView.js +13 -9
  21. package/dist/features/admin/components/AdminBundlesView.js +3 -1
  22. package/dist/features/admin/components/AdminCarouselEditorView.js +7 -8
  23. package/dist/features/admin/components/AdminCarouselView.js +4 -2
  24. package/dist/features/admin/components/AdminCartsView.js +1 -1
  25. package/dist/features/admin/components/AdminCategoriesView.js +6 -1
  26. package/dist/features/admin/components/AdminContactView.js +3 -1
  27. package/dist/features/admin/components/AdminCouponsView.js +4 -2
  28. package/dist/features/admin/components/AdminEmployeeEditorView.js +1 -1
  29. package/dist/features/admin/components/AdminFaqsView.js +3 -1
  30. package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -1
  31. package/dist/features/admin/components/AdminFeaturesView.js +4 -0
  32. package/dist/features/admin/components/AdminMediaView.js +10 -5
  33. package/dist/features/admin/components/AdminNavigationView.js +1 -1
  34. package/dist/features/admin/components/AdminNewsletterView.js +3 -1
  35. package/dist/features/admin/components/AdminNotificationsView.js +6 -1
  36. package/dist/features/admin/components/AdminOrdersView.js +10 -1
  37. package/dist/features/admin/components/AdminPayoutsView.js +5 -1
  38. package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -0
  39. package/dist/features/admin/components/AdminProductsView.js +13 -1
  40. package/dist/features/admin/components/AdminReviewsView.js +6 -1
  41. package/dist/features/admin/components/AdminScammersView.js +1 -1
  42. package/dist/features/admin/components/AdminSectionsView.js +1 -1
  43. package/dist/features/admin/components/AdminSessionsView.js +5 -1
  44. package/dist/features/admin/components/AdminSidebar.js +3 -3
  45. package/dist/features/admin/components/AdminSiteSettingsView.js +1 -1
  46. package/dist/features/admin/components/AdminStoreEditorView.js +1 -1
  47. package/dist/features/admin/components/AdminStoresView.js +6 -1
  48. package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
  49. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  50. package/dist/features/admin/components/AdminSupportTicketsView.js +1 -1
  51. package/dist/features/admin/components/AdminTeamView.js +3 -1
  52. package/dist/features/admin/components/AdminUserEditorView.js +1 -1
  53. package/dist/features/admin/components/AdminUsersView.js +5 -1
  54. package/dist/features/admin/components/AdminViewCards.js +2 -2
  55. package/dist/features/admin/constants/filter-tabs.d.ts +9 -0
  56. package/dist/features/admin/constants/filter-tabs.js +3 -0
  57. package/dist/features/auctions/components/AuctionBottomActions.d.ts +7 -0
  58. package/dist/features/auctions/components/AuctionBottomActions.js +24 -0
  59. package/dist/features/auctions/components/AuctionDetailPageView.js +7 -7
  60. package/dist/features/auctions/components/AuctionsListView.js +1 -1
  61. package/dist/features/auctions/components/MarketplaceAuctionGrid.js +1 -1
  62. package/dist/features/auctions/components/PlaceBidFormClient.js +1 -1
  63. package/dist/features/auth/components/LoginForm.js +1 -1
  64. package/dist/features/auth/components/VerifyEmailView.js +2 -2
  65. package/dist/features/before-after/components/BeforeAfterGallery.js +1 -1
  66. package/dist/features/blog/components/BlogFeaturedCard.js +1 -1
  67. package/dist/features/blog/components/BlogIndexListing.js +1 -1
  68. package/dist/features/blog/components/BlogListView.js +2 -2
  69. package/dist/features/blog/components/BlogPostView.js +8 -8
  70. package/dist/features/cart/components/CartDrawer.js +1 -1
  71. package/dist/features/cart/repository/cart.repository.js +11 -4
  72. package/dist/features/cart/schemas/firestore.d.ts +3 -0
  73. package/dist/features/categories/components/BrandDetailPageView.js +1 -1
  74. package/dist/features/categories/components/CategoriesIndexListing.js +1 -1
  75. package/dist/features/categories/components/CategoryDetailPageView.js +39 -3
  76. package/dist/features/categories/components/CategoryDetailTabs.d.ts +4 -1
  77. package/dist/features/categories/components/CategoryDetailTabs.js +7 -2
  78. package/dist/features/categories/components/CategoryGrid.js +1 -1
  79. package/dist/features/categories/components/CategoryProductsView.js +1 -1
  80. package/dist/features/categories/components/CategoryStoresListing.d.ts +5 -0
  81. package/dist/features/categories/components/CategoryStoresListing.js +11 -0
  82. package/dist/features/categories/components/CategoryTableColumns.js +2 -2
  83. package/dist/features/categories/components/CategoryTree.js +1 -1
  84. package/dist/features/categories/components/index.d.ts +2 -0
  85. package/dist/features/categories/components/index.js +1 -0
  86. package/dist/features/checkout/components/CheckoutStepper.js +1 -1
  87. package/dist/features/classified/components/ClassifiedIndexListing.js +2 -0
  88. package/dist/features/classified/components/ClassifiedListView.js +1 -1
  89. package/dist/features/contact/components/ContactInfoSidebar.js +1 -1
  90. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +2 -0
  91. package/dist/features/digital-codes/components/DigitalCodesListView.js +1 -1
  92. package/dist/features/events/components/AdminEventsView.js +5 -1
  93. package/dist/features/events/components/EventCard.js +1 -1
  94. package/dist/features/events/components/EventPollWidget.js +1 -1
  95. package/dist/features/events/components/EventStatusBadge.js +5 -5
  96. package/dist/features/events/components/EventsIndexListing.js +1 -1
  97. package/dist/features/events/components/EventsListView.js +2 -2
  98. package/dist/features/events/components/SpinWheelView.js +4 -2
  99. package/dist/features/filters/AsyncFacetSection.js +3 -3
  100. package/dist/features/filters/FilterFacetSection.js +5 -5
  101. package/dist/features/filters/RangeFilter.js +1 -1
  102. package/dist/features/forms/Input.js +2 -2
  103. package/dist/features/forms/Select.js +1 -1
  104. package/dist/features/forms/Slider.js +1 -1
  105. package/dist/features/forms/Textarea.js +1 -1
  106. package/dist/features/forms/utils.d.ts +1 -1
  107. package/dist/features/forms/utils.js +1 -1
  108. package/dist/features/history/hooks/useHistory.js +23 -10
  109. package/dist/features/homepage/components/AdvertisementBanner.js +2 -2
  110. package/dist/features/homepage/components/FeaturedResultsSection.js +1 -1
  111. package/dist/features/homepage/components/HeroSection.js +1 -1
  112. package/dist/features/homepage/components/NewsletterSection.js +1 -1
  113. package/dist/features/homepage/components/SectionCarousel.js +1 -1
  114. package/dist/features/live/components/LiveItemsIndexListing.js +2 -0
  115. package/dist/features/live/components/LiveItemsListView.js +1 -1
  116. package/dist/features/orders/components/OrdersList.js +12 -12
  117. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
  118. package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
  119. package/dist/features/pre-orders/components/PreOrderBadge.js +5 -5
  120. package/dist/features/pre-orders/components/PreOrderBottomActions.d.ts +5 -0
  121. package/dist/features/pre-orders/components/PreOrderBottomActions.js +22 -0
  122. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +5 -5
  123. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +47 -0
  124. package/dist/features/pre-orders/components/PreOrdersListView.js +1 -1
  125. package/dist/features/pre-orders/components/PreorderCard.js +3 -3
  126. package/dist/features/products/components/AuctionsIndexListing.js +23 -0
  127. package/dist/features/products/components/BidHistory.js +2 -2
  128. package/dist/features/products/components/CompareOverlay.js +1 -1
  129. package/dist/features/products/components/CustomFieldsEditor.js +1 -1
  130. package/dist/features/products/components/CustomSectionTabContent.js +1 -1
  131. package/dist/features/products/components/CustomSectionsEditor.js +1 -1
  132. package/dist/features/products/components/GroupSettingsPanel.js +2 -2
  133. package/dist/features/products/components/MakeOfferButton.js +1 -1
  134. package/dist/features/products/components/PrizeDrawBottomActions.d.ts +6 -0
  135. package/dist/features/products/components/PrizeDrawBottomActions.js +24 -0
  136. package/dist/features/products/components/PrizeDrawDetailPageView.js +3 -5
  137. package/dist/features/products/components/PrizeDrawEntryActions.js +5 -18
  138. package/dist/features/products/components/PrizeRevealModal.js +5 -2
  139. package/dist/features/products/components/ProductDetailActions.d.ts +6 -1
  140. package/dist/features/products/components/ProductDetailActions.js +36 -6
  141. package/dist/features/products/components/ProductDetailPageView.js +17 -16
  142. package/dist/features/products/components/ProductGalleryClient.js +1 -1
  143. package/dist/features/products/components/ProductGrid.js +6 -6
  144. package/dist/features/products/components/ProductTabs.js +1 -1
  145. package/dist/features/products/components/ProductTabsShell.js +1 -1
  146. package/dist/features/products/components/ProductsIndexListing.js +47 -0
  147. package/dist/features/products/components/RelatedProducts.js +1 -1
  148. package/dist/features/products/components/ShowGroupSection.js +1 -1
  149. package/dist/features/products/components/SublistingCarouselSection.js +1 -1
  150. package/dist/features/products/components/SublistingCategorySelect.js +1 -1
  151. package/dist/features/products/components/index.d.ts +0 -2
  152. package/dist/features/products/components/index.js +0 -1
  153. package/dist/features/products/constants/action-defs.d.ts +7 -0
  154. package/dist/features/products/constants/action-defs.js +14 -0
  155. package/dist/features/products/constants/listing-tabs.d.ts +6 -0
  156. package/dist/features/products/constants/listing-tabs.js +1 -0
  157. package/dist/features/promotions/components/CouponCard.js +2 -2
  158. package/dist/features/promotions/components/CouponsIndexListing.js +2 -2
  159. package/dist/features/reviews/components/ReviewDetailPageView.js +1 -1
  160. package/dist/features/reviews/components/ReviewDetailShell.js +3 -3
  161. package/dist/features/reviews/components/ReviewModal.js +2 -2
  162. package/dist/features/reviews/components/ReviewSummary.js +1 -1
  163. package/dist/features/reviews/components/ReviewsIndexListing.js +1 -1
  164. package/dist/features/reviews/components/ReviewsList.js +1 -1
  165. package/dist/features/seller/components/SellerAddressesView.js +4 -3
  166. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +1 -1
  167. package/dist/features/seller/components/SellerAuctionsView.js +10 -3
  168. package/dist/features/seller/components/SellerBidsView.js +22 -7
  169. package/dist/features/seller/components/SellerBundlesView.js +10 -3
  170. package/dist/features/seller/components/SellerClassifiedView.js +10 -3
  171. package/dist/features/seller/components/SellerCouponsView.js +13 -4
  172. package/dist/features/seller/components/SellerDigitalCodesView.js +10 -3
  173. package/dist/features/seller/components/SellerGoogleReviewsView.js +10 -5
  174. package/dist/features/seller/components/SellerGroupedListingsView.js +2 -0
  175. package/dist/features/seller/components/SellerLiveView.js +10 -3
  176. package/dist/features/seller/components/SellerOffersPanel.js +2 -2
  177. package/dist/features/seller/components/SellerOffersView.js +1 -1
  178. package/dist/features/seller/components/SellerOrdersView.js +40 -17
  179. package/dist/features/seller/components/SellerPayoutMethodsView.js +13 -4
  180. package/dist/features/seller/components/SellerPayoutsView.js +1 -1
  181. package/dist/features/seller/components/SellerPreOrdersView.js +7 -2
  182. package/dist/features/seller/components/SellerPrizeDrawsView.js +7 -2
  183. package/dist/features/seller/components/SellerProductShell.js +27 -9
  184. package/dist/features/seller/components/SellerProductsCards.js +2 -1
  185. package/dist/features/seller/components/SellerProductsView.js +21 -8
  186. package/dist/features/seller/components/SellerReviewsView.js +8 -0
  187. package/dist/features/seller/components/SellerShippingConfigsView.js +13 -4
  188. package/dist/features/seller/components/SellerSidebar.js +2 -2
  189. package/dist/features/seller/components/SellerStoreCategoriesView.js +10 -3
  190. package/dist/features/seller/components/SellerTemplatesView.js +19 -6
  191. package/dist/features/shell/StepForm.js +9 -3
  192. package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
  193. package/dist/features/site-settings/components/NavPermissionsManager.js +1 -1
  194. package/dist/features/stores/components/InteractiveStoreCard.js +1 -1
  195. package/dist/features/stores/components/StoreDetailLayoutView.js +1 -1
  196. package/dist/features/stores/components/StoreReviewsListing.js +1 -1
  197. package/dist/features/stores/components/StoresIndexListing.js +17 -1
  198. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +1 -1
  199. package/dist/features/wishlist/components/WishlistPage.js +2 -2
  200. package/dist/features/wishlist/hooks/useWishlistToggle.js +6 -1
  201. package/dist/index.d.ts +3 -2
  202. package/dist/index.js +6 -3
  203. package/dist/react/hooks/use-action-dispatch.d.ts +5 -0
  204. package/dist/react/hooks/use-action-dispatch.js +10 -0
  205. package/dist/seed/cart-seed-data.js +50 -3
  206. package/dist/seed/categories-seed-data.js +80 -16
  207. package/dist/seed/index.d.ts +3 -0
  208. package/dist/seed/index.js +3 -0
  209. package/dist/seed/manifest.js +15 -1
  210. package/dist/seed/products-classifieds-seed-data.d.ts +10 -0
  211. package/dist/seed/products-classifieds-seed-data.js +197 -0
  212. package/dist/seed/products-digital-codes-seed-data.d.ts +10 -0
  213. package/dist/seed/products-digital-codes-seed-data.js +204 -0
  214. package/dist/seed/products-live-items-seed-data.d.ts +13 -0
  215. package/dist/seed/products-live-items-seed-data.js +190 -0
  216. package/dist/styles.css +16 -1
  217. package/dist/tailwind-utilities.css +1 -1
  218. package/dist/tokens/index.d.ts +3 -3
  219. package/dist/tokens/index.js +3 -3
  220. package/dist/ui/DataTable.js +3 -3
  221. package/dist/ui/components/Button.style.css +15 -0
  222. package/dist/ui/components/Input.js +1 -1
  223. package/dist/ui/components/ListingToolbar.js +1 -1
  224. package/dist/ui/components/Select.js +1 -1
  225. package/dist/ui/components/StatsGrid.js +1 -1
  226. package/dist/ui/index.d.ts +0 -4
  227. package/dist/ui/index.js +0 -2
  228. package/package.json +1 -1
  229. package/dist/_internal/client/features/seller/print-center/InventoryLabel.d.ts +0 -12
  230. package/dist/_internal/client/features/seller/print-center/InventoryLabel.js +0 -49
  231. package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.d.ts +0 -7
  232. package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.js +0 -32
  233. package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.d.ts +0 -28
  234. package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.js +0 -33
  235. package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.d.ts +0 -12
  236. package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.js +0 -30
  237. package/dist/_internal/client/features/seller/print-center/PrintCenterView.d.ts +0 -12
  238. package/dist/_internal/client/features/seller/print-center/PrintCenterView.js +0 -102
  239. package/dist/_internal/client/features/seller/print-center/PrintGrid.d.ts +0 -12
  240. package/dist/_internal/client/features/seller/print-center/PrintGrid.js +0 -13
  241. package/dist/_internal/client/features/seller/print-center/StoreCard.d.ts +0 -19
  242. package/dist/_internal/client/features/seller/print-center/StoreCard.js +0 -30
  243. package/dist/_internal/client/features/seller/print-center/WebsiteCard.d.ts +0 -11
  244. package/dist/_internal/client/features/seller/print-center/WebsiteCard.js +0 -25
  245. package/dist/_internal/client/features/seller/print-center/index.d.ts +0 -11
  246. package/dist/_internal/client/features/seller/print-center/index.js +0 -10
  247. package/dist/_internal/client/features/seller/print-center/types.d.ts +0 -20
  248. package/dist/_internal/client/features/seller/print-center/types.js +0 -20
  249. package/dist/_internal/client/features/seller/print-center/useInventoryPdf.d.ts +0 -18
  250. package/dist/_internal/client/features/seller/print-center/useInventoryPdf.js +0 -76
  251. package/dist/features/account/repository/address.repository.d.ts +0 -17
  252. package/dist/features/account/repository/address.repository.js +0 -184
  253. package/dist/features/auctions/components/AuctionCard.d.ts +0 -29
  254. package/dist/features/auctions/components/AuctionCard.js +0 -38
  255. package/dist/features/categories/components/BundleAddToCartCta.d.ts +0 -16
  256. package/dist/features/categories/components/BundleAddToCartCta.js +0 -54
  257. package/dist/features/categories/components/CategoriesListView.d.ts +0 -31
  258. package/dist/features/categories/components/CategoriesListView.js +0 -27
  259. package/dist/features/products/components/AuctionsView.d.ts +0 -6
  260. package/dist/features/products/components/AuctionsView.js +0 -5
  261. package/dist/features/products/components/BuyBar.d.ts +0 -21
  262. package/dist/features/products/components/BuyBar.js +0 -19
  263. package/dist/features/products/components/PreOrdersView.d.ts +0 -6
  264. package/dist/features/products/components/PreOrdersView.js +0 -5
  265. package/dist/features/reviews/components/ReviewsListView.d.ts +0 -31
  266. package/dist/features/reviews/components/ReviewsListView.js +0 -16
  267. package/dist/features/stores/components/StoresListView.d.ts +0 -19
  268. package/dist/features/stores/components/StoresListView.js +0 -25
  269. package/dist/features/stores/repository/store-address.repository.d.ts +0 -55
  270. package/dist/features/stores/repository/store-address.repository.js +0 -187
  271. package/dist/ui/components/BulkActionsBar.d.ts +0 -18
  272. package/dist/ui/components/BulkActionsBar.js +0 -25
  273. package/dist/ui/components/CollageLightbox.d.ts +0 -18
  274. package/dist/ui/components/CollageLightbox.js +0 -41
  275. package/dist/ui/components/StickyBottomBar.d.ts +0 -22
  276. package/dist/ui/components/StickyBottomBar.js +0 -20
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "crypto";
2
- import { DatabaseError, NotFoundError } from "../../../errors";
2
+ import { DatabaseError, NotFoundError, ValidationError } from "../../../errors";
3
3
  const ERR_CART_ITEM_NOT_FOUND = "Cart item not found";
4
+ const ERR_CART_ITEM_LOCKED = "This item requires payment and cannot be removed or modified.";
4
5
  import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
5
6
  import { decryptPii } from "../../../security";
6
7
  import { CART_COLLECTION, } from "../schemas";
@@ -85,6 +86,7 @@ export class CartRepository extends BaseRepository {
85
86
  ...(input.lockedPrice !== undefined && {
86
87
  lockedPrice: input.lockedPrice,
87
88
  }),
89
+ ...(input.locked !== undefined && { locked: input.locked }),
88
90
  // SB-UNI-4 2026-05-13 — propagate bundle identifiers when present.
89
91
  ...(input.bundleCategorySlug !== undefined && {
90
92
  bundleCategorySlug: input.bundleCategorySlug,
@@ -122,6 +124,8 @@ export class CartRepository extends BaseRepository {
122
124
  const itemIndex = cart.items.findIndex((item) => item.itemId === itemId);
123
125
  if (itemIndex < 0)
124
126
  throw new NotFoundError(ERR_CART_ITEM_NOT_FOUND);
127
+ if (cart.items[itemIndex].locked)
128
+ throw new ValidationError(ERR_CART_ITEM_LOCKED);
125
129
  const items = [...cart.items];
126
130
  items[itemIndex] = {
127
131
  ...items[itemIndex],
@@ -151,9 +155,11 @@ export class CartRepository extends BaseRepository {
151
155
  const cart = await this.findByUserId(userId);
152
156
  if (!cart)
153
157
  throw new NotFoundError("Cart not found");
154
- const itemExists = cart.items.some((item) => item.itemId === itemId);
155
- if (!itemExists)
158
+ const target = cart.items.find((item) => item.itemId === itemId);
159
+ if (!target)
156
160
  throw new NotFoundError(ERR_CART_ITEM_NOT_FOUND);
161
+ if (target.locked)
162
+ throw new ValidationError(ERR_CART_ITEM_LOCKED);
157
163
  const items = cart.items.filter((item) => item.itemId !== itemId);
158
164
  const updatedCart = {
159
165
  ...cart,
@@ -176,9 +182,10 @@ export class CartRepository extends BaseRepository {
176
182
  async clearCart(userId) {
177
183
  try {
178
184
  const cart = await this.getOrCreate(userId);
185
+ const lockedItems = cart.items.filter((item) => item.locked);
179
186
  const clearedCart = {
180
187
  ...cart,
181
- items: [],
188
+ items: lockedItems,
182
189
  updatedAt: new Date(),
183
190
  };
184
191
  await this.db
@@ -27,6 +27,8 @@ export interface CartItemDocument {
27
27
  offerId?: string;
28
28
  /** Locked offer price — overrides normal product price at checkout */
29
29
  lockedPrice?: number;
30
+ /** When true the item cannot be removed or have its quantity changed. Set on won-auction and accepted-offer items that require mandatory payment. */
31
+ locked?: boolean;
30
32
  /**
31
33
  * SB-UNI-4 2026-05-13 — bundle identifier when this cart line represents a
32
34
  * bundle (categoryType:"bundle" row on the categories collection). When set,
@@ -114,6 +116,7 @@ export type AddToCartInput = {
114
116
  isOffer?: boolean;
115
117
  offerId?: string;
116
118
  lockedPrice?: number;
119
+ locked?: boolean;
117
120
  /** SB-UNI-4 2026-05-13 — bundle identifier when the line is a bundle. */
118
121
  bundleCategorySlug?: string;
119
122
  /** Snapshot of bundle members at add-to-cart time. */
@@ -81,5 +81,5 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
81
81
  counts.preOrders +
82
82
  counts.prizeDraws +
83
83
  counts.bundles;
84
- return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative overflow-hidden ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover", style: { backgroundImage: `url(${coverImage})` } }), _jsx("div", { className: "absolute inset-0 bg-black/55" })] })), !hasCover && (_jsx("div", { className: "absolute inset-0 opacity-10", style: { backgroundColor: brandColor } })), _jsxs("div", { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs("nav", { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Home" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.BRANDS ?? "/brands"), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Brands" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx("span", { className: hasCover ? "text-white font-medium" : "text-zinc-900 dark:text-zinc-100 font-medium", children: brand?.name ?? slug })] }), _jsxs("div", { className: "flex items-center gap-4 mb-3", children: [brand?.display?.icon && (_jsx("span", { className: `text-5xl leading-none ${!hasCover ? "" : ""}`, children: brand.display.icon })), _jsxs(_Fragment, { children: [_jsx(Heading, { level: 1, className: `text-3xl md:text-4xl font-bold ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: brand?.name ?? slug }), brand?.description && typeof brand.description === "string" && !brand.description.startsWith("{") && (_jsx(Text, { className: `text-base max-w-2xl mt-1 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: brand.description }))] })] }), _jsxs("div", { className: "flex flex-wrap gap-2 mt-3", children: [counts.products > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [counts.products.toLocaleString(), " ", counts.products === 1 ? "product" : "products"] })), counts.auctions > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [counts.auctions.toLocaleString(), " ", counts.auctions === 1 ? "auction" : "auctions"] })), counts.preOrders > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [counts.preOrders.toLocaleString(), " ", counts.preOrders === 1 ? "pre-order" : "pre-orders"] })), totalItems === 0 && (_jsx("span", { className: `text-sm ${hasCover ? "text-white/60" : "text-zinc-400"}`, children: "No items listed yet" }))] })] })] }), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: brandName ? (_jsx(BrandDetailTabs, { brandName: brandName, initialProductsData: productsResult ?? undefined, initialBundles: brandBundles, counts: counts })) : (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "Brand not found." })) }) })] }));
84
+ return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative overflow-hidden ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover", style: { backgroundImage: `url(${coverImage})` } }), _jsx("div", { className: "absolute inset-0 bg-black/55" })] })), !hasCover && (_jsx("div", { className: "absolute inset-0 opacity-10", style: { backgroundColor: brandColor } })), _jsxs("div", { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs("nav", { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 dark:text-zinc-400 hover:text-primary-600 transition-colors", children: "Home" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.BRANDS ?? "/brands"), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 dark:text-zinc-400 hover:text-primary-600 transition-colors", children: "Brands" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx("span", { className: hasCover ? "text-white font-medium" : "text-zinc-900 dark:text-zinc-100 font-medium", children: brand?.name ?? slug })] }), _jsxs("div", { className: "flex items-center gap-4 mb-3", children: [brand?.display?.icon && (_jsx("span", { className: `text-5xl leading-none ${!hasCover ? "" : ""}`, children: brand.display.icon })), _jsxs(_Fragment, { children: [_jsx(Heading, { level: 1, className: `text-3xl md:text-4xl font-bold ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: brand?.name ?? slug }), brand?.description && typeof brand.description === "string" && !brand.description.startsWith("{") && (_jsx(Text, { className: `text-base max-w-2xl mt-1 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: brand.description }))] })] }), _jsxs("div", { className: "flex flex-wrap gap-2 mt-3", children: [counts.products > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [counts.products.toLocaleString(), " ", counts.products === 1 ? "product" : "products"] })), counts.auctions > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [counts.auctions.toLocaleString(), " ", counts.auctions === 1 ? "auction" : "auctions"] })), counts.preOrders > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [counts.preOrders.toLocaleString(), " ", counts.preOrders === 1 ? "pre-order" : "pre-orders"] })), totalItems === 0 && (_jsx("span", { className: `text-sm ${hasCover ? "text-white/60" : "text-zinc-400"}`, children: "No items listed yet" }))] })] })] }), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: brandName ? (_jsx(BrandDetailTabs, { brandName: brandName, initialProductsData: productsResult ?? undefined, initialBundles: brandBundles, counts: counts })) : (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "Brand not found." })) }) })] }));
85
85
  }
@@ -30,7 +30,7 @@ function renderCategoryFilterDrawer(props) {
30
30
  const { filterOpen, setFilterOpen, activeFilterCount, clearFilters, applyFilters, pendingTable } = props;
31
31
  if (!filterOpen)
32
32
  return null;
33
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && _jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" }), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(CategoryFilters, { table: pendingTable, variant: "public" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
33
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && _jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" }), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(CategoryFilters, { table: pendingTable, variant: "public" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
34
34
  }
35
35
  function getNumericParam(table, key) {
36
36
  return table.get(key) ? Number(table.get(key)) : undefined;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import Link from "next/link";
3
- import { categoriesRepository, productRepository, } from "../../../repositories";
3
+ import { categoriesRepository, productRepository, storeRepository, } from "../../../repositories";
4
4
  import { ROUTES } from "../../../next";
5
5
  import { Container, Heading, Main, Section, Text } from "../../../ui";
6
6
  import { CategoryDetailTabs } from "./CategoryDetailTabs";
@@ -63,19 +63,55 @@ export async function CategoryDetailPageView({ slug }) {
63
63
  ? categoriesRepository.getChildren(category.id).catch(() => [])
64
64
  : Promise.resolve([]),
65
65
  ]);
66
+ // Stores tab — query stores whose storeCategory matches this category or any child
67
+ const storeCategorySlugs = [
68
+ slug,
69
+ ...childCategories.map((c) => c.slug).filter(Boolean),
70
+ ];
71
+ const storeResults = await Promise.all(storeCategorySlugs.map((catSlug) => storeRepository
72
+ .listStores({ filters: `storeCategory==${catSlug}`, page: 1, pageSize: 50 }, true)
73
+ .catch(() => null)));
74
+ const seen = new Set();
75
+ const categoryStores = storeResults
76
+ .flatMap((r) => r?.items ?? [])
77
+ .filter((s) => {
78
+ if (seen.has(s.id))
79
+ return false;
80
+ seen.add(s.id);
81
+ return true;
82
+ })
83
+ .map((s) => ({
84
+ id: s.id,
85
+ storeSlug: s.storeSlug ?? s.id,
86
+ ownerId: s.ownerId,
87
+ storeName: s.storeName,
88
+ storeDescription: s.storeDescription,
89
+ storeCategory: s.storeCategory,
90
+ storeLogoURL: s.storeLogoURL,
91
+ storeBannerURL: s.storeBannerURL,
92
+ status: s.status,
93
+ isPublic: s.isPublic ?? true,
94
+ totalProducts: s.stats?.totalProducts,
95
+ itemsSold: s.stats?.itemsSold,
96
+ totalReviews: s.stats?.totalReviews,
97
+ averageRating: s.stats?.averageRating,
98
+ createdAt: s.createdAt,
99
+ }));
66
100
  const productCount = productsResult?.total ?? category?.metrics?.productCount ?? 0;
67
101
  const auctionCount = auctionsCountResult?.total ?? category?.metrics?.auctionCount ?? 0;
68
102
  const preOrderCount = preOrdersCountResult?.total ?? 0;
69
103
  const prizeDrawCount = prizeDrawsCountResult?.total ?? 0;
70
104
  const bundleCount = bundlesResult?.length ?? 0;
71
- const totalCount = productCount + auctionCount + preOrderCount + prizeDrawCount + bundleCount;
105
+ const storeCount = categoryStores.length;
106
+ const totalCount = productCount + auctionCount + preOrderCount + prizeDrawCount + bundleCount + storeCount;
72
107
  const coverImage = category?.display?.coverImage;
73
108
  const hasCover = Boolean(coverImage);
74
- return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative overflow-hidden ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover", style: { backgroundImage: `url(${coverImage})` } }), _jsx("div", { className: "absolute inset-0 bg-black/55" })] })), _jsxs("div", { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs("nav", { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Home" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORIES), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Categories" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx("span", { className: hasCover ? "text-white font-medium" : "text-zinc-900 dark:text-zinc-100 font-medium", children: category?.name ?? slug })] }), _jsx(Heading, { level: 1, className: `text-3xl md:text-4xl font-bold mb-2 ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: category?.name ?? slug }), category?.description && typeof category.description === "string" && !category.description.startsWith("{") && (_jsx(Text, { className: `text-base max-w-2xl mb-4 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: category.description })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [productCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [productCount.toLocaleString(), " ", productCount === 1 ? "product" : "products"] })), auctionCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [auctionCount.toLocaleString(), " ", auctionCount === 1 ? "auction" : "auctions"] })), preOrderCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [preOrderCount.toLocaleString(), " ", preOrderCount === 1 ? "pre-order" : "pre-orders"] }))] })] })] }), childCategories.length > 0 && (_jsx(Section, { className: "border-b border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900", children: _jsx("div", { className: "max-w-7xl mx-auto px-4 py-3", children: _jsx("div", { className: "flex gap-2.5 overflow-x-auto pb-1", style: { scrollbarWidth: "none" }, children: childCategories.map((child) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(child.slug)), className: "flex-shrink-0 flex items-center gap-1.5 rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800 px-4 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:border-primary hover:text-primary transition-colors whitespace-nowrap", children: [child.display?.icon && (_jsx("span", { className: "text-base leading-none", children: child.display.icon })), child.name, (child.metrics?.productCount ?? 0) > 0 && (_jsx("span", { className: "text-xs text-zinc-400 dark:text-zinc-500", children: (child.metrics?.productCount ?? 0).toLocaleString() }))] }, child.id))) }) }) })), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: _jsx(CategoryDetailTabs, { categorySlug: slug, categoryId: category?.id, initialProductsData: productsResult ?? undefined, initialBundles: bundlesResult ?? [], counts: {
109
+ return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative overflow-hidden ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover", style: { backgroundImage: `url(${coverImage})` } }), _jsx("div", { className: "absolute inset-0 bg-black/55" })] })), _jsxs("div", { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs("nav", { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 dark:text-zinc-400 hover:text-primary-600 transition-colors", children: "Home" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORIES), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 dark:text-zinc-400 hover:text-primary-600 transition-colors", children: "Categories" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx("span", { className: hasCover ? "text-white font-medium" : "text-zinc-900 dark:text-zinc-100 font-medium", children: category?.name ?? slug })] }), _jsx(Heading, { level: 1, className: `text-3xl md:text-4xl font-bold mb-2 ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: category?.name ?? slug }), category?.description && typeof category.description === "string" && !category.description.startsWith("{") && (_jsx(Text, { className: `text-base max-w-2xl mb-4 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: category.description })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [productCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [productCount.toLocaleString(), " ", productCount === 1 ? "product" : "products"] })), auctionCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [auctionCount.toLocaleString(), " ", auctionCount === 1 ? "auction" : "auctions"] })), preOrderCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [preOrderCount.toLocaleString(), " ", preOrderCount === 1 ? "pre-order" : "pre-orders"] })), storeCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400"}`, children: [storeCount.toLocaleString(), " ", storeCount === 1 ? "store" : "stores"] }))] })] })] }), childCategories.length > 0 && (_jsx(Section, { className: "border-b border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900", children: _jsx("div", { className: "max-w-7xl mx-auto px-4 py-3", children: _jsx("div", { className: "flex gap-2.5 overflow-x-auto pb-1", style: { scrollbarWidth: "none" }, children: childCategories.map((child) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(child.slug)), className: "flex-shrink-0 flex items-center gap-1.5 rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800 px-4 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:border-primary hover:text-primary transition-colors whitespace-nowrap", children: [child.display?.icon && (_jsx("span", { className: "text-base leading-none", children: child.display.icon })), child.name, (child.metrics?.productCount ?? 0) > 0 && (_jsx("span", { className: "text-xs text-zinc-400 dark:text-zinc-400", children: (child.metrics?.productCount ?? 0).toLocaleString() }))] }, child.id))) }) }) })), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: _jsx(CategoryDetailTabs, { categorySlug: slug, categoryId: category?.id, initialProductsData: productsResult ?? undefined, initialBundles: bundlesResult ?? [], initialStores: categoryStores, counts: {
75
110
  products: productCount,
76
111
  auctions: auctionCount,
77
112
  preOrders: preOrderCount,
78
113
  prizeDraws: prizeDrawCount,
79
114
  bundles: bundleCount,
115
+ stores: storeCount,
80
116
  } }) }) })] }));
81
117
  }
@@ -1,19 +1,22 @@
1
1
  import type { CategoryDocument } from "../schemas";
2
+ import type { StoreListItem } from "../../stores/types";
2
3
  export interface CategoryDetailTabsProps {
3
4
  categorySlug: string;
4
5
  categoryId?: string;
5
6
  initialProductsData?: any;
6
7
  initialBundles?: CategoryDocument[];
8
+ initialStores?: StoreListItem[];
7
9
  counts?: {
8
10
  products?: number;
9
11
  auctions?: number;
10
12
  preOrders?: number;
11
13
  prizeDraws?: number;
12
14
  bundles?: number;
15
+ stores?: number;
13
16
  };
14
17
  /** Enabled listing types (e.g. ["standard","auction","pre-order"]). When omitted, all tabs shown. */
15
18
  enabledListingTypes?: string[];
16
19
  /** Enabled category types (e.g. ["category","brand","bundle"]). When omitted, all tabs shown. */
17
20
  enabledCategoryTypes?: string[];
18
21
  }
19
- export declare function CategoryDetailTabs({ categorySlug, categoryId, initialProductsData, initialBundles, counts, enabledListingTypes, enabledCategoryTypes, }: CategoryDetailTabsProps): import("react/jsx-runtime").JSX.Element;
22
+ export declare function CategoryDetailTabs({ categorySlug, categoryId, initialProductsData, initialBundles, initialStores, counts, enabledListingTypes, enabledCategoryTypes, }: CategoryDetailTabsProps): import("react/jsx-runtime").JSX.Element;
@@ -6,6 +6,7 @@ import { AuctionsIndexListing } from "../../products/components/AuctionsIndexLis
6
6
  import { PreOrdersIndexListing } from "../../pre-orders/components/PreOrdersIndexListing";
7
7
  import { PrizeDrawsIndexListing } from "../../products/components/PrizeDrawsIndexListing";
8
8
  import { CategoryBundlesListing } from "./CategoryBundlesListing";
9
+ import { CategoryStoresListing } from "./CategoryStoresListing";
9
10
  import { CATEGORY_PAGE_TABS } from "../../products/constants/listing-tabs";
10
11
  function tabLabel(label, count) {
11
12
  if (!count)
@@ -19,8 +20,9 @@ const TAB_TYPE_MAP = {
19
20
  "pre-orders": { kind: "listing", type: "pre-order" },
20
21
  "prize-draws": { kind: "listing", type: "prize-draw" },
21
22
  bundles: { kind: "category", type: "bundle" },
23
+ stores: { kind: "entity", type: "stores" },
22
24
  };
23
- export function CategoryDetailTabs({ categorySlug, categoryId, initialProductsData, initialBundles = [], counts, enabledListingTypes, enabledCategoryTypes, }) {
25
+ export function CategoryDetailTabs({ categorySlug, categoryId, initialProductsData, initialBundles = [], initialStores = [], counts, enabledListingTypes, enabledCategoryTypes, }) {
24
26
  const visibleTabs = CATEGORY_PAGE_TABS.filter((t) => {
25
27
  const mapping = TAB_TYPE_MAP[t.id];
26
28
  if (!mapping)
@@ -31,6 +33,8 @@ export function CategoryDetailTabs({ categorySlug, categoryId, initialProductsDa
31
33
  if (mapping.kind === "category" && enabledCategoryTypes) {
32
34
  return enabledCategoryTypes.includes(mapping.type);
33
35
  }
36
+ if (mapping.kind === "entity")
37
+ return true;
34
38
  return true;
35
39
  });
36
40
  const firstTabId = (visibleTabs[0]?.id ?? "products");
@@ -42,10 +46,11 @@ export function CategoryDetailTabs({ categorySlug, categoryId, initialProductsDa
42
46
  case "pre-orders": return counts?.preOrders;
43
47
  case "prize-draws": return counts?.prizeDraws;
44
48
  case "bundles": return counts?.bundles;
49
+ case "stores": return counts?.stores;
45
50
  default: return undefined;
46
51
  }
47
52
  };
48
53
  return (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex border-b border-zinc-200 dark:border-slate-700 mb-6 overflow-x-auto", children: visibleTabs.map((t) => (_jsx("button", { type: "button", onClick: () => setActiveTab(t.id), className: `px-5 py-2.5 text-sm font-medium whitespace-nowrap transition-colors -mb-px border-b-2 ${activeTab === t.id
49
54
  ? "border-primary text-primary"
50
- : "border-transparent text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"}`, children: tabLabel(t.label, countFor(t.id)) }, t.id))) }), activeTab === "products" && (_jsx(CategoryProductsListing, { categorySlug: categorySlug, categoryId: categoryId, initialData: initialProductsData })), activeTab === "auctions" && (_jsx(AuctionsIndexListing, { categorySlug: categorySlug })), activeTab === "pre-orders" && (_jsx(PreOrdersIndexListing, { categorySlug: categorySlug })), activeTab === "prize-draws" && (_jsx(PrizeDrawsIndexListing, { categorySlug: categorySlug })), activeTab === "bundles" && (_jsx(CategoryBundlesListing, { initialBundles: initialBundles }))] }));
55
+ : "border-transparent text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"}`, children: tabLabel(t.label, countFor(t.id)) }, t.id))) }), activeTab === "products" && (_jsx(CategoryProductsListing, { categorySlug: categorySlug, categoryId: categoryId, initialData: initialProductsData })), activeTab === "auctions" && (_jsx(AuctionsIndexListing, { categorySlug: categorySlug })), activeTab === "pre-orders" && (_jsx(PreOrdersIndexListing, { categorySlug: categorySlug })), activeTab === "prize-draws" && (_jsx(PrizeDrawsIndexListing, { categorySlug: categorySlug })), activeTab === "bundles" && (_jsx(CategoryBundlesListing, { initialBundles: initialBundles })), activeTab === "stores" && (_jsx(CategoryStoresListing, { stores: initialStores }))] }));
51
56
  }
@@ -5,7 +5,7 @@ import { Div, Span, Text } from "../../../ui";
5
5
  import { THEME_CONSTANTS } from "../../../tokens";
6
6
  export function CategoryCard({ category, href, onClick, className = "", }) {
7
7
  const productCount = category.metrics?.productCount ?? category.productCount ?? 0;
8
- const inner = (_jsxs(Div, { className: "flex h-full flex-col", children: [_jsxs(Div, { className: "relative aspect-[4/3] w-full overflow-hidden bg-gradient-to-br from-neutral-100 to-neutral-200 dark:from-zinc-800 dark:to-zinc-700 flex-shrink-0", children: [category.display?.coverImage ? (_jsx(Div, { role: "img", "aria-label": category.name, className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${category.display.coverImage})` } })) : category.display?.color ? (_jsx(Div, { className: "h-full w-full opacity-80", style: { backgroundColor: category.display.color } })) : null, category.display?.icon && (_jsx(Div, { className: "absolute inset-0 flex items-center justify-center text-4xl", children: category.display.icon })), category.isFeatured && (_jsx(Span, { className: "absolute left-2 top-2 rounded-full bg-amber-400 p-1 text-xs leading-none", children: "\u2605" }))] }), _jsxs(Div, { className: "flex flex-1 flex-col p-3.5", children: [_jsx(Text, { className: `font-semibold text-sm leading-snug ${THEME_CONSTANTS.utilities.textClamp2} text-neutral-900 dark:text-white`, children: category.name }), category.description && (_jsx(Text, { className: `mt-1 text-xs ${THEME_CONSTANTS.utilities.textClamp2} text-neutral-500 dark:text-zinc-400 flex-1`, children: category.description })), _jsxs(Div, { className: "mt-2 flex items-center justify-between gap-2", children: [_jsxs(Text, { className: "text-xs text-neutral-400 dark:text-zinc-500", children: [productCount.toLocaleString(), " ", productCount === 1 ? "item" : "items"] }), _jsxs(Span, { className: "inline-flex items-center gap-1 rounded-md border border-zinc-200 dark:border-zinc-700 px-2.5 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-300 group-hover:bg-primary group-hover:border-primary group-hover:text-white transition-colors", children: ["Browse ", _jsx(ArrowRight, { className: "h-3 w-3" })] })] })] })] }));
8
+ const inner = (_jsxs(Div, { className: "flex h-full flex-col", children: [_jsxs(Div, { className: "relative aspect-[4/3] w-full overflow-hidden bg-gradient-to-br from-neutral-100 to-neutral-200 dark:from-zinc-800 dark:to-zinc-700 flex-shrink-0", children: [category.display?.coverImage ? (_jsx(Div, { role: "img", "aria-label": category.name, className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${category.display.coverImage})` } })) : category.display?.color ? (_jsx(Div, { className: "h-full w-full opacity-80", style: { backgroundColor: category.display.color } })) : null, category.display?.icon && (_jsx(Div, { className: "absolute inset-0 flex items-center justify-center text-4xl", children: category.display.icon })), category.isFeatured && (_jsx(Span, { className: "absolute left-2 top-2 rounded-full bg-amber-400 p-1 text-xs leading-none", children: "\u2605" }))] }), _jsxs(Div, { className: "flex flex-1 flex-col p-3.5", children: [_jsx(Text, { className: `font-semibold text-sm leading-snug ${THEME_CONSTANTS.utilities.textClamp2} text-neutral-900 dark:text-white`, children: category.name }), category.description && (_jsx(Text, { className: `mt-1 text-xs ${THEME_CONSTANTS.utilities.textClamp2} text-neutral-500 dark:text-zinc-400 flex-1`, children: category.description })), _jsxs(Div, { className: "mt-2 flex items-center justify-between gap-2", children: [_jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: [productCount.toLocaleString(), " ", productCount === 1 ? "item" : "items"] }), _jsxs(Span, { className: "inline-flex items-center gap-1 rounded-md border border-zinc-200 dark:border-zinc-700 px-2.5 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-300 group-hover:bg-primary group-hover:border-primary group-hover:text-white transition-colors", children: ["Browse ", _jsx(ArrowRight, { className: "h-3 w-3" })] })] })] })] }));
9
9
  const cardClass = `group relative flex flex-col overflow-hidden rounded-xl border border-neutral-200 bg-white dark:border-zinc-700 dark:bg-zinc-900 shadow-sm transition hover:shadow-md h-full ${className}`;
10
10
  if (href) {
11
11
  return (_jsx(Link, { href: href, className: cardClass, children: inner }));
@@ -16,6 +16,6 @@ export function CategoryProductsView({ labels = {}, table, filterVariant = "publ
16
16
  }
17
17
  table?.set("sorts", next);
18
18
  };
19
- return (_jsx(Div, { className: `min-h-screen ${className}`, children: _jsxs(Div, { className: "py-6 px-4 max-w-screen-xl mx-auto space-y-4", children: [category && renderBreadcrumbs?.(category), category && (_jsxs(Div, { children: [_jsx(Heading, { level: 1, className: "text-2xl font-bold", children: category.name }), category.description && (_jsx(Text, { className: "text-neutral-500 mt-1", children: category.description }))] })), childCategories.length > 0 && renderChildCategories?.(childCategories), _jsx(SlottedListingView, { portal: "public", inlineToolbar: true, className: "space-y-4", renderSearch: () => renderSearch?.(search, setSearch) ?? (_jsx(Input, { value: search, onChange: (event) => setSearch(event.target.value), placeholder: "Search in category", className: "max-w-sm" })), renderSort: () => renderSort ? (renderSort(resolvedSort, handleSortChange)) : (_jsx(CategorySortSelect, { value: resolvedSort, onChange: handleSortChange, variant: filterVariant })), renderFilters: () => renderFilters?.() ??
19
+ return (_jsx(Div, { className: `min-h-screen ${className}`, children: _jsxs(Div, { className: "py-6 px-4 max-w-screen-xl mx-auto space-y-4", children: [category && renderBreadcrumbs?.(category), category && (_jsxs(Div, { children: [_jsx(Heading, { level: 1, className: "text-2xl font-bold", children: category.name }), category.description && (_jsx(Text, { className: "text-zinc-500 dark:text-zinc-400 mt-1", children: category.description }))] })), childCategories.length > 0 && renderChildCategories?.(childCategories), _jsx(SlottedListingView, { portal: "public", inlineToolbar: true, className: "space-y-4", renderSearch: () => renderSearch?.(search, setSearch) ?? (_jsx(Input, { value: search, onChange: (event) => setSearch(event.target.value), placeholder: "Search in category", className: "max-w-sm" })), renderSort: () => renderSort ? (renderSort(resolvedSort, handleSortChange)) : (_jsx(CategorySortSelect, { value: resolvedSort, onChange: handleSortChange, variant: filterVariant })), renderFilters: () => renderFilters?.() ??
20
20
  (table ? _jsx(CategoryFilters, { table: table, variant: filterVariant }) : null), renderActiveFilters: renderActiveFilters, renderBulkActions: () => renderViewToggle?.(viewMode, setViewMode) ?? null, renderTable: () => renderProducts(isLoading), renderPagination: () => renderPagination?.(total) ?? null, total: total, isLoading: isLoading })] }) }));
21
21
  }
@@ -0,0 +1,5 @@
1
+ import type { StoreListItem } from "../../stores/types";
2
+ export interface CategoryStoresListingProps {
3
+ stores: StoreListItem[];
4
+ }
5
+ export declare function CategoryStoresListing({ stores }: CategoryStoresListingProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { InteractiveStoreCard } from "../../stores/components/InteractiveStoreCard";
4
+ import { ROUTES } from "../../../next";
5
+ import { Text } from "../../../ui";
6
+ export function CategoryStoresListing({ stores }) {
7
+ if (stores.length === 0) {
8
+ return (_jsx("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: _jsx(Text, { className: "text-zinc-500 dark:text-zinc-400 text-sm", children: "No stores found in this category." }) }));
9
+ }
10
+ return (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-5", children: stores.map((store) => (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(store.storeSlug)) }, store.id))) }));
11
+ }
@@ -18,13 +18,13 @@ export function getCategoryTableColumns(onEdit, onDelete, labels = {}) {
18
18
  {
19
19
  key: "image",
20
20
  header: "",
21
- render: (cat) => (_jsx("div", { className: "relative w-9 h-9 rounded overflow-hidden flex-shrink-0", "data-section": "categorytablecolumns-div-270", children: cat.display?.coverImage ? (_jsx(MediaImage, { src: cat.display.coverImage, alt: "", size: "thumbnail", className: "w-full h-full object-cover" })) : (_jsx("div", { className: "w-full h-full flex items-center justify-center text-base", style: { backgroundColor: cat.display?.color ?? "#94a3b8" }, "data-section": "categorytablecolumns-div-271", children: cat.display?.icon ?? "🗂️" })) })),
21
+ render: (cat) => (_jsx("div", { className: "relative w-9 h-9 rounded overflow-hidden flex-shrink-0", "data-section": "categorytablecolumns-div-270", children: cat.display?.coverImage ? (_jsx(MediaImage, { src: cat.display.coverImage, alt: "", size: "thumbnail", className: "w-full h-full object-cover" })) : (_jsx("div", { className: "w-full h-full flex items-center justify-center text-base", style: { backgroundColor: cat.display?.color ?? "#94a3b8", color: "#fff" }, "data-section": "categorytablecolumns-div-271", children: cat.display?.icon ?? "🗂️" })) })),
22
22
  },
23
23
  {
24
24
  key: "name",
25
25
  header: L.colName,
26
26
  sortable: true,
27
- render: (cat) => (_jsxs("div", { style: { paddingLeft: `${cat.tier * 20}px` }, "data-section": "categorytablecolumns-div-272", children: [cat.name, cat.tier > 0 && (_jsxs(Span, { className: "text-zinc-400 text-xs ml-2", children: ["(Tier ", cat.tier, ")"] }))] })),
27
+ render: (cat) => (_jsxs("div", { style: { paddingLeft: `${cat.tier * 20}px` }, "data-section": "categorytablecolumns-div-272", children: [cat.name, cat.tier > 0 && (_jsxs(Span, { className: "text-zinc-400 dark:text-zinc-400 text-xs ml-2", children: ["(Tier ", cat.tier, ")"] }))] })),
28
28
  },
29
29
  {
30
30
  key: "slug",
@@ -19,7 +19,7 @@ function CategoryTreeNode({ item, children = [], activeId, onSelect, depth = 0,
19
19
  setExpanded((p) => !p);
20
20
  onSelect?.(item);
21
21
  }, className: `flex cursor-pointer items-center gap-1 rounded-md px-2 py-1.5 text-sm transition select-none
22
- ${isActive ? "bg-primary-50 font-semibold text-primary-700 dark:bg-primary-900/30 dark:text-primary-400" : "text-neutral-700 hover:bg-neutral-50 dark:text-zinc-300 dark:hover:bg-zinc-800"}`, style: { paddingLeft: `${8 + indent}px` }, children: [hasChildren && (_jsx(Span, { "aria-hidden": "true", className: `text-xs text-neutral-400 dark:text-zinc-500 transition-transform duration-150 ${expanded ? "rotate-90" : ""}`, children: "\u25B6" })), !hasChildren && _jsx(Span, { className: "w-3" }), item.display?.icon && (_jsx(Span, { className: "flex-shrink-0 text-base", "aria-hidden": "true", children: item.display.icon })), _jsx(Text, { className: "flex-1 truncate", children: item.name }), item.metrics && (_jsx(Span, { className: "ml-auto flex-shrink-0 text-xs text-neutral-400 dark:text-zinc-500 tabular-nums", children: item.metrics.productCount }))] }), hasChildren && expanded && (_jsx(Div, { role: "group", children: children.map((child) => (_jsx(CategoryTreeNode, { item: child, activeId: activeId, onSelect: onSelect, depth: depth + 1 }, child.id))) }))] }));
22
+ ${isActive ? "bg-primary-50 font-semibold text-primary-700 dark:bg-primary-900/30 dark:text-primary-400" : "text-neutral-700 hover:bg-neutral-50 dark:text-zinc-300 dark:hover:bg-zinc-800"}`, style: { paddingLeft: `${8 + indent}px` }, children: [hasChildren && (_jsx(Span, { "aria-hidden": "true", className: `text-xs text-zinc-400 dark:text-zinc-400 transition-transform duration-150 ${expanded ? "rotate-90" : ""}`, children: "\u25B6" })), !hasChildren && _jsx(Span, { className: "w-3" }), item.display?.icon && (_jsx(Span, { className: "flex-shrink-0 text-base", "aria-hidden": "true", children: item.display.icon })), _jsx(Text, { className: "flex-1 truncate", children: item.name }), item.metrics && (_jsx(Span, { className: "ml-auto flex-shrink-0 text-xs text-zinc-400 dark:text-zinc-400 tabular-nums", children: item.metrics.productCount }))] }), hasChildren && expanded && (_jsx(Div, { role: "group", children: children.map((child) => (_jsx(CategoryTreeNode, { item: child, activeId: activeId, onSelect: onSelect, depth: depth + 1 }, child.id))) }))] }));
23
23
  }
24
24
  /**
25
25
  * CategoryTree — hierarchical expandable navigation for categories.
@@ -34,3 +34,5 @@ export { BundleItemsPicker, defaultBundleItemsFetch, } from "./BundleItemsPicker
34
34
  export type { BundleItemsPickerProps, BundleItemSearchResult, } from "./BundleItemsPicker";
35
35
  export { CategoryBundlesListing } from "./CategoryBundlesListing";
36
36
  export type { CategoryBundlesListingProps } from "./CategoryBundlesListing";
37
+ export { CategoryStoresListing } from "./CategoryStoresListing";
38
+ export type { CategoryStoresListingProps } from "./CategoryStoresListing";
@@ -20,3 +20,4 @@ export { BundleCollage } from "./BundleCollage";
20
20
  // S-SBUNI-4 2026-05-13 — multi-select product picker for bundle editor.
21
21
  export { BundleItemsPicker, defaultBundleItemsFetch, } from "./BundleItemsPicker";
22
22
  export { CategoryBundlesListing } from "./CategoryBundlesListing";
23
+ export { CategoryStoresListing } from "./CategoryStoresListing";
@@ -11,6 +11,6 @@ export function CheckoutStepper({ steps, current }) {
11
11
  ? "bg-green-500 text-white"
12
12
  : isActive
13
13
  ? "bg-neutral-900 text-white"
14
- : "bg-neutral-200 dark:bg-slate-700 text-neutral-500 dark:text-zinc-400"}`, children: isDone ? "✓" : idx + 1 }), _jsx(Span, { className: `text-sm font-medium ${isActive ? "text-neutral-900 dark:text-zinc-100" : isDone ? "text-neutral-500 dark:text-zinc-400" : "text-neutral-400 dark:text-zinc-500"}`, children: step.label })] }), idx < steps.length - 1 && (_jsx(Span, { className: "h-px flex-1 bg-neutral-200 dark:bg-slate-700" }))] }, step.id));
14
+ : "bg-neutral-200 dark:bg-slate-700 text-neutral-500 dark:text-zinc-400"}`, children: isDone ? "✓" : idx + 1 }), _jsx(Span, { className: `text-sm font-medium ${isActive ? "text-neutral-900 dark:text-zinc-100" : isDone ? "text-neutral-500 dark:text-zinc-400" : "text-zinc-400 dark:text-zinc-400"}`, children: step.label })] }), idx < steps.length - 1 && (_jsx(Span, { className: "h-px flex-1 bg-neutral-200 dark:bg-slate-700" }))] }, step.id));
15
15
  }) }));
16
16
  }
@@ -18,6 +18,7 @@ import { InteractiveProductCard } from "../../products/components/InteractivePro
18
18
  import { ClassifiedFilters } from "./ClassifiedFilters";
19
19
  import { ACTION_ID } from "../../products/constants/action-defs";
20
20
  import { useToast } from "../../../ui";
21
+ import { useBottomActions } from "../../layout";
21
22
  const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
22
23
  const SORT_OPTIONS = [
23
24
  { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
@@ -103,6 +104,7 @@ export function ClassifiedIndexListing({ initialData }) {
103
104
  wishlistActions.addToWishlist(id);
104
105
  }
105
106
  }
107
+ useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] } } : {});
106
108
  return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search classified listings\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: (e) => { if (e.key === "Enter")
107
109
  commitSearch(); }, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: (v) => { if (v !== "table")
108
110
  setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" })] })] }, i))) })) : products.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No classified listings found." })) : (_jsx("div", { className: view === "list" ? "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800" : gridClass, children: products.map((product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.CLASSIFIED_DETAIL(product.id)), variant: view === "list" ? "list" : "grid", isWishlisted: wishlistActions.isWishlisted(product.id), onToggleWishlist: handleToggleWishlist, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ClassifiedFilters, { table: pendingTable, categoryOptions: categoryOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
@@ -39,5 +39,5 @@ export async function ClassifiedListView({ searchParams = {} }) {
39
39
  const result = await productRepository
40
40
  .list({ filters, sorts: sort, page, pageSize })
41
41
  .catch(() => null);
42
- return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8 text-3xl font-semibold text-zinc-900", children: "Classifieds" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(ClassifiedIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
42
+ return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8 text-3xl font-semibold text-zinc-900 dark:text-zinc-50", children: "Classifieds" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(ClassifiedIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
43
43
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Div, Heading, Span, Stack, Text } from "../../../ui";
3
3
  export function ContactInfoSidebar({ infoItems, labels = {}, renderActions, }) {
4
- return (_jsxs(Stack, { as: "aside", gap: "lg", children: [_jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-4", children: labels.title ?? "Contact Information" }), _jsx(Stack, { gap: "md", children: infoItems.map((item) => (_jsxs(Div, { className: "flex items-start gap-3", children: [_jsx(Span, { className: "text-xl flex-shrink-0 mt-0.5", children: item.icon }), _jsxs(Div, { children: [_jsx(Text, { className: "text-xs font-medium text-neutral-500 dark:text-neutral-400 uppercase tracking-wide mb-0.5", children: item.label }), item.href ? (_jsx("a", { href: item.href, className: "text-sm text-primary-800 dark:text-primary-300 hover:underline", children: item.value })) : (_jsx(Text, { className: "text-sm", children: item.value }))] })] }, item.label))) })] }), (labels.businessHoursLabel || labels.responseTimeLabel) && (_jsxs(Stack, { gap: "sm", className: "rounded-xl border border-neutral-200 dark:border-slate-700 bg-neutral-50 dark:bg-slate-800/60 p-4", children: [labels.businessHoursLabel && labels.businessHoursValue && (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs font-semibold text-neutral-500 uppercase tracking-wide", children: labels.businessHoursLabel }), _jsx(Text, { className: "text-sm mt-0.5", children: labels.businessHoursValue })] })), labels.responseTimeLabel && labels.responseTimeValue && (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs font-semibold text-neutral-500 uppercase tracking-wide", children: labels.responseTimeLabel }), _jsx(Text, { className: "text-sm mt-0.5", children: labels.responseTimeValue })] }))] })), renderActions?.()] }));
4
+ return (_jsxs(Stack, { as: "aside", gap: "lg", children: [_jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-4", children: labels.title ?? "Contact Information" }), _jsx(Stack, { gap: "md", children: infoItems.map((item) => (_jsxs(Div, { className: "flex items-start gap-3", children: [_jsx(Span, { className: "text-xl flex-shrink-0 mt-0.5", children: item.icon }), _jsxs(Div, { children: [_jsx(Text, { className: "text-xs font-medium text-neutral-500 dark:text-neutral-400 uppercase tracking-wide mb-0.5", children: item.label }), item.href ? (_jsx("a", { href: item.href, className: "text-sm text-primary-800 dark:text-primary-300 hover:underline", children: item.value })) : (_jsx(Text, { className: "text-sm", children: item.value }))] })] }, item.label))) })] }), (labels.businessHoursLabel || labels.responseTimeLabel) && (_jsxs(Stack, { gap: "sm", className: "rounded-xl border border-neutral-200 dark:border-slate-700 bg-neutral-50 dark:bg-slate-800/60 p-4", children: [labels.businessHoursLabel && labels.businessHoursValue && (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: labels.businessHoursLabel }), _jsx(Text, { className: "text-sm mt-0.5", children: labels.businessHoursValue })] })), labels.responseTimeLabel && labels.responseTimeValue && (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: labels.responseTimeLabel }), _jsx(Text, { className: "text-sm mt-0.5", children: labels.responseTimeValue })] }))] })), renderActions?.()] }));
5
5
  }
@@ -17,6 +17,7 @@ import { ROUTES } from "../../../next";
17
17
  import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
18
18
  import { DigitalCodeFilters } from "./DigitalCodeFilters";
19
19
  import { ACTION_ID } from "../../products/constants/action-defs";
20
+ import { useBottomActions } from "../../layout";
20
21
  const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
21
22
  const SORT_OPTIONS = [
22
23
  { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
@@ -100,6 +101,7 @@ export function DigitalCodesIndexListing({ initialData }) {
100
101
  wishlistActions.addToWishlist(id);
101
102
  }
102
103
  }
104
+ useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] } } : {});
103
105
  return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search digital codes\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: (e) => { if (e.key === "Enter")
104
106
  commitSearch(); }, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: (v) => { if (v !== "table")
105
107
  setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" })] })] }, i))) })) : products.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No digital code listings found." })) : (_jsx("div", { className: view === "list" ? "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800" : gridClass, children: products.map((product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.DIGITAL_CODE_DETAIL(product.id)), variant: view === "list" ? "list" : "grid", isWishlisted: wishlistActions.isWishlisted(product.id), onToggleWishlist: handleToggleWishlist, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(DigitalCodeFilters, { table: pendingTable, categoryOptions: categoryOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
@@ -33,5 +33,5 @@ export async function DigitalCodesListView({ searchParams = {} }) {
33
33
  const result = await productRepository
34
34
  .list({ filters, sorts: sort, page, pageSize })
35
35
  .catch(() => null);
36
- return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8 text-3xl font-semibold text-zinc-900", children: "Digital Codes" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(DigitalCodesIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
36
+ return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8 text-3xl font-semibold text-zinc-900 dark:text-zinc-50", children: "Digital Codes" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(DigitalCodesIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
37
37
  }
@@ -12,6 +12,7 @@ import { ADMIN_EVENT_STATUS_TABS } from "../../admin/constants/filter-tabs";
12
12
  import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../../admin/hooks/useAdminListingData";
13
13
  import { DataTable } from "../../admin/components/DataTable";
14
14
  import { AdminEventEditorView } from "./AdminEventEditorView";
15
+ import { useBottomActions } from "../../layout";
15
16
  const PAGE_SIZE = 25;
16
17
  const FILTER_KEYS = ["status", "type"];
17
18
  const DEFAULT_SORT = "-startsAt";
@@ -102,7 +103,10 @@ export function AdminEventsView({ children, getRowHref, ...props }) {
102
103
  if (hasChildren) {
103
104
  return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
104
105
  }
106
+ useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
107
+ { id: "delete", label: "Delete Selected", variant: "secondary", onClick: () => { selection.clearSelection(); } },
108
+ ] } } : {});
105
109
  return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events by title or type", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add Event"] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
106
110
  { id: "delete", label: "Delete Selected", variant: "secondary", onClick: () => { selection.clearSelection(); } },
107
- ] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No events found", onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No events found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Type", tabs: TYPE_OPTIONS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) })] }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Event" : "Edit Event", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminEventEditorView, { eventId: editId ?? undefined, onSaved: closePanel, embedded: true })) })] }));
111
+ ] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No events found", onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No events found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Type", tabs: TYPE_OPTIONS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) })] }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Event" : "Edit Event", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminEventEditorView, { eventId: editId ?? undefined, onSaved: closePanel, embedded: true })) })] }));
108
112
  }
@@ -24,6 +24,6 @@ export function EventCard({ event, labels = {}, onParticipate, className = "", s
24
24
  const msLeft = endsAt.getTime() - now.getTime();
25
25
  const daysLeft = Math.max(0, Math.ceil(msLeft / (1000 * 60 * 60 * 24)));
26
26
  const detailHref = String(ROUTES.PUBLIC.EVENT_DETAIL(event.slug ?? event.id));
27
- return (_jsxs(Article, { className: `group relative flex h-full ${LAYOUT.cardHeight.event} flex-col overflow-hidden rounded-xl border bg-white shadow-sm transition-shadow hover:shadow-md dark:bg-slate-900 ${isSelected ? "border-primary outline outline-2 outline-primary" : "border-zinc-200 dark:border-slate-700"} ${className}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(event.id, !isSelected); }, label: isSelected ? "Deselect event" : "Select event", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsx(Link, { href: detailHref, className: "block flex-shrink-0", children: event.coverImageUrl ? (_jsx(Div, { className: "aspect-video overflow-hidden", children: _jsx(Div, { role: "img", "aria-label": safeTitle, className: "h-full w-full bg-cover bg-center transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${event.coverImageUrl})` } }) })) : (_jsx(Div, { className: "aspect-video bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700 flex items-center justify-center", children: _jsx("span", { className: "text-5xl opacity-40", "aria-hidden": "true", children: TYPE_ICONS[event.type] }) })) }), _jsxs(Div, { className: "flex flex-1 flex-col p-4", children: [_jsxs(Div, { className: "flex items-start justify-between gap-2 mb-2", children: [_jsx(Span, { className: "text-lg", "aria-hidden": "true", children: TYPE_ICONS[event.type] }), _jsx(EventStatusBadge, { status: event.status })] }), _jsx(Link, { href: detailHref, className: "block", children: _jsx(Heading, { level: 3, className: "font-semibold text-gray-900 dark:text-zinc-100 text-base leading-snug mb-1 group-hover:text-primary transition-colors", children: safeTitle }) }), _jsx(RichText, { html: normalizeRichTextHtml(event.description ?? ""), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `mb-3 ${THEME_CONSTANTS.utilities.textClamp3} text-sm text-zinc-600 dark:text-zinc-400` }), _jsxs(Div, { className: "mb-3 mt-auto flex items-center justify-between text-xs text-zinc-500 dark:text-zinc-500", children: [event.status === EVENT_FIELDS.STATUS_VALUES.ACTIVE &&
27
+ return (_jsxs(Article, { className: `group relative flex h-full ${LAYOUT.cardHeight.event} flex-col overflow-hidden rounded-xl border bg-white shadow-sm transition-shadow hover:shadow-md dark:bg-slate-900 ${isSelected ? "border-primary outline outline-2 outline-primary" : "border-zinc-200 dark:border-slate-700"} ${className}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(event.id, !isSelected); }, label: isSelected ? "Deselect event" : "Select event", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsx(Link, { href: detailHref, className: "block flex-shrink-0", children: event.coverImageUrl ? (_jsx(Div, { className: "aspect-video overflow-hidden", children: _jsx(Div, { role: "img", "aria-label": safeTitle, className: "h-full w-full bg-cover bg-center transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${event.coverImageUrl})` } }) })) : (_jsx(Div, { className: "aspect-video bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700 flex items-center justify-center", children: _jsx("span", { className: "text-5xl opacity-40", "aria-hidden": "true", children: TYPE_ICONS[event.type] }) })) }), _jsxs(Div, { className: "flex flex-1 flex-col p-4", children: [_jsxs(Div, { className: "flex items-start justify-between gap-2 mb-2", children: [_jsx(Span, { className: "text-lg", "aria-hidden": "true", children: TYPE_ICONS[event.type] }), _jsx(EventStatusBadge, { status: event.status })] }), _jsx(Link, { href: detailHref, className: "block", children: _jsx(Heading, { level: 3, className: "font-semibold text-gray-900 dark:text-zinc-100 text-base leading-snug mb-1 group-hover:text-primary transition-colors", children: safeTitle }) }), _jsx(RichText, { html: normalizeRichTextHtml(event.description ?? ""), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `mb-3 ${THEME_CONSTANTS.utilities.textClamp3} text-sm text-zinc-600 dark:text-zinc-400` }), _jsxs(Div, { className: "mb-3 mt-auto flex items-center justify-between text-xs text-zinc-500 dark:text-zinc-400", children: [event.status === EVENT_FIELDS.STATUS_VALUES.ACTIVE &&
28
28
  daysLeft > 0 && _jsxs(Span, { children: ["\u23F1 ", daysLeft, "d remaining"] }), _jsxs(Span, { children: ["\uD83D\uDC65 ", event.stats.totalEntries, " ", labels.entries ?? "entries"] })] }), event.status === EVENT_FIELDS.STATUS_VALUES.ACTIVE && onParticipate ? (_jsx(Button, { type: "button", onClick: () => onParticipate(event), className: "w-full rounded-lg bg-primary py-2 text-sm font-medium text-white transition-colors hover:bg-primary-600", children: labels.participate ?? "Participate" })) : (_jsxs(TextLink, { href: detailHref, className: "inline-flex w-full items-center justify-center gap-1.5 rounded-lg border border-zinc-300 px-3 py-2 text-sm font-medium text-zinc-700 transition-colors hover:bg-zinc-100 dark:border-slate-600 dark:text-zinc-200 dark:hover:bg-slate-800", children: [labels.viewDetails ?? "View details", " \u2192"] }))] })] }));
29
29
  }
@@ -56,7 +56,7 @@ export function EventPollWidget({ eventId, pollConfig, eventStatus, totalEntries
56
56
  }
57
57
  };
58
58
  if (isEnded) {
59
- return (_jsxs("div", { className: `rounded-xl border border-zinc-200 dark:border-zinc-700 px-5 py-4 space-y-3 ${className}`, "data-section": "eventpollwidget-div-1", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-500 dark:text-zinc-400", children: "This poll has closed." }), totalEntries !== undefined && (_jsxs(Text, { className: "text-sm text-zinc-400 dark:text-zinc-500", children: [totalEntries.toLocaleString(), " vote", totalEntries !== 1 ? "s" : "", " cast"] })), _jsx("div", { className: "space-y-2", "data-section": "eventpollwidget-div-2", children: pollConfig.options.map((opt) => (_jsx("div", { className: "rounded-lg border border-zinc-100 dark:border-zinc-800 px-4 py-2.5 text-sm text-zinc-600 dark:text-zinc-400", "data-section": "eventpollwidget-div-3", children: opt.label }, opt.id))) })] }));
59
+ return (_jsxs("div", { className: `rounded-xl border border-zinc-200 dark:border-zinc-700 px-5 py-4 space-y-3 ${className}`, "data-section": "eventpollwidget-div-1", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-500 dark:text-zinc-400", children: "This poll has closed." }), totalEntries !== undefined && (_jsxs(Text, { className: "text-sm text-zinc-400 dark:text-zinc-400", children: [totalEntries.toLocaleString(), " vote", totalEntries !== 1 ? "s" : "", " cast"] })), _jsx("div", { className: "space-y-2", "data-section": "eventpollwidget-div-2", children: pollConfig.options.map((opt) => (_jsx("div", { className: "rounded-lg border border-zinc-100 dark:border-zinc-800 px-4 py-2.5 text-sm text-zinc-600 dark:text-zinc-400", "data-section": "eventpollwidget-div-3", children: opt.label }, opt.id))) })] }));
60
60
  }
61
61
  if (pollConfig.requireLogin && !user) {
62
62
  return (_jsxs("div", { className: `rounded-xl border border-zinc-200 dark:border-zinc-700 px-6 py-8 text-center space-y-3 ${className}`, "data-section": "eventpollwidget-div-4", children: [_jsx(Text, { className: "font-semibold text-zinc-900 dark:text-zinc-100", children: "Login to vote" }), _jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "You need an account to participate in this poll." }), _jsx("a", { href: String(ROUTES.AUTH.LOGIN), className: "inline-block rounded-xl bg-primary px-6 py-2.5 text-sm font-semibold text-white hover:bg-primary-600", children: "Log In" })] }));
@@ -1,11 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Span } from "../../../ui";
3
3
  const STATUS_MAP = {
4
- draft: { label: "Draft", color: "bg-gray-100 text-gray-700" },
5
- active: { label: "Active", color: "bg-green-100 text-green-700" },
6
- paused: { label: "Paused", color: "bg-yellow-100 text-yellow-700" },
7
- ended: { label: "Ended", color: "bg-red-100 text-red-600" },
8
- cancelled: { label: "Cancelled", color: "bg-red-100 text-red-700" },
4
+ draft: { label: "Draft", color: "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300" },
5
+ active: { label: "Active", color: "bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300" },
6
+ paused: { label: "Paused", color: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/40 dark:text-yellow-300" },
7
+ ended: { label: "Ended", color: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300" },
8
+ cancelled: { label: "Cancelled", color: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300" },
9
9
  };
10
10
  export function EventStatusBadge({ status, className = "", }) {
11
11
  const { label, color } = STATUS_MAP[status] ?? STATUS_MAP.draft;
@@ -116,5 +116,5 @@ export function EventsIndexListing({ initialData }) {
116
116
  const commitSearch = useCallback(() => {
117
117
  table.set(TABLE_KEYS.QUERY, searchInput.trim());
118
118
  }, [searchInput, table]);
119
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || sortBy(EVENT_FIELDS.STARTS_AT, "ASC"), sortOptions: EVENT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-4 space-y-2", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded mt-2" })] })] }, i))) })) : events.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No events found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(EventFilters, { table: pendingTable, variant: "public" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
119
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || sortBy(EVENT_FIELDS.STARTS_AT, "ASC"), sortOptions: EVENT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-4 space-y-2", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded mt-2" })] })] }, i))) })) : events.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No events found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(EventFilters, { table: pendingTable, variant: "public" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
120
120
  }