@mohasinac/appkit 2.3.1 → 2.3.2

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 (253) hide show
  1. package/dist/client.d.ts +12 -8
  2. package/dist/client.js +7 -4
  3. package/dist/constants/api-endpoints.d.ts +4 -0
  4. package/dist/constants/api-endpoints.js +2 -0
  5. package/dist/core/contact-submissions.repository.d.ts +32 -0
  6. package/dist/core/contact-submissions.repository.js +49 -0
  7. package/dist/core/index.d.ts +2 -0
  8. package/dist/core/index.js +1 -0
  9. package/dist/features/about/components/HowPayoutsWorkView.js +1 -1
  10. package/dist/features/account/components/AddressFilters.d.ts +5 -0
  11. package/dist/features/account/components/AddressFilters.js +20 -0
  12. package/dist/features/account/components/AddressesIndexListing.d.ts +6 -0
  13. package/dist/features/account/components/AddressesIndexListing.js +51 -0
  14. package/dist/features/account/components/UserSidebar.d.ts +7 -3
  15. package/dist/features/account/components/UserSidebar.js +55 -7
  16. package/dist/features/account/hooks/useAddresses.d.ts +7 -0
  17. package/dist/features/account/hooks/useAddresses.js +12 -1
  18. package/dist/features/admin/actions/admin-read-actions.d.ts +12 -0
  19. package/dist/features/admin/actions/admin-read-actions.js +18 -0
  20. package/dist/features/admin/components/AdminBlogView.js +26 -2
  21. package/dist/features/admin/components/AdminCarouselView.js +18 -2
  22. package/dist/features/admin/components/AdminCategoriesView.js +27 -2
  23. package/dist/features/admin/components/AdminContactView.d.ts +4 -0
  24. package/dist/features/admin/components/AdminContactView.js +50 -0
  25. package/dist/features/admin/components/AdminFaqsView.js +19 -2
  26. package/dist/features/admin/components/AdminListingScaffold.d.ts +11 -2
  27. package/dist/features/admin/components/AdminListingScaffold.js +14 -3
  28. package/dist/features/admin/components/AdminNewsletterView.d.ts +4 -0
  29. package/dist/features/admin/components/AdminNewsletterView.js +50 -0
  30. package/dist/features/admin/components/AdminProductsView.js +30 -2
  31. package/dist/features/admin/components/AdminReviewsView.js +26 -2
  32. package/dist/features/admin/components/AdminStoresView.js +17 -2
  33. package/dist/features/admin/components/AdminUsersView.js +27 -2
  34. package/dist/features/admin/components/DataTable.d.ts +2 -1
  35. package/dist/features/admin/components/DataTable.js +18 -4
  36. package/dist/features/admin/components/index.d.ts +6 -0
  37. package/dist/features/admin/components/index.js +3 -0
  38. package/dist/features/admin/hooks/useAdminListingData.d.ts +3 -1
  39. package/dist/features/admin/hooks/useAdminListingData.js +12 -7
  40. package/dist/features/admin/server.d.ts +3 -0
  41. package/dist/features/admin/server.js +2 -0
  42. package/dist/features/auctions/components/AuctionDetailPageView.js +93 -47
  43. package/dist/features/auctions/components/AuctionFilters.d.ts +8 -0
  44. package/dist/features/auctions/components/AuctionFilters.js +12 -0
  45. package/dist/features/auctions/components/AuctionsListView.d.ts +6 -1
  46. package/dist/features/auctions/components/AuctionsListView.js +37 -5
  47. package/dist/features/auctions/schemas/index.d.ts +4 -4
  48. package/dist/features/blog/components/BlogFeaturedCard.d.ts +1 -7
  49. package/dist/features/blog/components/BlogFeaturedCard.js +4 -5
  50. package/dist/features/blog/components/BlogFilters.js +2 -1
  51. package/dist/features/blog/components/BlogIndexListing.js +14 -9
  52. package/dist/features/blog/components/BlogIndexPageView.d.ts +6 -1
  53. package/dist/features/blog/components/BlogIndexPageView.js +10 -2
  54. package/dist/features/blog/components/BlogListView.d.ts +2 -1
  55. package/dist/features/blog/components/BlogListView.js +10 -3
  56. package/dist/features/categories/components/CategoriesIndexListing.d.ts +1 -1
  57. package/dist/features/categories/components/CategoriesIndexListing.js +41 -38
  58. package/dist/features/categories/components/CategoriesIndexPageView.d.ts +6 -1
  59. package/dist/features/categories/components/CategoriesIndexPageView.js +41 -2
  60. package/dist/features/categories/components/CategoryDetailPageView.js +13 -6
  61. package/dist/features/categories/components/CategoryDetailTabs.d.ts +5 -0
  62. package/dist/features/categories/components/CategoryDetailTabs.js +17 -0
  63. package/dist/features/categories/components/CategoryFilters.js +2 -1
  64. package/dist/features/categories/components/CategoryGrid.d.ts +2 -1
  65. package/dist/features/categories/components/CategoryGrid.js +8 -6
  66. package/dist/features/categories/components/CategoryProductsListing.js +22 -11
  67. package/dist/features/categories/components/index.d.ts +2 -0
  68. package/dist/features/categories/components/index.js +1 -0
  69. package/dist/features/categories/hooks/useCategories.d.ts +20 -0
  70. package/dist/features/categories/hooks/useCategories.js +50 -1
  71. package/dist/features/categories/hooks/useCategoryTree.d.ts +17 -0
  72. package/dist/features/categories/hooks/useCategoryTree.js +65 -0
  73. package/dist/features/events/components/AdminEventEditorView.d.ts +6 -0
  74. package/dist/features/events/components/AdminEventEditorView.js +203 -0
  75. package/dist/features/events/components/AdminEventsView.js +28 -2
  76. package/dist/features/events/components/EventCard.js +4 -2
  77. package/dist/features/events/components/EventFilters.js +2 -1
  78. package/dist/features/events/components/EventsIndexListing.js +40 -10
  79. package/dist/features/events/components/EventsListPageView.d.ts +6 -1
  80. package/dist/features/events/components/EventsListPageView.js +40 -7
  81. package/dist/features/events/components/index.d.ts +2 -0
  82. package/dist/features/events/components/index.js +1 -0
  83. package/dist/features/events/hooks/useEvents.js +2 -0
  84. package/dist/features/events/types/index.d.ts +1 -0
  85. package/dist/features/homepage/components/BlogArticlesSection.js +1 -1
  86. package/dist/features/homepage/components/EventsSection.js +1 -1
  87. package/dist/features/homepage/components/FeaturedAuctionsSection.js +1 -1
  88. package/dist/features/homepage/components/FeaturedPreOrdersSection.js +1 -1
  89. package/dist/features/homepage/components/FeaturedProductsSection.js +1 -1
  90. package/dist/features/homepage/components/FeaturedStoresSection.js +1 -1
  91. package/dist/features/homepage/components/HeroCarousel.js +1 -1
  92. package/dist/features/homepage/components/MarketplaceHomepageView.js +27 -17
  93. package/dist/features/homepage/components/SectionCarousel.js +4 -4
  94. package/dist/features/homepage/components/ShopByCategorySection.js +2 -2
  95. package/dist/features/homepage/schemas/firestore.d.ts +1 -1
  96. package/dist/features/homepage/schemas/firestore.js +2 -1
  97. package/dist/features/layout/AppLayoutShell.d.ts +6 -2
  98. package/dist/features/layout/AppLayoutShell.js +7 -3
  99. package/dist/features/pre-orders/components/MarketplacePreorderCard.d.ts +3 -1
  100. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +6 -2
  101. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +80 -12
  102. package/dist/features/pre-orders/components/PreOrderFilters.d.ts +8 -0
  103. package/dist/features/pre-orders/components/PreOrderFilters.js +21 -0
  104. package/dist/features/pre-orders/components/PreOrdersIndexListing.d.ts +2 -1
  105. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +69 -10
  106. package/dist/features/pre-orders/components/PreOrdersListView.d.ts +6 -1
  107. package/dist/features/pre-orders/components/PreOrdersListView.js +26 -7
  108. package/dist/features/pre-orders/components/index.d.ts +2 -0
  109. package/dist/features/pre-orders/components/index.js +1 -0
  110. package/dist/features/products/components/AuctionsIndexListing.d.ts +2 -1
  111. package/dist/features/products/components/AuctionsIndexListing.js +61 -9
  112. package/dist/features/products/components/InteractiveProductCard.d.ts +2 -4
  113. package/dist/features/products/components/InteractiveProductCard.js +2 -2
  114. package/dist/features/products/components/ProductDetailPageView.d.ts +1 -1
  115. package/dist/features/products/components/ProductDetailPageView.js +116 -25
  116. package/dist/features/products/components/ProductFilters.d.ts +6 -11
  117. package/dist/features/products/components/ProductFilters.js +5 -3
  118. package/dist/features/products/components/ProductGrid.d.ts +8 -2
  119. package/dist/features/products/components/ProductGrid.js +20 -5
  120. package/dist/features/products/components/ProductTabsShell.d.ts +3 -11
  121. package/dist/features/products/components/ProductTabsShell.js +14 -14
  122. package/dist/features/products/components/ProductsIndexListing.js +73 -9
  123. package/dist/features/products/components/ProductsIndexPageView.d.ts +6 -1
  124. package/dist/features/products/components/ProductsIndexPageView.js +39 -6
  125. package/dist/features/products/components/RelatedProductsCarousel.d.ts +7 -0
  126. package/dist/features/products/components/RelatedProductsCarousel.js +11 -0
  127. package/dist/features/products/components/index.d.ts +1 -0
  128. package/dist/features/products/components/index.js +1 -0
  129. package/dist/features/products/hooks/useProducts.js +16 -0
  130. package/dist/features/products/repository/products.repository.d.ts +8 -0
  131. package/dist/features/products/repository/products.repository.js +2 -0
  132. package/dist/features/products/schemas/index.d.ts +8 -8
  133. package/dist/features/products/types/index.d.ts +12 -0
  134. package/dist/features/promotions/components/CouponsIndexListing.d.ts +9 -0
  135. package/dist/features/promotions/components/CouponsIndexListing.js +86 -0
  136. package/dist/features/promotions/components/PromotionsView.d.ts +11 -5
  137. package/dist/features/promotions/components/PromotionsView.js +6 -1
  138. package/dist/features/promotions/components/index.d.ts +4 -2
  139. package/dist/features/promotions/components/index.js +2 -1
  140. package/dist/features/reviews/components/ReviewDetailPageView.d.ts +4 -0
  141. package/dist/features/reviews/components/ReviewDetailPageView.js +20 -0
  142. package/dist/features/reviews/components/ReviewDetailShell.d.ts +7 -0
  143. package/dist/features/reviews/components/ReviewDetailShell.js +80 -0
  144. package/dist/features/reviews/components/ReviewFilters.d.ts +3 -3
  145. package/dist/features/reviews/components/ReviewFilters.js +5 -4
  146. package/dist/features/reviews/components/ReviewsIndexListing.d.ts +4 -3
  147. package/dist/features/reviews/components/ReviewsIndexListing.js +35 -51
  148. package/dist/features/reviews/components/ReviewsIndexPageView.d.ts +6 -1
  149. package/dist/features/reviews/components/ReviewsIndexPageView.js +49 -3
  150. package/dist/features/reviews/components/ReviewsList.js +9 -1
  151. package/dist/features/reviews/components/index.d.ts +1 -0
  152. package/dist/features/reviews/hooks/useReviews.js +15 -1
  153. package/dist/features/reviews/types/index.d.ts +6 -1
  154. package/dist/features/seller/components/SellerSidebar.d.ts +8 -4
  155. package/dist/features/seller/components/SellerSidebar.js +6 -4
  156. package/dist/features/seller/components/index.d.ts +30 -0
  157. package/dist/features/seller/components/index.js +17 -0
  158. package/dist/features/seller/hooks/useSellerStore.d.ts +2 -0
  159. package/dist/features/seller/hooks/useSellerStore.js +2 -0
  160. package/dist/features/seller/permission-map.d.ts +4 -2
  161. package/dist/features/seller/permission-map.js +16 -14
  162. package/dist/features/seller/schemas/index.d.ts +2 -2
  163. package/dist/features/stores/api/[storeSlug]/reviews/route.d.ts +1 -1
  164. package/dist/features/stores/api/[storeSlug]/reviews/route.js +24 -19
  165. package/dist/features/stores/components/InteractiveStoreCard.d.ts +0 -5
  166. package/dist/features/stores/components/InteractiveStoreCard.js +9 -9
  167. package/dist/features/stores/components/StoreAuctionsListing.js +27 -9
  168. package/dist/features/stores/components/StoreDetailLayoutView.js +2 -0
  169. package/dist/features/stores/components/StoreFilters.d.ts +5 -0
  170. package/dist/features/stores/components/StoreFilters.js +20 -0
  171. package/dist/features/stores/components/StoreHeader.js +2 -2
  172. package/dist/features/stores/components/StorePreOrdersListing.d.ts +5 -0
  173. package/dist/features/stores/components/StorePreOrdersListing.js +40 -0
  174. package/dist/features/stores/components/StorePreOrdersPageView.d.ts +4 -0
  175. package/dist/features/stores/components/StorePreOrdersPageView.js +21 -0
  176. package/dist/features/stores/components/StoreProductsListing.js +21 -11
  177. package/dist/features/stores/components/StoreReviewsListing.js +2 -7
  178. package/dist/features/stores/components/StoresIndexListing.js +42 -8
  179. package/dist/features/stores/components/StoresIndexPageView.d.ts +6 -1
  180. package/dist/features/stores/components/StoresIndexPageView.js +9 -2
  181. package/dist/features/stores/components/index.d.ts +3 -0
  182. package/dist/features/stores/components/index.js +1 -0
  183. package/dist/features/stores/hooks/useStores.d.ts +7 -1
  184. package/dist/features/stores/hooks/useStores.js +16 -3
  185. package/dist/features/stores/schemas/index.d.ts +2 -2
  186. package/dist/features/stores/types/index.d.ts +3 -0
  187. package/dist/features/wishlist/hooks/useGuestWishlist.d.ts +20 -0
  188. package/dist/features/wishlist/hooks/useGuestWishlist.js +49 -0
  189. package/dist/features/wishlist/hooks/useWishlistCount.d.ts +7 -0
  190. package/dist/features/wishlist/hooks/useWishlistCount.js +31 -0
  191. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +56 -0
  192. package/dist/features/wishlist/hooks/useWishlistWithGuest.js +57 -0
  193. package/dist/features/wishlist/index.d.ts +3 -0
  194. package/dist/features/wishlist/index.js +3 -0
  195. package/dist/features/wishlist/utils/guest-wishlist.d.ts +22 -0
  196. package/dist/features/wishlist/utils/guest-wishlist.js +70 -0
  197. package/dist/index.d.ts +50 -1
  198. package/dist/index.js +63 -1
  199. package/dist/next/routing/route-map.d.ts +70 -36
  200. package/dist/next/routing/route-map.js +30 -22
  201. package/dist/seed/addresses-seed-data.js +62 -261
  202. package/dist/seed/beyblade-seed-data.d.ts +7 -0
  203. package/dist/seed/beyblade-seed-data.js +947 -0
  204. package/dist/seed/bids-seed-data.d.ts +10 -2
  205. package/dist/seed/bids-seed-data.js +220 -1071
  206. package/dist/seed/blog-posts-seed-data.d.ts +2 -2
  207. package/dist/seed/blog-posts-seed-data.js +455 -117
  208. package/dist/seed/cart-seed-data.d.ts +9 -9
  209. package/dist/seed/cart-seed-data.js +73 -74
  210. package/dist/seed/coupons-seed-data.d.ts +3 -4
  211. package/dist/seed/coupons-seed-data.js +3 -509
  212. package/dist/seed/events-seed-data.d.ts +2 -2
  213. package/dist/seed/events-seed-data.js +315 -476
  214. package/dist/seed/faq-seed-data.d.ts +18 -41
  215. package/dist/seed/faq-seed-data.js +1059 -1172
  216. package/dist/seed/hot-wheels-seed-data.d.ts +7 -0
  217. package/dist/seed/hot-wheels-seed-data.js +1365 -0
  218. package/dist/seed/index.d.ts +6 -1
  219. package/dist/seed/index.js +6 -1
  220. package/dist/seed/pokemon-carousel-slides-seed-data.d.ts +4 -2
  221. package/dist/seed/pokemon-carousel-slides-seed-data.js +152 -268
  222. package/dist/seed/pokemon-categories-seed-data.d.ts +18 -21
  223. package/dist/seed/pokemon-categories-seed-data.js +424 -1004
  224. package/dist/seed/pokemon-coupons-seed-data.d.ts +6 -0
  225. package/dist/seed/pokemon-coupons-seed-data.js +465 -0
  226. package/dist/seed/pokemon-homepage-sections-seed-data.d.ts +3 -2
  227. package/dist/seed/pokemon-homepage-sections-seed-data.js +67 -289
  228. package/dist/seed/pokemon-products-seed-data.js +662 -0
  229. package/dist/seed/pokemon-seed-bundle.d.ts +32 -11
  230. package/dist/seed/pokemon-seed-bundle.js +41 -11
  231. package/dist/seed/pokemon-stores-seed-data.d.ts +2 -3
  232. package/dist/seed/pokemon-stores-seed-data.js +56 -31
  233. package/dist/seed/pokemon-users-seed-data.d.ts +2 -2
  234. package/dist/seed/pokemon-users-seed-data.js +245 -261
  235. package/dist/seed/reviews-seed-data.d.ts +17 -2
  236. package/dist/seed/reviews-seed-data.js +519 -483
  237. package/dist/seed/site-settings-seed-data.js +14 -14
  238. package/dist/seed/store-addresses-seed-data.js +68 -50
  239. package/dist/seed/transformers-seed-data.d.ts +7 -0
  240. package/dist/seed/transformers-seed-data.js +510 -0
  241. package/dist/seed/wishlists-seed-data.d.ts +5 -1
  242. package/dist/seed/wishlists-seed-data.js +82 -4
  243. package/dist/server.d.ts +1 -0
  244. package/dist/server.js +2 -0
  245. package/dist/tokens/index.d.ts +6 -0
  246. package/dist/tokens/index.js +2 -0
  247. package/dist/ui/components/BaseListingCard.js +24 -26
  248. package/dist/ui/components/BaseListingCard.style.css +5 -5
  249. package/dist/ui/components/HorizontalScroller.d.ts +1 -1
  250. package/dist/ui/components/HorizontalScroller.js +19 -5
  251. package/dist/ui/components/SideDrawer.style.css +3 -11
  252. package/dist/ui/rich-text/RichText.js +19 -1
  253. package/package.json +1 -1
@@ -85,6 +85,13 @@ const DEFAULT_SECURITY_ITEMS = [
85
85
  },
86
86
  ];
87
87
  // --- Helpers -----------------------------------------------------------------
88
+ /** Strip leading emoji/symbols that admins sometimes prefix onto DB titles. */
89
+ function cleanTitle(raw) {
90
+ if (!raw)
91
+ return raw;
92
+ const cleaned = raw.replace(/^[^\p{L}\p{N}]+/u, "").trim();
93
+ return cleaned || raw;
94
+ }
88
95
  function parseWelcomeDescription(description) {
89
96
  if (!description)
90
97
  return "";
@@ -105,18 +112,21 @@ function parseWelcomeDescription(description) {
105
112
  * Render a single homepage section based on its type and config
106
113
  */
107
114
  const AD_SLOT_MAP = {
115
+ carousel: "afterHero",
108
116
  products: "afterFeaturedProducts",
109
117
  reviews: "afterReviews",
110
118
  faq: "afterFAQ",
111
119
  };
112
- function renderSection(section, adSlots, newsletterFormSlot, faqItems) {
120
+ function renderSection(section, adSlots, newsletterFormSlot, faqItems, slides) {
113
121
  const { type, config } = section;
114
122
  const adSlotKey = AD_SLOT_MAP[type];
115
123
  const sectionElement = (() => {
116
124
  switch (type) {
125
+ case "carousel":
126
+ return _jsx(HeroCarousel, { initialSlides: slides });
117
127
  case "welcome": {
118
128
  const cfg = config;
119
- const title = cfg?.h1 || "Discover Amazing Products";
129
+ const title = cleanTitle(cfg?.h1) || "Discover Amazing Products";
120
130
  const subtitle = parseWelcomeDescription(cfg?.description);
121
131
  const ctaText = cfg?.ctaText || "Shop Now";
122
132
  const ctaLink = cfg?.ctaLink || ROUTES.PUBLIC.PRODUCTS;
@@ -129,7 +139,7 @@ function renderSection(section, adSlots, newsletterFormSlot, faqItems) {
129
139
  }
130
140
  case "categories": {
131
141
  const cfg = config;
132
- return (_jsx(ShopByCategorySection, { title: cfg?.title || "Shop by Category", viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All categories \u2192" }));
142
+ return (_jsx(ShopByCategorySection, { title: cleanTitle(cfg?.title) || "Shop by Category", viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All categories \u2192" }));
133
143
  }
134
144
  case "stats": {
135
145
  const cfg = config;
@@ -154,27 +164,27 @@ function renderSection(section, adSlots, newsletterFormSlot, faqItems) {
154
164
  }
155
165
  case "products": {
156
166
  const cfg = config;
157
- return (_jsx(FeaturedProductsSection, { title: cfg?.title || "Featured Products", viewMoreHref: ROUTES.PUBLIC.PRODUCTS, viewMoreLabel: "View all products \u2192" }));
167
+ return (_jsx(FeaturedProductsSection, { title: cleanTitle(cfg?.title) || "Featured Products", viewMoreHref: ROUTES.PUBLIC.PRODUCTS, viewMoreLabel: "View all products \u2192" }));
158
168
  }
159
169
  case "auctions": {
160
170
  const cfg = config;
161
- return (_jsx(FeaturedAuctionsSection, { title: cfg?.title || "Live Auctions", viewMoreHref: ROUTES.PUBLIC.AUCTIONS, viewMoreLabel: "View all auctions \u2192" }));
171
+ return (_jsx(FeaturedAuctionsSection, { title: cleanTitle(cfg?.title) || "Live Auctions", viewMoreHref: ROUTES.PUBLIC.AUCTIONS, viewMoreLabel: "View all auctions \u2192" }));
162
172
  }
163
173
  case "pre-orders": {
164
174
  const cfg = config;
165
- return (_jsx(FeaturedPreOrdersSection, { title: cfg?.title || "Reserve Before It Ships", viewMoreHref: ROUTES.PUBLIC.PRE_ORDERS, viewMoreLabel: "View all pre-orders \u2192" }));
175
+ return (_jsx(FeaturedPreOrdersSection, { title: cleanTitle(cfg?.title) || "Reserve Before It Ships", viewMoreHref: ROUTES.PUBLIC.PRE_ORDERS, viewMoreLabel: "View all pre-orders \u2192" }));
166
176
  }
167
177
  case "stores": {
168
178
  const cfg = config;
169
- return (_jsx(FeaturedStoresSection, { title: cfg?.title || "Featured Stores", viewMoreHref: ROUTES.PUBLIC.STORES, viewMoreLabel: "View all stores \u2192" }));
179
+ return (_jsx(FeaturedStoresSection, { title: cleanTitle(cfg?.title) || "Featured Stores", viewMoreHref: ROUTES.PUBLIC.STORES, viewMoreLabel: "View all stores \u2192" }));
170
180
  }
171
181
  case "events": {
172
182
  const cfg = config;
173
- return (_jsx(EventsSection, { title: cfg?.title || "Events & Offers", viewMoreHref: ROUTES.PUBLIC.EVENTS, viewMoreLabel: "View all events \u2192" }));
183
+ return (_jsx(EventsSection, { title: cleanTitle(cfg?.title) || "Events & Offers", viewMoreHref: ROUTES.PUBLIC.EVENTS, viewMoreLabel: "View all events \u2192" }));
174
184
  }
175
185
  case "reviews": {
176
186
  const cfg = config;
177
- return (_jsx(HomepageCustomerReviewsSection, { title: cfg?.title || "What Our Customers Say", viewMoreHref: ROUTES.PUBLIC.REVIEWS, viewMoreLabel: "See all reviews \u2192" }));
187
+ return (_jsx(HomepageCustomerReviewsSection, { title: cleanTitle(cfg?.title) || "What Our Customers Say", viewMoreHref: ROUTES.PUBLIC.REVIEWS, viewMoreLabel: "See all reviews \u2192" }));
178
188
  }
179
189
  case "banner": {
180
190
  const cfg = config;
@@ -182,34 +192,34 @@ function renderSection(section, adSlots, newsletterFormSlot, faqItems) {
182
192
  }
183
193
  case "trust-indicators": {
184
194
  const cfg = config;
185
- return (_jsx(TrustFeaturesSection, { title: cfg?.title || "Why Buyers Trust LetiTrip", items: DEFAULT_TRUST_FEATURES }));
195
+ return (_jsx(TrustFeaturesSection, { title: cleanTitle(cfg?.title) || "Why Buyers Trust LetiTrip", items: DEFAULT_TRUST_FEATURES }));
186
196
  }
187
197
  case "features": {
188
198
  const cfg = config;
189
- return (_jsx(SecurityHighlightsSection, { title: cfg?.title || "Security You Can Trust", pillLabel: "Built for trust", items: DEFAULT_SECURITY_ITEMS, learnMoreHref: ROUTES.PUBLIC.SECURITY, learnMoreLabel: "Learn about our security \u2192" }));
199
+ return (_jsx(SecurityHighlightsSection, { title: cleanTitle(cfg?.title) || "Security You Can Trust", pillLabel: "Built for trust", items: DEFAULT_SECURITY_ITEMS, learnMoreHref: ROUTES.PUBLIC.SECURITY, learnMoreLabel: "Learn about our security \u2192" }));
190
200
  }
191
201
  case "whatsapp-community": {
192
202
  const cfg = config;
193
- return (_jsx(WhatsAppCommunitySection, { title: cfg?.title || "Join Our WhatsApp Community", descriptionHtml: cfg?.description || "5,000+ members. Get deal alerts, auction updates, and exclusive drops before anyone else.", memberCount: cfg?.memberCount || 5000, groupLink: cfg?.groupLink || "https://chat.whatsapp.com/" }));
203
+ return (_jsx(WhatsAppCommunitySection, { title: cleanTitle(cfg?.title) || "Join Our WhatsApp Community", descriptionHtml: cfg?.description || "5,000+ members. Get deal alerts, auction updates, and exclusive drops before anyone else.", memberCount: cfg?.memberCount || 5000, groupLink: cfg?.groupLink || "https://chat.whatsapp.com/" }));
194
204
  }
195
205
  case "faq": {
196
206
  const cfg = config;
197
207
  if (!cfg?.showOnHomepage) {
198
208
  return null;
199
209
  }
200
- return (_jsx(FAQSection, { title: cfg?.title || "Frequently Asked Questions", tabs: [], activeTab: "", items: faqItems, viewMoreHref: ROUTES.PUBLIC.FAQS, viewMoreLabel: "View all FAQs \u2192" }));
210
+ return (_jsx(FAQSection, { title: cleanTitle(cfg?.title) || "Frequently Asked Questions", tabs: [], activeTab: "", items: faqItems, viewMoreHref: ROUTES.PUBLIC.FAQS, viewMoreLabel: "View all FAQs \u2192" }));
201
211
  }
202
212
  case "blog-articles": {
203
213
  const cfg = config;
204
- return (_jsx(BlogArticlesSection, { title: cfg?.title || "From Our Blog", viewMoreHref: ROUTES.BLOG.LIST, viewMoreLabel: "View all posts \u2192" }));
214
+ return (_jsx(BlogArticlesSection, { title: cleanTitle(cfg?.title) || "From Our Blog", viewMoreHref: ROUTES.BLOG.LIST, viewMoreLabel: "View all posts \u2192" }));
205
215
  }
206
216
  case "newsletter": {
207
217
  const cfg = config;
208
- return (_jsx(NewsletterSection, { title: cfg?.title || "Stay Updated", subtitle: cfg?.description || "Get the latest drops, auctions, and deals in your inbox.", renderForm: () => newsletterFormSlot ?? null }));
218
+ return (_jsx(NewsletterSection, { title: cleanTitle(cfg?.title) || "Stay Updated", subtitle: cfg?.description || "Get the latest drops, auctions, and deals in your inbox.", renderForm: () => newsletterFormSlot ?? null }));
209
219
  }
210
220
  case "brands": {
211
221
  const cfg = config;
212
- return (_jsx(BrandsSection, { title: cfg?.title || "Top Brands", subtitle: cfg?.subtitle, limit: cfg?.maxBrands || 12, viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All brands \u2192" }));
222
+ return (_jsx(BrandsSection, { title: cleanTitle(cfg?.title) || "Top Brands", subtitle: cfg?.subtitle, limit: cfg?.maxBrands || 12, viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All brands \u2192" }));
213
223
  }
214
224
  default:
215
225
  return null;
@@ -248,5 +258,5 @@ export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, } =
248
258
  answer: typeof faq.answer === "string" ? faq.answer : faq.answer.text,
249
259
  }));
250
260
  // Render with dynamic sections from DB
251
- return (_jsxs(Main, { children: [showAnnouncement ? _jsx(AnnouncementBar, { message: announcementMessage }) : null, _jsx(HeroCarousel, { initialSlides: slides }), adSlots?.afterHero, orderedSections.map((section) => renderSection(section, adSlots, newsletterFormSlot ?? null, faqItems))] }));
261
+ return (_jsxs(Main, { children: [showAnnouncement ? _jsx(AnnouncementBar, { message: announcementMessage }) : null, orderedSections.map((section) => renderSection(section, adSlots, newsletterFormSlot ?? null, faqItems, slides))] }));
252
262
  }
@@ -8,7 +8,7 @@ function CarouselSkeleton({ count }) {
8
8
  return (_jsx("div", { className: "flex gap-4 overflow-hidden px-4", "data-section": "sectioncarousel-div-349", children: Array.from({ length: count }).map((_, i) => (_jsxs("div", { className: "flex-none min-w-[clamp(150px,18vw,260px)] max-w-[clamp(240px,36vw,380px)] h-[clamp(180px,26vh,260px)] space-y-2", "data-section": "sectioncarousel-div-350", children: [_jsx("div", { className: `aspect-square rounded-xl ${skeleton.image}` }), _jsx("div", { className: `${skeleton.text} w-3/4` }), _jsx("div", { className: `${skeleton.text} w-1/2` })] }, i))) }));
9
9
  }
10
10
  // --- Component ---------------------------------------------------------------
11
- export function SectionCarousel({ title, description, headingVariant = "default", pillLabel, backgroundImage, viewMoreHref, viewMoreLabel = "View all →", items, renderItem, perView = THEME_CONSTANTS.carousel.perView.default, gap = 16, autoScroll = false, autoScrollInterval = 3500, keyExtractor, rows = 1, className = "", isLoading = false, skeletonCount = 4, lightText, showPeek = false, minItemWidth = 220, }) {
11
+ export function SectionCarousel({ title, description, headingVariant = "editorial", pillLabel, backgroundImage, viewMoreHref, viewMoreLabel = "View all →", items, renderItem, perView = THEME_CONSTANTS.carousel.perView.standard, gap = 16, autoScroll = false, autoScrollInterval = 3500, keyExtractor, rows = 1, className = "", isLoading = false, skeletonCount = 4, lightText, showPeek = false, minItemWidth = 220, }) {
12
12
  const hasBg = Boolean(backgroundImage);
13
13
  const useLightText = lightText ?? hasBg;
14
14
  const { themed, flex } = THEME_CONSTANTS;
@@ -39,7 +39,7 @@ export function SectionCarousel({ title, description, headingVariant = "default"
39
39
  - show fade edges for visual affordance
40
40
  - hide native scrollbar for cleaner appearance
41
41
  */
42
- showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, pauseOnHover: true })), viewMoreHref && !isLoading && (_jsx("div", { className: "mt-6 text-center", "data-section": "sectioncarousel-div-356", children: _jsx(TextLink, { href: viewMoreHref, className: `inline-flex items-center gap-2 px-6 py-2.5 rounded-full text-sm font-medium transition-colors ${useLightText
43
- ? "bg-white/20 text-white hover:bg-white/30 backdrop-blur-sm"
44
- : "bg-primary/10 text-primary hover:bg-primary/20 dark:bg-primary/15 dark:text-primary-400"}`, children: viewMoreLabel }) }))] })] }));
42
+ showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, pauseOnHover: true })), viewMoreHref && !isLoading && (_jsx("div", { className: "mt-6 flex justify-center", "data-section": "sectioncarousel-div-356", children: _jsx(TextLink, { href: viewMoreHref, className: `inline-flex items-center gap-1.5 rounded-lg border px-6 py-2.5 text-sm font-medium transition-colors ${useLightText
43
+ ? "border-white/40 text-white hover:bg-white/10"
44
+ : "border-zinc-200 dark:border-zinc-700 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-800"}`, children: viewMoreLabel }) }))] })] }));
45
45
  }
@@ -10,12 +10,12 @@ function CategoryChip({ category }) {
10
10
  const coverImage = category.display?.coverImage;
11
11
  const initial = category.name[0]?.toUpperCase() ?? "?";
12
12
  const productCount = category.metrics?.productCount ?? 0;
13
- return (_jsxs(Link, { href: ROUTES.PUBLIC.CATEGORY_DETAIL(category.slug), className: "group flex min-h-[220px] min-w-[180px] max-w-[220px] flex-col overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-sm transition-all hover:border-primary-300 hover:shadow-md dark:border-slate-700 dark:bg-slate-900 dark:hover:border-primary-600", children: [coverImage ? (_jsx("div", { className: "aspect-video w-full overflow-hidden bg-zinc-100 dark:bg-slate-800", "data-section": "shopbycategorysection-div-362", children: _jsx("img", { src: coverImage, alt: category.name, className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" }) })) : (_jsx("div", { className: "aspect-video w-full bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700" })), _jsxs("div", { className: "flex flex-1 flex-col p-3 text-left", "data-section": "shopbycategorysection-div-363", children: [_jsx("div", { className: "mb-2 flex h-9 w-9 items-center justify-center rounded-lg bg-primary-100 text-sm font-bold text-primary-700 dark:bg-primary-900 dark:text-primary-300", "data-section": "shopbycategorysection-div-364", children: iconSrc ? (_jsx("img", { src: iconSrc, alt: "", className: "h-6 w-6 rounded object-cover", "aria-hidden": "true" })) : (initial) }), _jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-800 dark:text-zinc-200`, children: category.name }), _jsxs(Text, { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: [productCount.toLocaleString(), " items"] }), _jsx(Text, { className: "mt-auto pt-3 text-xs font-medium text-primary dark:text-primary-400", children: "Browse category \u2192" })] })] }));
13
+ return (_jsxs(Link, { href: ROUTES.PUBLIC.CATEGORY_DETAIL(category.slug), className: "group flex w-full min-h-[220px] flex-col overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-sm transition-all hover:border-primary-300 hover:shadow-md dark:border-slate-700 dark:bg-slate-900 dark:hover:border-primary-600", children: [coverImage ? (_jsx("div", { className: "aspect-video w-full overflow-hidden bg-zinc-100 dark:bg-slate-800", "data-section": "shopbycategorysection-div-362", children: _jsx("img", { src: coverImage, alt: category.name, className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" }) })) : (_jsx("div", { className: "aspect-video w-full bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700" })), _jsxs("div", { className: "flex flex-1 flex-col p-3 text-left", "data-section": "shopbycategorysection-div-363", children: [_jsx("div", { className: "mb-2 flex h-9 w-9 items-center justify-center rounded-lg bg-primary-100 text-sm font-bold text-primary-700 dark:bg-primary-900 dark:text-primary-300", "data-section": "shopbycategorysection-div-364", children: iconSrc ? (_jsx("img", { src: iconSrc, alt: "", className: "h-6 w-6 rounded object-cover", "aria-hidden": "true" })) : (initial) }), _jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-800 dark:text-zinc-200`, children: category.name }), _jsxs(Text, { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: [productCount.toLocaleString(), " items"] }), _jsx(Text, { className: "mt-auto pt-3 text-xs font-medium text-primary dark:text-primary-400", children: "Browse category \u2192" })] })] }));
14
14
  }
15
15
  export function ShopByCategorySection({ title = "Shop by Category", subtitle, limit = 12, viewMoreHref, viewMoreLabel = "View all categories →", className = "", }) {
16
16
  const { themed } = THEME_CONSTANTS;
17
17
  const { data: categories = [], isLoading } = useTopCategories(limit);
18
18
  if (!isLoading && categories.length === 0)
19
19
  return null;
20
- return (_jsx(Section, { className: `py-12 px-4 ${themed.bgSecondary} ${className}`, children: _jsxs("div", { className: "mx-auto max-w-7xl", "data-section": "shopbycategorysection-div-365", children: [_jsxs("div", { className: "mb-6 text-center", "data-section": "shopbycategorysection-div-366", children: [_jsx(Heading, { level: 2, className: `text-2xl font-bold md:text-3xl ${themed.textPrimary}`, children: title }), subtitle && (_jsx(Text, { variant: "secondary", className: "mt-1 text-sm", children: subtitle }))] }), isLoading ? (_jsx("div", { className: "flex gap-3 overflow-hidden px-1", "data-section": "shopbycategorysection-div-367", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "flex-none h-[104px] w-[108px] animate-pulse rounded-xl bg-zinc-200 dark:bg-slate-700" }, i))) })) : (_jsx(HorizontalScroller, { items: categories, renderItem: (cat) => _jsx(CategoryChip, { category: cat }), perView: THEME_CONSTANTS.carousel.perView.compact, gap: 16, keyExtractor: (cat) => cat.id, minItemWidth: 180 })), viewMoreHref && !isLoading && (_jsx("div", { className: "mt-6 text-center", "data-section": "shopbycategorysection-div-368", children: _jsx(Link, { href: viewMoreHref, className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:text-primary/80", children: viewMoreLabel }) }))] }) }));
20
+ return (_jsx(Section, { className: `py-12 px-4 ${themed.bgSecondary} ${className}`, children: _jsxs("div", { className: "mx-auto max-w-7xl", "data-section": "shopbycategorysection-div-365", children: [_jsxs("div", { className: "mb-6 text-center", "data-section": "shopbycategorysection-div-366", children: [_jsx(Heading, { level: 2, className: `text-2xl font-bold md:text-3xl ${themed.textPrimary}`, children: title }), subtitle && (_jsx(Text, { variant: "secondary", className: "mt-1 text-sm", children: subtitle }))] }), isLoading ? (_jsx("div", { className: "flex gap-3 overflow-hidden px-1", "data-section": "shopbycategorysection-div-367", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "flex-none h-[104px] w-[108px] animate-pulse rounded-xl bg-zinc-200 dark:bg-slate-700" }, i))) })) : (_jsx(HorizontalScroller, { items: categories, renderItem: (cat) => _jsx(CategoryChip, { category: cat }), perView: THEME_CONSTANTS.carousel.perView.standard, gap: 16, keyExtractor: (cat) => cat.id, autoScroll: true, autoScrollInterval: 3500, showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, pauseOnHover: true })), viewMoreHref && !isLoading && (_jsx("div", { className: "mt-6 text-center", "data-section": "shopbycategorysection-div-368", children: _jsx(Link, { href: viewMoreHref, className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:text-primary/80", children: viewMoreLabel }) }))] }) }));
21
21
  }
@@ -245,7 +245,7 @@ export interface BannerSectionConfig {
245
245
  clickable: boolean;
246
246
  clickLink?: string;
247
247
  }
248
- export type SectionType = "welcome" | "stats" | "trust-indicators" | "categories" | "brands" | "products" | "pre-orders" | "auctions" | "banner" | "features" | "reviews" | "whatsapp-community" | "faq" | "blog-articles" | "newsletter" | "stores" | "events";
248
+ export type SectionType = "welcome" | "carousel" | "stats" | "trust-indicators" | "categories" | "brands" | "products" | "pre-orders" | "auctions" | "banner" | "features" | "reviews" | "whatsapp-community" | "faq" | "blog-articles" | "newsletter" | "stores" | "events";
249
249
  export type SectionConfig = WelcomeSectionConfig | StatsSectionConfig | TrustIndicatorsSectionConfig | CategoriesSectionConfig | BrandsSectionConfig | ProductsSectionConfig | PreOrdersSectionConfig | AuctionsSectionConfig | BannerSectionConfig | FeaturesSectionConfig | ReviewsSectionConfig | WhatsAppCommunitySectionConfig | FAQSectionConfig | BlogArticlesSectionConfig | NewsletterSectionConfig | StoresSectionConfig | EventsSectionConfig;
250
250
  export interface HomepageSectionDocument {
251
251
  id: string;
@@ -53,7 +53,8 @@ export const HOMEPAGE_SECTIONS_INDEXED_FIELDS = [
53
53
  ];
54
54
  export const DEFAULT_SECTION_ORDER = {
55
55
  welcome: 1,
56
- stats: 2,
56
+ carousel: 2,
57
+ stats: 3,
57
58
  "trust-indicators": 3,
58
59
  categories: 2,
59
60
  brands: 2,
@@ -51,7 +51,9 @@ export interface AppLayoutShellProps {
51
51
  onLogout?: () => void;
52
52
  /** Href for the admin dashboard — shown in sidebar Dashboard section when user.role is admin. */
53
53
  adminHref?: string;
54
- /** Href for the seller dashboard — shown in sidebar Dashboard section when user.role is admin or seller. */
54
+ /** Href for the store management dashboard — shown in sidebar Dashboard section when user.role is admin or seller. */
55
+ storeHref?: string;
56
+ /** @deprecated Use storeHref */
55
57
  sellerHref?: string;
56
58
  /** Href for the user orders page — auto-generates PROFILE section in sidebar. */
57
59
  userOrdersHref?: string;
@@ -72,6 +74,8 @@ export interface AppLayoutShellProps {
72
74
  settings?: string;
73
75
  dashboardSectionTitle?: string;
74
76
  adminDashboard?: string;
77
+ storeDashboard?: string;
78
+ /** @deprecated Use storeDashboard */
75
79
  sellerDashboard?: string;
76
80
  logout?: string;
77
81
  };
@@ -95,4 +99,4 @@ export interface AppLayoutShellProps {
95
99
  };
96
100
  };
97
101
  }
98
- export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, logoHref, promotionsHref, cartHref, profileHref, loginHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, lightBackground, darkBackground, }: AppLayoutShellProps): import("react/jsx-runtime").JSX.Element;
102
+ export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, logoHref, promotionsHref, cartHref, profileHref, loginHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, lightBackground, darkBackground, }: AppLayoutShellProps): import("react/jsx-runtime").JSX.Element;
@@ -33,7 +33,7 @@ function CollapsibleSidebarSection({ section, navItemClass, }) {
33
33
  }
34
34
  return (_jsxs(Div, { className: "space-y-0.5", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-1 py-1 text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-300 transition-colors", children: [_jsx("span", { children: section.title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(Ul, { className: "space-y-0.5", children: section.items.map((item) => (_jsx(Li, { children: _jsxs(TextLink, { href: item.href, variant: "none", className: navItemClass, children: [item.icon && (_jsx("span", { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }) }, `${item.href}-${item.label}`))) }))] }));
35
35
  }
36
- export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, logoHref, promotionsHref, cartHref, profileHref, loginHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
36
+ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, logoHref, promotionsHref, cartHref, profileHref, loginHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
37
37
  const [queryClient] = useState(() => new QueryClient());
38
38
  const [sidebarOpen, setSidebarOpen] = useState(false);
39
39
  const [searchOpen, setSearchOpen] = useState(false);
@@ -60,6 +60,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
60
60
  const isAuthenticated = !!user;
61
61
  const role = user?.role ?? "user";
62
62
  const isAdminOrSeller = role === "admin" || role === "seller";
63
+ const resolvedStoreHref = storeHref ?? sellerHref;
63
64
  // Labels with defaults
64
65
  const labels = {
65
66
  sectionTitle: sidebarProfileLabels?.sectionTitle ?? "Profile",
@@ -69,7 +70,9 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
69
70
  settings: sidebarProfileLabels?.settings ?? "Settings",
70
71
  dashboardSectionTitle: sidebarProfileLabels?.dashboardSectionTitle ?? "Dashboard",
71
72
  adminDashboard: sidebarProfileLabels?.adminDashboard ?? "Admin Dashboard",
72
- sellerDashboard: sidebarProfileLabels?.sellerDashboard ?? "Seller Dashboard",
73
+ storeDashboard: sidebarProfileLabels?.storeDashboard ??
74
+ sidebarProfileLabels?.sellerDashboard ??
75
+ "Store Dashboard",
73
76
  logout: sidebarProfileLabels?.logout ?? "Logout",
74
77
  };
75
78
  const sectionLabelClass = "px-1 text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400";
@@ -86,7 +89,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
86
89
  action.variant === "outline"
87
90
  ? "border border-zinc-300 text-zinc-700 hover:bg-zinc-50 dark:border-slate-700 dark:text-zinc-100 dark:hover:bg-slate-800"
88
91
  : "bg-primary !text-zinc-900 hover:bg-primary-500 dark:bg-secondary dark:!text-white dark:hover:bg-secondary-600 shadow-lg shadow-primary/20 dark:shadow-secondary/20",
89
- ].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: sectionLabelClass, children: labels.sectionTitle }), _jsxs(Ul, { className: "space-y-0.5", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: navItemClass, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: navItemClass, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: navItemClass, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: navItemClass, children: labels.settings }) }))] })] })), isAuthenticated && user?.stats && (_jsxs(Div, { className: "grid grid-cols-2 gap-2", children: [user.stats.totalOrders != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.totalOrders }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Orders" })] })), user.stats.reviewsCount != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.reviewsCount }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Reviews" })] })), user.stats.auctionsWon != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.auctionsWon }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Auctions Won" })] })), user.stats.itemsSold != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.itemsSold }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Items Sold" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref || sellerHref) && (_jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: sectionLabelClass, children: labels.dashboardSectionTitle }), _jsxs(Ul, { className: "space-y-0.5", children: [adminHref && role === "admin" && (_jsx(Li, { children: _jsx(TextLink, { href: adminHref, variant: "none", className: navItemClass, children: labels.adminDashboard }) })), sellerHref && isAdminOrSeller && (_jsx(Li, { children: _jsx(TextLink, { href: sellerHref, variant: "none", className: navItemClass, children: labels.sellerDashboard }) }))] })] })), normalizedSections.map((section, sectionIndex) => (_jsx(CollapsibleSidebarSection, { section: section, navItemClass: navItemClass }, `sidebar-section-${sectionIndex}`))), (sidebarLocaleSlot ||
92
+ ].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: sectionLabelClass, children: labels.sectionTitle }), _jsxs(Ul, { className: "space-y-0.5", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: navItemClass, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: navItemClass, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: navItemClass, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: navItemClass, children: labels.settings }) }))] })] })), isAuthenticated && user?.stats && (_jsxs(Div, { className: "grid grid-cols-2 gap-2", children: [user.stats.totalOrders != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.totalOrders }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Orders" })] })), user.stats.reviewsCount != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.reviewsCount }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Reviews" })] })), user.stats.auctionsWon != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.auctionsWon }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Auctions Won" })] })), user.stats.itemsSold != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.itemsSold }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Items Sold" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref || resolvedStoreHref) && (_jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: sectionLabelClass, children: labels.dashboardSectionTitle }), _jsxs(Ul, { className: "space-y-0.5", children: [adminHref && role === "admin" && (_jsx(Li, { children: _jsx(TextLink, { href: adminHref, variant: "none", className: navItemClass, children: labels.adminDashboard }) })), resolvedStoreHref && isAdminOrSeller && (_jsx(Li, { children: _jsx(TextLink, { href: resolvedStoreHref, variant: "none", className: navItemClass, children: labels.storeDashboard }) }))] })] })), normalizedSections.map((section, sectionIndex) => (_jsx(CollapsibleSidebarSection, { section: section, navItemClass: navItemClass }, `sidebar-section-${sectionIndex}`))), (sidebarLocaleSlot ||
90
93
  showThemeToggleInSidebar ||
91
94
  (isAuthenticated && onLogout)) && (_jsxs(Div, { className: "border-t border-zinc-200 pt-4 space-y-3 dark:border-slate-800", children: [sidebarLocaleSlot, showThemeToggleInSidebar && (_jsxs("button", { type: "button", onClick: toggleTheme, className: "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm text-zinc-700 transition-colors hover:bg-primary-50 hover:text-primary-800 dark:text-zinc-300 dark:hover:bg-slate-800 dark:hover:text-secondary-300", children: [_jsx("span", { "aria-hidden": "true", children: theme === "dark" ? "☀️" : "🌙" }), theme === "dark" ? "Light mode" : "Dark mode"] })), isAuthenticated && onLogout && (_jsx("button", { type: "button", onClick: () => {
92
95
  onLogout();
@@ -102,6 +105,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
102
105
  userWishlistHref,
103
106
  userSettingsHref,
104
107
  adminHref,
108
+ storeHref,
105
109
  sellerHref,
106
110
  sidebarLocaleSlot,
107
111
  showThemeToggleInSidebar,
@@ -5,6 +5,7 @@ export interface MarketplacePreorderCardLabels {
5
5
  preOrderBadge?: string;
6
6
  featuredBadge?: string;
7
7
  reserveNow?: string;
8
+ addToCart?: string;
8
9
  addToWishlist?: string;
9
10
  removeFromWishlist?: string;
10
11
  }
@@ -19,7 +20,8 @@ export interface MarketplacePreorderCardProps {
19
20
  href?: string;
20
21
  hrefBuilder?: (product: MarketplacePreorderCardData) => string;
21
22
  onNavigate?: (href: string) => void;
23
+ onAddToCart?: (product: MarketplacePreorderCardData) => void;
22
24
  wishlistActions?: WishlistToggleActions;
23
25
  labels?: MarketplacePreorderCardLabels;
24
26
  }
25
- export declare function MarketplacePreorderCard({ product, className, variant, selectable, isSelected, onSelect, inWishlist: initialInWishlist, href, hrefBuilder, onNavigate, wishlistActions, labels, }: MarketplacePreorderCardProps): import("react/jsx-runtime").JSX.Element;
27
+ export declare function MarketplacePreorderCard({ product, className, variant, selectable, isSelected, onSelect, inWishlist: initialInWishlist, href, hrefBuilder, onNavigate, onAddToCart, wishlistActions, labels, }: MarketplacePreorderCardProps): import("react/jsx-runtime").JSX.Element;
@@ -15,6 +15,7 @@ const DEFAULT_LABELS = {
15
15
  preOrderBadge: "Pre-order",
16
16
  featuredBadge: "Featured",
17
17
  reserveNow: "Reserve now",
18
+ addToCart: "Add to Cart",
18
19
  addToWishlist: "Add to wishlist",
19
20
  removeFromWishlist: "Remove from wishlist",
20
21
  };
@@ -25,7 +26,7 @@ function resolveHref(product, href, hrefBuilder) {
25
26
  return hrefBuilder(product);
26
27
  return ROUTES.PUBLIC.PRE_ORDER_DETAIL(product.id);
27
28
  }
28
- export function MarketplacePreorderCard({ product, className = "", variant = "grid", selectable = false, isSelected = false, onSelect, inWishlist: initialInWishlist = false, href, hrefBuilder, onNavigate, wishlistActions, labels, }) {
29
+ export function MarketplacePreorderCard({ product, className = "", variant = "grid", selectable = false, isSelected = false, onSelect, inWishlist: initialInWishlist = false, href, hrefBuilder, onNavigate, onAddToCart, wishlistActions, labels, }) {
29
30
  const router = useRouter();
30
31
  const mergedLabels = { ...DEFAULT_LABELS, ...labels };
31
32
  const detailHref = resolveHref(product, href, hrefBuilder);
@@ -51,7 +52,10 @@ export function MarketplacePreorderCard({ product, className = "", variant = "gr
51
52
  return (_jsxs(BaseListingCard, { isSelected: isSelected, variant: variant, className: className, children: [_jsxs(BaseListingCard.Hero, { aspect: "square", variant: variant, children: [_jsx(TextLink, { href: detailHref, className: "absolute inset-0 block", children: _jsx(MediaImage, { src: product.mainImage, alt: product.title, size: "card", fallback: "Product image" }) }), _jsxs(Div, { className: "absolute right-2 top-2 flex flex-col items-end gap-1", children: [_jsx(Span, { className: "inline-flex items-center rounded-full bg-cobalt px-2 py-0.5 text-xs font-medium text-white", children: mergedLabels.preOrderBadge }), product.featured && (_jsx(Span, { className: "inline-flex items-center rounded-full bg-amber-500 px-2 py-0.5 text-xs font-medium text-white", children: mergedLabels.featuredBadge }))] }), selectable && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (event) => {
52
53
  event.stopPropagation();
53
54
  onSelect?.(product.id, !isSelected);
54
- } }))] }), _jsxs(BaseListingCard.Info, { variant: variant, children: [_jsx(TextLink, { href: detailHref, children: _jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-medium text-zinc-900`, children: product.title }) }), product.description ? (_jsx(RichText, { html: normalizeRichTextHtml(product.description), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `${THEME_CONSTANTS.utilities.textClamp2} text-xs text-zinc-500` })) : null, _jsxs(Row, { justify: "between", className: "mt-1 gap-2", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-900", children: formatCurrency(product.price, getDefaultCurrency()) }), shipDate && _jsx(PreorderBadge, { shipDate: shipDate })] }), _jsxs(Row, { justify: "between", className: "mt-2 gap-2", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", className: "flex-1 text-xs", onClick: handleNavigate, children: mergedLabels.reserveNow }), wishlistActions ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: `text-base ${inWishlist ? "text-primary" : "text-zinc-500"}`, onClick: handleWishlist, "aria-label": inWishlist
55
+ } }))] }), _jsxs(BaseListingCard.Info, { variant: variant, children: [_jsx(TextLink, { href: detailHref, children: _jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-medium text-zinc-900`, children: product.title }) }), product.description ? (_jsx(RichText, { html: normalizeRichTextHtml(product.description), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `${THEME_CONSTANTS.utilities.textClamp2} text-xs text-zinc-500` })) : null, _jsxs(Row, { justify: "between", className: "mt-1 gap-2", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-900", children: formatCurrency(product.price, getDefaultCurrency()) }), shipDate && _jsx(PreorderBadge, { shipDate: shipDate })] }), _jsxs(Row, { justify: "between", className: "mt-2 gap-2", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", className: "flex-1 text-xs", onClick: handleNavigate, children: mergedLabels.reserveNow }), onAddToCart ? (_jsx(Button, { type: "button", variant: "outline", size: "sm", className: "flex-1 text-xs", onClick: (e) => {
56
+ e.stopPropagation();
57
+ onAddToCart(product);
58
+ }, children: mergedLabels.addToCart })) : null, wishlistActions ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: `text-base ${inWishlist ? "text-primary" : "text-zinc-500"}`, onClick: handleWishlist, "aria-label": inWishlist
55
59
  ? mergedLabels.removeFromWishlist
56
60
  : mergedLabels.addToWishlist, children: inWishlist ? "♥" : "♡" })) : null] })] })] }));
57
61
  }
@@ -3,8 +3,25 @@ import Link from "next/link";
3
3
  import { productRepository } from "../../../repositories";
4
4
  import { ROUTES } from "../../../next";
5
5
  import { getDefaultCurrency } from "../../../core/baseline-resolver";
6
- import { Button, Container, Div, Heading, Main, Row, Section, Span, Stack, Text, } from "../../../ui";
6
+ import { formatCurrency } from "../../../utils/number.formatter";
7
+ import { normalizeRichTextHtml } from "../../../utils/string.formatter";
8
+ import { safeDisplayName } from "../../../security";
9
+ import { Button, Container, Div, Heading, Main, RichText, Row, Section, Span, Stack, Text, } from "../../../ui";
7
10
  import { PreOrderDetailView } from "../../products/components/PreOrderDetailView";
11
+ import { BuyBar } from "../../products/components/BuyBar";
12
+ import { ProductGalleryClient } from "../../products/components/ProductGalleryClient";
13
+ import { ProductFeatureBadges } from "../../products/components/ProductFeatureBadges";
14
+ function toDescriptionHtml(raw) {
15
+ if (!raw)
16
+ return "";
17
+ const s = typeof raw === "string" ? raw : JSON.stringify(raw);
18
+ return normalizeRichTextHtml(s);
19
+ }
20
+ const PRODUCTION_STATUS_LABELS = {
21
+ upcoming: "Coming Soon",
22
+ in_production: "In Production",
23
+ ready_to_ship: "Ready to Ship",
24
+ };
8
25
  export async function PreOrderDetailPageView({ id }) {
9
26
  const product = await productRepository.findByIdOrSlug(id).catch(() => undefined);
10
27
  if (!product) {
@@ -12,16 +29,67 @@ export async function PreOrderDetailPageView({ id }) {
12
29
  }
13
30
  const p = product;
14
31
  const currency = p.currency || getDefaultCurrency();
15
- const price = typeof p.price === "number"
16
- ? new Intl.NumberFormat(undefined, { style: "currency", currency }).format(p.price)
32
+ const title = String(p.title ?? p.name ?? "Pre-Order Item");
33
+ const price = typeof p.price === "number" ? p.price : null;
34
+ const images = Array.isArray(p.images)
35
+ ? p.images
36
+ : typeof p.mainImage === "string"
37
+ ? [p.mainImage]
38
+ : [];
39
+ const reservedCount = typeof p.preOrderCurrentCount === "number"
40
+ ? p.preOrderCurrentCount
41
+ : typeof p.reservedCount === "number"
42
+ ? p.reservedCount
43
+ : 0;
44
+ const reserveTarget = typeof p.preOrderMaxQuantity === "number"
45
+ ? p.preOrderMaxQuantity
46
+ : typeof p.preOrderTarget === "number"
47
+ ? p.preOrderTarget
48
+ : typeof p.reserveTarget === "number"
49
+ ? p.reserveTarget
50
+ : 0;
51
+ const progressPct = reserveTarget > 0
52
+ ? Math.min(100, Math.round((reservedCount / reserveTarget) * 100))
53
+ : 0;
54
+ const depositPercent = typeof p.preOrderDepositPercent === "number" ? p.preOrderDepositPercent : null;
55
+ const depositAmount = typeof p.preOrderDepositAmount === "number"
56
+ ? p.preOrderDepositAmount
57
+ : price !== null && depositPercent !== null
58
+ ? Math.round((price * depositPercent) / 100)
59
+ : null;
60
+ const deliveryDate = p.preOrderDeliveryDate
61
+ ? new Date(p.preOrderDeliveryDate)
62
+ : null;
63
+ const productionStatus = typeof p.preOrderProductionStatus === "string"
64
+ ? p.preOrderProductionStatus
17
65
  : null;
18
- const images = Array.isArray(p.images) ? p.images : p.imageUrl ? [p.imageUrl] : [];
19
- const primaryImage = images[0];
20
- const reservedCount = typeof p.reservedCount === "number" ? p.reservedCount
21
- : typeof p.preOrderCurrentCount === "number" ? p.preOrderCurrentCount : 0;
22
- const reserveTarget = typeof p.reserveTarget === "number" ? p.reserveTarget
23
- : typeof p.preOrderTarget === "number" ? p.preOrderTarget
24
- : typeof p.preOrderMaxQuantity === "number" ? p.preOrderMaxQuantity : 0;
25
- const progressPct = reserveTarget > 0 ? Math.min(100, Math.round((reservedCount / reserveTarget) * 100)) : 0;
26
- return (_jsx(PreOrderDetailView, { renderGallery: () => primaryImage ? (_jsx(Div, { className: "overflow-hidden rounded-xl border border-zinc-100 dark:border-zinc-800", children: _jsx(Div, { role: "img", "aria-label": p.name ?? "Pre-order image", className: "aspect-square w-full bg-cover bg-center", style: { backgroundImage: `url(${primaryImage})` } }) })) : (_jsx(Div, { className: "overflow-hidden rounded-xl border border-zinc-100 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-900", children: _jsx(Div, { className: "flex aspect-square items-center justify-center text-zinc-300 dark:text-zinc-700", children: _jsxs("svg", { width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }), _jsx("path", { d: "M21 15l-5-5L5 21" })] }) }) })), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 1, className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: p.name ?? "Pre-Order Item" }), price && (_jsx(Text, { className: "text-2xl font-semibold text-primary-600 dark:text-primary-400", children: price })), p.description && (_jsx(Text, { className: "text-sm leading-relaxed text-zinc-600 dark:text-zinc-400", children: typeof p.description === "string" ? p.description : "" }))] })), renderBuyBar: () => (_jsx(Div, { className: "rounded-xl border border-zinc-100 bg-zinc-50 p-5 dark:border-zinc-800 dark:bg-zinc-900", children: _jsxs(Stack, { gap: "sm", children: [reserveTarget > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Span, { className: "text-xs text-zinc-500", children: [reservedCount, " of ", reserveTarget, " reserved"] }), _jsxs(Span, { className: "text-xs font-medium text-primary-600", children: [progressPct, "%"] })] }), _jsx(Div, { className: "h-2 w-full overflow-hidden rounded-full bg-zinc-200 dark:bg-zinc-700", children: _jsx(Div, { className: "h-full rounded-full bg-primary", style: { width: `${progressPct}%` } }) })] })), price && (_jsx(Text, { className: "text-xl font-bold text-zinc-900 dark:text-zinc-50", children: price })), _jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Reserve Now" })] }) })) }));
66
+ const isCancellable = p.preOrderCancellable === true;
67
+ const condition = typeof p.condition === "string" ? p.condition : null;
68
+ const featured = p.featured === true;
69
+ const shippingPaidBy = p.shippingPaidBy;
70
+ const freeShipping = shippingPaidBy === "seller";
71
+ const sellerName = typeof p.sellerName === "string" ? p.sellerName : null;
72
+ const safeSeller = sellerName ? safeDisplayName(sellerName, "") : null;
73
+ const features = Array.isArray(p.features) ? p.features : [];
74
+ const tags = Array.isArray(p.tags) ? p.tags : [];
75
+ const descriptionHtml = toDescriptionHtml(p.description);
76
+ return (_jsx(Main, { children: _jsxs(Container, { size: "xl", className: "px-4 py-6", children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "mb-4 flex items-center gap-1.5 text-xs text-zinc-500 dark:text-zinc-400 flex-wrap", children: [_jsx(Link, { href: String(ROUTES.HOME), className: "hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRE_ORDERS), className: "hover:text-primary-600 transition-colors", children: "Pre-Orders" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "text-zinc-700 dark:text-zinc-300 truncate max-w-[200px]", children: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, productName: title })), renderInfo: () => (_jsxs(Stack, { gap: "sm", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", className: "mb-1.5 flex-wrap", children: [_jsx(Span, { className: "inline-block rounded-full bg-indigo-100 dark:bg-indigo-900/30 px-2.5 py-0.5 text-xs font-semibold text-indigo-700 dark:text-indigo-300", children: "Pre-Order" }), productionStatus && (_jsx(Span, { className: "inline-block rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-0.5 text-xs font-medium text-zinc-600 dark:text-zinc-300", children: PRODUCTION_STATUS_LABELS[productionStatus] ?? productionStatus }))] }), _jsx(Heading, { level: 1, className: "text-xl font-bold leading-snug text-zinc-900 dark:text-zinc-50 sm:text-2xl", children: title })] }), price !== null && (_jsx(Span, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formatCurrency(price, currency) })), deliveryDate && (_jsxs(Row, { align: "center", gap: "xs", className: "text-sm text-zinc-600 dark:text-zinc-400", children: [_jsx(Span, { children: "\uD83D\uDCC5" }), _jsx(Span, { children: "Estimated delivery:" }), _jsx(Span, { className: "font-medium", children: deliveryDate.toLocaleDateString(undefined, { year: "numeric", month: "long" }) })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, returnable: isCancellable, labels: {
77
+ featured: "Featured",
78
+ fasterDelivery: "Faster Delivery",
79
+ ratedSeller: "Rated Seller",
80
+ condition: "Condition",
81
+ conditionNew: "New",
82
+ conditionUsed: "Used",
83
+ conditionBroken: "For Parts",
84
+ conditionRefurbished: "Refurbished",
85
+ returnable: "Cancellable",
86
+ freeShipping: "Free Shipping",
87
+ codAvailable: "Cash on Delivery",
88
+ wishlistCount: (n) => `${n} wishlisted`,
89
+ categoryProductCount: (n, cat) => `${n} in ${cat}`,
90
+ } }), features.length > 0 && (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 px-4 py-3", children: [_jsx(Text, { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "About this product" }), _jsx("ul", { className: "space-y-1.5", children: features.map((f, i) => (_jsxs("li", { className: "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i))) })] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-sm leading-relaxed text-zinc-600 dark:text-zinc-400 line-clamp-4" })), safeSeller && (_jsxs(Row, { align: "center", gap: "xs", className: "border-t border-zinc-100 dark:border-zinc-800 pt-3", children: [_jsx(Span, { className: "text-xs text-zinc-500", children: "Sold by" }), _jsx(Span, { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: safeSeller })] }))] })), renderBuyBar: () => (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5 space-y-4", children: [reserveTarget > 0 && (_jsxs(Div, { className: "space-y-2", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Text, { className: "text-xs text-zinc-500", children: [reservedCount, " of ", reserveTarget, " reserved"] }), _jsxs(Span, { className: "text-xs font-semibold text-primary-600 dark:text-primary-400", children: [progressPct, "%"] })] }), _jsx(Div, { className: "h-2 w-full overflow-hidden rounded-full bg-zinc-200 dark:bg-zinc-700", children: _jsx(Div, { className: "h-full rounded-full bg-primary transition-all", style: { width: `${progressPct}%` } }) })] })), price !== null && (_jsxs(Div, { children: [_jsx(Text, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formatCurrency(price, currency) }), depositAmount !== null && (_jsxs(Text, { className: "mt-0.5 text-xs text-zinc-500", children: ["Reserve with ", formatCurrency(depositAmount, currency), depositPercent !== null ? ` (${depositPercent}% deposit)` : ""] }))] })), _jsxs(Stack, { gap: "sm", children: [_jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Reserve Now" }), isCancellable && (_jsx(Text, { className: "text-center text-xs text-zinc-500 dark:text-zinc-400", children: "\u2713 Free cancellation before production" }))] }), tags.length > 0 && (_jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { className: "rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-1 text-xs text-zinc-600 dark:text-zinc-300", children: tag }, tag))) }) })), _jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "sm", className: "justify-center text-center", children: [
91
+ { icon: "🔒", label: "Secure\nPayment" },
92
+ { icon: "📅", label: "Guaranteed\nDelivery" },
93
+ { icon: "↩", label: "Free\nCancellation" },
94
+ ].map(({ icon, label }) => (_jsxs(Div, { className: "flex flex-col items-center gap-1 text-xs text-zinc-500 dark:text-zinc-400 min-w-[60px]", children: [_jsx(Span, { className: "text-base", children: icon }), _jsx(Span, { className: "whitespace-pre-line leading-tight", children: label })] }, label))) }) })] })) }), _jsxs(BuyBar, { children: [price !== null && (_jsx(Span, { className: "mr-auto text-sm font-bold text-zinc-900 dark:text-zinc-50", children: formatCurrency(price, currency) })), _jsx(Button, { variant: "primary", size: "sm", className: "flex-1", children: "Reserve Now" })] })] }) }));
27
95
  }
@@ -0,0 +1,8 @@
1
+ import type { FacetOption } from "../../filters/FilterFacetSection";
2
+ import type { UrlTable } from "../../filters/FilterPanel";
3
+ export interface PreOrderFiltersProps {
4
+ table: UrlTable;
5
+ storeOptions?: FacetOption[];
6
+ currencyPrefix?: string;
7
+ }
8
+ export declare function PreOrderFilters({ table, storeOptions, currencyPrefix, }: PreOrderFiltersProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslations } from "next-intl";
3
+ import { FilterFacetSection } from "../../filters/FilterFacetSection";
4
+ import { RangeFilter } from "../../filters/RangeFilter";
5
+ import { Div } from "../../../ui";
6
+ export function PreOrderFilters({ table, storeOptions = [], currencyPrefix = "", }) {
7
+ const t = useTranslations("filters");
8
+ const statusOptions = [
9
+ { value: "upcoming", label: "Upcoming" },
10
+ { value: "in_production", label: "In Production" },
11
+ { value: "ready_to_ship", label: "Ready to Ship" },
12
+ { value: "shipped", label: "Shipped" },
13
+ ];
14
+ const selectedStatuses = table.get("preOrderStatus")
15
+ ? table.get("preOrderStatus").split("|").filter(Boolean)
16
+ : [];
17
+ const selectedStores = table.get("storeId")
18
+ ? table.get("storeId").split("|").filter(Boolean)
19
+ : [];
20
+ return (_jsxs(Div, { children: [_jsx(FilterFacetSection, { title: t("preOrderStatus"), options: statusOptions, selected: selectedStatuses, onChange: (vals) => table.set("preOrderStatus", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(RangeFilter, { title: t("priceRange"), minValue: table.get("minPrice"), maxValue: table.get("maxPrice"), onMinChange: (v) => table.set("minPrice", v), onMaxChange: (v) => table.set("maxPrice", v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 500000, step: 100, minPlaceholder: t("minPrice"), maxPlaceholder: t("maxPrice"), defaultCollapsed: false }), storeOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("store"), options: storeOptions, selected: selectedStores, onChange: (vals) => table.set("storeId", vals[0] ?? ""), searchable: storeOptions.length > 6, selectionMode: "single", defaultCollapsed: true })), _jsx(RangeFilter, { title: "Delivery Date Range", type: "date", minValue: table.get("dateFrom"), maxValue: table.get("dateTo"), onMinChange: (v) => table.set("dateFrom", v), onMaxChange: (v) => table.set("dateTo", v), minPlaceholder: t("minDate"), maxPlaceholder: t("maxDate"), defaultCollapsed: true })] }));
21
+ }
@@ -1,4 +1,5 @@
1
1
  export interface PreOrdersIndexListingProps {
2
2
  initialData?: any;
3
+ categorySlug?: string;
3
4
  }
4
- export declare function PreOrdersIndexListing({ initialData }: PreOrdersIndexListingProps): import("react/jsx-runtime").JSX.Element;
5
+ export declare function PreOrdersIndexListing({ initialData, categorySlug }: PreOrdersIndexListingProps): import("react/jsx-runtime").JSX.Element;