@mohasinac/appkit 2.7.57 → 2.7.59

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 (245) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +7 -1
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -2
  3. package/dist/_internal/server/features/scams/og.js +1 -1
  4. package/dist/_internal/shared/actions/bulk-helpers.d.ts +3 -0
  5. package/dist/_internal/shared/actions/bulk-helpers.js +19 -0
  6. package/dist/_internal/shared/config/schema.d.ts +4 -4
  7. package/dist/client.d.ts +4 -1
  8. package/dist/client.js +3 -1
  9. package/dist/configs/next.js +11 -0
  10. package/dist/configs/tailwind.d.ts +16 -3
  11. package/dist/configs/tailwind.js +18 -5
  12. package/dist/features/account/components/AddressBook.js +1 -1
  13. package/dist/features/account/components/AddressesIndexListing.js +3 -4
  14. package/dist/features/account/components/BuyerAccountGuideView.js +7 -7
  15. package/dist/features/account/components/BuyerAuctionsGuideView.js +7 -7
  16. package/dist/features/account/components/BuyerOrdersGuideView.js +6 -6
  17. package/dist/features/account/components/BuyerShoppingGuideView.js +7 -7
  18. package/dist/features/account/components/UserOffersPanel.js +1 -1
  19. package/dist/features/account/components/UserSidebar.js +14 -10
  20. package/dist/features/account/components/UserSupportView.js +4 -4
  21. package/dist/features/admin/components/AdminAdsView.js +1 -1
  22. package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -10
  23. package/dist/features/admin/components/AdminAnalyticsGuideView.js +3 -3
  24. package/dist/features/admin/components/AdminBidsView.js +3 -9
  25. package/dist/features/admin/components/AdminBlogView.js +4 -9
  26. package/dist/features/admin/components/AdminBrandEditorView.js +14 -17
  27. package/dist/features/admin/components/AdminBrandsView.js +4 -9
  28. package/dist/features/admin/components/AdminBundlesView.js +40 -59
  29. package/dist/features/admin/components/AdminCarouselView.js +8 -21
  30. package/dist/features/admin/components/AdminCartsView.js +3 -4
  31. package/dist/features/admin/components/AdminCatalogGuideView.js +5 -5
  32. package/dist/features/admin/components/AdminCategoriesView.js +4 -9
  33. package/dist/features/admin/components/AdminCategoryEditorView.js +1 -5
  34. package/dist/features/admin/components/AdminContactEditorView.js +2 -2
  35. package/dist/features/admin/components/AdminContactView.js +7 -25
  36. package/dist/features/admin/components/AdminContentGuideView.js +6 -6
  37. package/dist/features/admin/components/AdminCouponEditorView.js +14 -17
  38. package/dist/features/admin/components/AdminCouponsView.js +8 -16
  39. package/dist/features/admin/components/AdminFaqEditorView.js +14 -17
  40. package/dist/features/admin/components/AdminFaqsView.js +18 -69
  41. package/dist/features/admin/components/AdminFeatureEditorView.js +7 -9
  42. package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -1
  43. package/dist/features/admin/components/AdminFeaturesView.js +1 -1
  44. package/dist/features/admin/components/AdminGuideHubView.js +1 -1
  45. package/dist/features/admin/components/AdminHistoryView.js +1 -1
  46. package/dist/features/admin/components/AdminListingScaffold.js +1 -1
  47. package/dist/features/admin/components/AdminNewsletterView.js +5 -15
  48. package/dist/features/admin/components/AdminNotificationsView.js +3 -9
  49. package/dist/features/admin/components/AdminOrdersGuideView.js +5 -5
  50. package/dist/features/admin/components/AdminOrdersView.js +3 -9
  51. package/dist/features/admin/components/AdminPayoutsView.js +3 -9
  52. package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -9
  53. package/dist/features/admin/components/AdminProductEditorView.js +1 -5
  54. package/dist/features/admin/components/AdminProductsView.js +5 -10
  55. package/dist/features/admin/components/AdminReturnRequestsView.js +1 -1
  56. package/dist/features/admin/components/AdminReviewsView.js +3 -9
  57. package/dist/features/admin/components/AdminScammerEditorView.js +3 -3
  58. package/dist/features/admin/components/AdminScammersView.js +6 -12
  59. package/dist/features/admin/components/AdminSectionsView.js +1 -1
  60. package/dist/features/admin/components/AdminSessionsView.js +3 -9
  61. package/dist/features/admin/components/AdminSiteSettingsView.js +1 -1
  62. package/dist/features/admin/components/AdminStoreAddressesView.js +1 -1
  63. package/dist/features/admin/components/AdminStoresGuideView.js +6 -6
  64. package/dist/features/admin/components/AdminStoresView.js +3 -9
  65. package/dist/features/admin/components/AdminSublistingCategoriesView.js +3 -7
  66. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +17 -20
  67. package/dist/features/admin/components/AdminSupportTicketDetailView.js +4 -4
  68. package/dist/features/admin/components/AdminSupportTicketsView.js +9 -15
  69. package/dist/features/admin/components/AdminTeamGuideView.js +4 -4
  70. package/dist/features/admin/components/AdminTeamView.js +6 -15
  71. package/dist/features/admin/components/AdminTrustGuideView.js +5 -5
  72. package/dist/features/admin/components/AdminUserEditorView.js +1 -1
  73. package/dist/features/admin/components/AdminUsersGuideView.js +5 -5
  74. package/dist/features/admin/components/AdminUsersView.js +3 -9
  75. package/dist/features/admin/components/AdminViewCards.js +1 -1
  76. package/dist/features/admin/components/AdminWishlistsView.js +1 -1
  77. package/dist/features/admin/components/DataTable.js +1 -1
  78. package/dist/features/admin/hooks/useAdminListing.d.ts +58 -0
  79. package/dist/features/admin/hooks/useAdminListing.js +71 -0
  80. package/dist/features/auctions/components/AuctionBidsTable.js +1 -1
  81. package/dist/features/auctions/components/AuctionDetailPageView.js +2 -2
  82. package/dist/features/auctions/components/MarketplaceAuctionCard.js +2 -2
  83. package/dist/features/auctions/components/PlaceBidFormClient.js +1 -1
  84. package/dist/features/auth/components/VerifyEmailView.js +1 -1
  85. package/dist/features/auth/schemas/firestore.d.ts +1 -0
  86. package/dist/features/blog/components/BlogIndexListing.js +3 -6
  87. package/dist/features/blog/components/BlogListView.js +1 -1
  88. package/dist/features/blog/components/BlogPostView.js +1 -1
  89. package/dist/features/cart/components/CartDrawer.js +1 -1
  90. package/dist/features/categories/components/CategoriesIndexListing.js +3 -6
  91. package/dist/features/categories/components/CategoryDetailPageView.js +1 -1
  92. package/dist/features/checkout/components/CheckoutStepper.js +1 -1
  93. package/dist/features/collections/components/CollectionCard.js +1 -1
  94. package/dist/features/copilot/components/AdminCopilotView.js +2 -2
  95. package/dist/features/events/components/AdminEventsView.js +4 -4
  96. package/dist/features/events/components/EventPollWidget.js +2 -2
  97. package/dist/features/events/components/EventStatusBadge.js +4 -4
  98. package/dist/features/events/components/EventsIndexListing.js +3 -4
  99. package/dist/features/events/components/SpinWheelView.js +1 -1
  100. package/dist/features/faq/components/FAQAccordion.js +2 -2
  101. package/dist/features/forms/Input.js +3 -3
  102. package/dist/features/forms/Select.js +2 -2
  103. package/dist/features/forms/Textarea.js +2 -2
  104. package/dist/features/forms/utils.d.ts +1 -1
  105. package/dist/features/forms/utils.js +1 -1
  106. package/dist/features/homepage/components/AnnouncementBar.d.ts +4 -1
  107. package/dist/features/homepage/components/AnnouncementBar.js +51 -4
  108. package/dist/features/homepage/components/CharacterHotspotForm.js +2 -2
  109. package/dist/features/homepage/components/MarketplaceHomepageView.d.ts +3 -1
  110. package/dist/features/homepage/components/MarketplaceHomepageView.js +2 -2
  111. package/dist/features/homepage/components/index.d.ts +1 -1
  112. package/dist/features/homepage/components/index.js +1 -1
  113. package/dist/features/homepage/lib/section-renderer.d.ts +2 -2
  114. package/dist/features/homepage/lib/section-renderer.js +2 -2
  115. package/dist/features/layout/BottomActions.js +2 -2
  116. package/dist/features/layout/ListingLayout.js +3 -3
  117. package/dist/features/layout/TitleBarLayout.js +1 -1
  118. package/dist/features/media/index.d.ts +2 -0
  119. package/dist/features/media/index.js +1 -0
  120. package/dist/features/media/modals/ImageCropModal.js +1 -1
  121. package/dist/features/media/modals/ImageEditor.d.ts +12 -0
  122. package/dist/features/media/modals/ImageEditor.js +73 -0
  123. package/dist/features/media/upload/ImageUpload.d.ts +5 -1
  124. package/dist/features/media/upload/ImageUpload.js +33 -8
  125. package/dist/features/media/upload/MediaUploadField.d.ts +5 -1
  126. package/dist/features/media/upload/MediaUploadField.js +26 -12
  127. package/dist/features/orders/components/OrdersList.js +7 -7
  128. package/dist/features/orders/components/RefundHistoryTable.js +2 -2
  129. package/dist/features/orders/components/RefundRequestView.js +3 -3
  130. package/dist/features/orders/repository/orders.repository.d.ts +0 -1
  131. package/dist/features/orders/repository/orders.repository.js +0 -1
  132. package/dist/features/orders/server.d.ts +1 -1
  133. package/dist/features/orders/server.js +1 -1
  134. package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
  135. package/dist/features/pre-orders/components/PreOrderBadge.js +4 -4
  136. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
  137. package/dist/features/pre-orders/components/PreorderCard.js +2 -2
  138. package/dist/features/products/components/CustomSectionsEditor.js +1 -1
  139. package/dist/features/products/components/GroupSettingsPanel.js +64 -48
  140. package/dist/features/products/components/MakeOfferButton.js +2 -2
  141. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
  142. package/dist/features/products/components/PrizeDrawItemsEditor.js +3 -3
  143. package/dist/features/products/components/PrizeRevealModal.js +3 -3
  144. package/dist/features/products/components/ProductDetailPageView.js +4 -4
  145. package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
  146. package/dist/features/products/components/ProductGrid.js +3 -3
  147. package/dist/features/products/repository/products.repository.d.ts +0 -2
  148. package/dist/features/products/repository/products.repository.js +0 -2
  149. package/dist/features/products/server.d.ts +1 -1
  150. package/dist/features/products/server.js +1 -1
  151. package/dist/features/promotions/components/CouponCard.js +7 -7
  152. package/dist/features/promotions/components/CouponsIndexListing.js +3 -2
  153. package/dist/features/promotions/components/PromotionsView.js +1 -1
  154. package/dist/features/reviews/components/ReviewDetailShell.js +2 -2
  155. package/dist/features/reviews/components/ReviewsIndexListing.js +3 -4
  156. package/dist/features/reviews/components/ReviewsList.js +1 -1
  157. package/dist/features/reviews/repository/reviews.repository.d.ts +0 -1
  158. package/dist/features/reviews/repository/reviews.repository.js +0 -1
  159. package/dist/features/reviews/server.d.ts +1 -1
  160. package/dist/features/reviews/server.js +1 -1
  161. package/dist/features/seller/components/SellerAddressesView.js +11 -21
  162. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +2 -2
  163. package/dist/features/seller/components/SellerAuctionsView.js +14 -23
  164. package/dist/features/seller/components/SellerBidsView.js +6 -6
  165. package/dist/features/seller/components/SellerBundlesView.js +13 -21
  166. package/dist/features/seller/components/SellerClassifiedView.js +14 -22
  167. package/dist/features/seller/components/SellerCouponEditorView.js +2 -2
  168. package/dist/features/seller/components/SellerCouponsView.js +14 -20
  169. package/dist/features/seller/components/SellerDigitalCodesView.js +13 -21
  170. package/dist/features/seller/components/SellerFeaturesView.js +1 -1
  171. package/dist/features/seller/components/SellerGroupedListingsView.js +6 -10
  172. package/dist/features/seller/components/SellerLiveView.js +15 -23
  173. package/dist/features/seller/components/SellerOffersPanel.js +2 -2
  174. package/dist/features/seller/components/SellerOffersView.js +4 -5
  175. package/dist/features/seller/components/SellerOrdersView.js +9 -23
  176. package/dist/features/seller/components/SellerPayoutMethodsView.js +13 -25
  177. package/dist/features/seller/components/SellerPayoutsView.js +4 -5
  178. package/dist/features/seller/components/SellerPreOrdersView.js +13 -22
  179. package/dist/features/seller/components/SellerPrizeDrawsView.js +13 -22
  180. package/dist/features/seller/components/SellerProductsCards.js +1 -1
  181. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +1 -1
  182. package/dist/features/seller/components/SellerProductsFilterDrawer.js +3 -6
  183. package/dist/features/seller/components/SellerProductsView.js +10 -23
  184. package/dist/features/seller/components/SellerReviewsView.js +1 -1
  185. package/dist/features/seller/components/SellerShippingConfigsView.js +14 -26
  186. package/dist/features/seller/components/SellerStoreCategoriesView.js +16 -35
  187. package/dist/features/seller/components/SellerTemplatesView.js +27 -43
  188. package/dist/features/seller/components/analytics/SellerTopProducts.js +1 -1
  189. package/dist/features/stores/components/StoreFinanceGuideView.js +2 -2
  190. package/dist/features/stores/components/StoreHeader.js +1 -1
  191. package/dist/features/stores/components/StoreListingsGuideView.js +3 -3
  192. package/dist/features/stores/components/StoreOrdersGuideView.js +4 -4
  193. package/dist/features/stores/components/StoreReviewsListing.js +3 -4
  194. package/dist/features/stores/components/StoreSettingsGuideView.js +2 -2
  195. package/dist/features/stores/components/StoresIndexListing.js +3 -4
  196. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +4 -4
  197. package/dist/features/wishlist/components/WishlistPage.js +1 -1
  198. package/dist/index.d.ts +4 -6
  199. package/dist/index.js +6 -12
  200. package/dist/react/contexts/SessionContext.d.ts +2 -0
  201. package/dist/react/contexts/SessionContext.js +2 -0
  202. package/dist/react/hooks/useEntityDelete.d.ts +15 -0
  203. package/dist/react/hooks/useEntityDelete.js +39 -0
  204. package/dist/react/index.d.ts +2 -0
  205. package/dist/react/index.js +2 -0
  206. package/dist/repositories/index.d.ts +3 -3
  207. package/dist/repositories/index.js +3 -3
  208. package/dist/styles.css +286 -3
  209. package/dist/tailwind-utilities.css +1 -1
  210. package/dist/tokens/motion.d.ts +162 -0
  211. package/dist/tokens/motion.js +89 -0
  212. package/dist/ui/DataTable.js +3 -2
  213. package/dist/ui/components/Accordion.js +2 -1
  214. package/dist/ui/components/Card.d.ts +5 -2
  215. package/dist/ui/components/Card.js +29 -11
  216. package/dist/ui/components/Div.d.ts +2 -16
  217. package/dist/ui/components/Div.js +2 -16
  218. package/dist/ui/components/Layout.d.ts +9 -19
  219. package/dist/ui/components/Layout.js +9 -7
  220. package/dist/ui/components/ListingFilterDrawer.d.ts +10 -0
  221. package/dist/ui/components/ListingFilterDrawer.js +8 -0
  222. package/dist/ui/components/Modal.js +7 -4
  223. package/dist/ui/components/Motion.d.ts +75 -0
  224. package/dist/ui/components/Motion.js +122 -0
  225. package/dist/ui/components/Semantic.d.ts +81 -14
  226. package/dist/ui/components/Semantic.js +75 -10
  227. package/dist/ui/components/Semantic.style.css +261 -2
  228. package/dist/ui/components/SideDrawer.d.ts +1 -1
  229. package/dist/ui/components/SideDrawer.js +8 -6
  230. package/dist/ui/components/SideModal.d.ts +1 -1
  231. package/dist/ui/components/SideModal.js +11 -9
  232. package/dist/ui/components/StackedViewShell.js +6 -1
  233. package/dist/ui/components/Toast.js +5 -2
  234. package/dist/ui/components/Typography.d.ts +44 -3
  235. package/dist/ui/components/Typography.js +15 -30
  236. package/dist/ui/components/Typography.style.css +24 -0
  237. package/dist/ui/components/VacationBanner.js +2 -1
  238. package/dist/ui/components/surface-tokens.d.ts +70 -0
  239. package/dist/ui/components/surface-tokens.js +68 -0
  240. package/dist/ui/forms/FormShell.d.ts +6 -1
  241. package/dist/ui/forms/FormShell.js +32 -4
  242. package/dist/ui/index.d.ts +11 -2
  243. package/dist/ui/index.js +5 -2
  244. package/dist/validation/schemas.d.ts +28 -28
  245. package/package.json +3 -1
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
- import { Button, Div, FormField, Heading, Modal, Row, Select, SideDrawer, Stack, Text, Tabs, TabsContent, TabsList, TabsTrigger, DynamicSelect, useToast, } from "../../../ui";
4
+ import { Button, ConfirmDeleteModal, Div, FormField, Heading, Modal, Row, Select, SideDrawer, Stack, Text, Tabs, TabsContent, TabsList, TabsTrigger, DynamicSelect, useToast, } from "../../../ui";
5
5
  import { apiClient } from "../../../http";
6
6
  const CONDITION_OPTIONS = [
7
7
  { value: "new", label: "New" },
@@ -24,6 +24,7 @@ export function GroupSettingsPanel({ productId, productSlug, groupId, isGroupPar
24
24
  });
25
25
  const [linkTarget, setLinkTarget] = useState(null);
26
26
  const [children, setChildren] = useState(null);
27
+ const [confirmAction, setConfirmAction] = useState(null);
27
28
  if (isAuction)
28
29
  return null;
29
30
  const groupEndpoint = `${storeProductsEndpoint}/${productId}/group`;
@@ -71,38 +72,48 @@ export function GroupSettingsPanel({ productId, productSlug, groupId, isGroupPar
71
72
  setLoading(false);
72
73
  }
73
74
  }
74
- async function dissolveGroup() {
75
- if (!confirm("Dissolve this group? All members will be unlinked. This cannot be undone."))
76
- return;
77
- setLoading(true);
78
- try {
79
- await apiClient.delete(groupEndpoint);
80
- showToast("Group dissolved.", "success");
81
- onGroupChanged();
82
- }
83
- catch (e) {
84
- showToast(e?.message ?? "Failed to dissolve group.", "error");
85
- }
86
- finally {
87
- setLoading(false);
88
- }
75
+ function requestDissolveGroup() {
76
+ setConfirmAction({
77
+ title: "Dissolve Group",
78
+ message: "Dissolve this group? All members will be unlinked. This cannot be undone.",
79
+ onConfirm: async () => {
80
+ setConfirmAction(null);
81
+ setLoading(true);
82
+ try {
83
+ await apiClient.delete(groupEndpoint);
84
+ showToast("Group dissolved.", "success");
85
+ onGroupChanged();
86
+ }
87
+ catch (e) {
88
+ showToast(e?.message ?? "Failed to dissolve group.", "error");
89
+ }
90
+ finally {
91
+ setLoading(false);
92
+ }
93
+ },
94
+ });
89
95
  }
90
- async function unlinkChild(childId) {
91
- if (!confirm("Remove this listing from the group?"))
92
- return;
93
- setLoading(true);
94
- try {
95
- await apiClient.delete(`${childrenEndpoint}/${childId}`);
96
- showToast("Listing removed from group.", "success");
97
- setChildren((prev) => prev?.filter((c) => c.id !== childId) ?? null);
98
- onGroupChanged();
99
- }
100
- catch (e) {
101
- showToast(e?.message ?? "Failed to unlink.", "error");
102
- }
103
- finally {
104
- setLoading(false);
105
- }
96
+ function requestUnlinkChild(childId) {
97
+ setConfirmAction({
98
+ title: "Remove from Group",
99
+ message: "Remove this listing from the group?",
100
+ onConfirm: async () => {
101
+ setConfirmAction(null);
102
+ setLoading(true);
103
+ try {
104
+ await apiClient.delete(`${childrenEndpoint}/${childId}`);
105
+ showToast("Listing removed from group.", "success");
106
+ setChildren((prev) => prev?.filter((c) => c.id !== childId) ?? null);
107
+ onGroupChanged();
108
+ }
109
+ catch (e) {
110
+ showToast(e?.message ?? "Failed to unlink.", "error");
111
+ }
112
+ finally {
113
+ setLoading(false);
114
+ }
115
+ },
116
+ });
106
117
  }
107
118
  async function addCreateChild() {
108
119
  if (!createForm.title || !createForm.price) {
@@ -152,21 +163,26 @@ export function GroupSettingsPanel({ productId, productSlug, groupId, isGroupPar
152
163
  setLoading(false);
153
164
  }
154
165
  }
155
- async function leaveGroup() {
156
- if (!confirm("Leave this group? This listing will become standalone."))
157
- return;
158
- setLoading(true);
159
- try {
160
- await apiClient.delete(`${storeProductsEndpoint}/${productId}/group/leave`);
161
- showToast("Left the group.", "success");
162
- onGroupChanged();
163
- }
164
- catch (e) {
165
- showToast(e?.message ?? "Failed to leave group.", "error");
166
- }
167
- finally {
168
- setLoading(false);
169
- }
166
+ function requestLeaveGroup() {
167
+ setConfirmAction({
168
+ title: "Leave Group",
169
+ message: "Leave this group? This listing will become standalone.",
170
+ onConfirm: async () => {
171
+ setConfirmAction(null);
172
+ setLoading(true);
173
+ try {
174
+ await apiClient.delete(`${storeProductsEndpoint}/${productId}/group/leave`);
175
+ showToast("Left the group.", "success");
176
+ onGroupChanged();
177
+ }
178
+ catch (e) {
179
+ showToast(e?.message ?? "Failed to leave group.", "error");
180
+ }
181
+ finally {
182
+ setLoading(false);
183
+ }
184
+ },
185
+ });
170
186
  }
171
187
  async function loadLinkOptions(query, page) {
172
188
  const params = new URLSearchParams({ page: String(page), pageSize: "25" });
@@ -185,7 +201,7 @@ export function GroupSettingsPanel({ productId, productSlug, groupId, isGroupPar
185
201
  loadChildren();
186
202
  }, className: "w-full flex items-center justify-between py-2 text-left group", "aria-expanded": open, children: [_jsx(Heading, { level: 3, className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Group Settings" }), _jsx("span", { className: "text-xs text-zinc-400 dark:text-zinc-400 group-hover:text-zinc-600 dark:group-hover:text-zinc-300 transition-colors", children: open ? "▲" : "▼" })] }), open && (_jsxs(Stack, { gap: "sm", className: "mt-3 p-4 rounded-lg border border-zinc-200 dark:border-zinc-700 bg-zinc-50/50 dark:bg-zinc-800/30", children: [!groupId && !groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Group related listings together \u2014 e.g. a set, bundle, or multi-part item. Parts can be sold individually but shown together." }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: startGroup, isLoading: loading, children: "Start a group" })] })), isGroupParent && groupId && (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { align: "start", gap: "sm", className: "flex-wrap", children: [_jsx("div", { className: "flex-1 min-w-[200px]", children: _jsx(FormField, { name: "groupTitle", label: "Group title", type: "text", value: editTitle, onChange: setEditTitle, placeholder: "e.g. Human Toy Complete Set" }) }), _jsx("div", { className: "pt-6", children: _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: saveTitle, isLoading: loading, children: "Save title" }) })] }), _jsxs(Div, { children: [_jsxs(Text, { className: "text-xs font-semibold text-zinc-600 dark:text-zinc-400 mb-2", children: ["Members (", childSlugsCount + 1, " \u2014 including this listing)"] }), loading && !children ? (_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: "Loading\u2026" })) : (_jsxs("div", { className: "divide-y divide-zinc-100 dark:divide-zinc-800", children: [_jsxs(Row, { align: "center", gap: "sm", className: "py-2", children: [_jsx("span", { className: "rounded bg-[var(--appkit-color-primary,#6366f1)]/10 text-[var(--appkit-color-primary,#6366f1)] text-[10px] font-semibold px-1.5 py-0.5", children: "Parent" }), _jsx(Text, { className: "text-sm text-zinc-800 dark:text-zinc-200 flex-1", children: productSlug })] }), (children ?? []).filter((c) => c.id !== productId).map((child) => (_jsxs(Row, { align: "center", gap: "sm", className: "py-2", children: [child.images?.[0] ? (
187
203
  // eslint-disable-next-line @next/next/no-img-element
188
- _jsx("img", { src: child.images[0], alt: child.title, className: "w-8 h-8 rounded-full object-cover border border-zinc-200 dark:border-zinc-700" })) : (_jsx("div", { className: "w-8 h-8 rounded-full bg-zinc-100 dark:bg-zinc-800" })), _jsx(Text, { className: "text-sm text-zinc-800 dark:text-zinc-200 flex-1 truncate", children: child.title }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => unlinkChild(child.id), isLoading: loading, className: "text-red-500 hover:text-red-600 text-xs", children: "Unlink" })] }, child.id)))] }))] }), _jsxs(Row, { gap: "sm", className: "flex-wrap", children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setShowAddModal(true), children: "Add child listing" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: dissolveGroup, isLoading: loading, className: "text-red-500 hover:text-red-600", children: "Dissolve group" })] })] })), !isGroupParent && groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { className: "text-sm text-zinc-600 dark:text-zinc-400", children: ["Part of:", " ", _jsx("span", { className: "font-medium text-zinc-800 dark:text-zinc-200", children: groupTitle ?? groupParentSlug })] }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["Parent listing: ", _jsx("code", { className: "font-mono", children: groupParentSlug })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: leaveGroup, isLoading: loading, className: "text-red-500 hover:text-red-600 w-fit", children: "Leave group" })] }))] })), (groupChildSlugs?.length ?? 0) >= 4 ? (_jsx(SideDrawer, { isOpen: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, linkTarget: linkTarget, setLinkTarget: setLinkTarget, loadLinkOptions: loadLinkOptions, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })) : (_jsx(Modal, { open: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", size: "lg", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, linkTarget: linkTarget, setLinkTarget: setLinkTarget, loadLinkOptions: loadLinkOptions, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) }))] }));
204
+ _jsx("img", { src: child.images[0], alt: child.title, className: "w-8 h-8 rounded-full object-cover border border-zinc-200 dark:border-zinc-700" })) : (_jsx("div", { className: "w-8 h-8 rounded-full bg-zinc-100 dark:bg-zinc-800" })), _jsx(Text, { className: "text-sm text-zinc-800 dark:text-zinc-200 flex-1 truncate", children: child.title }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => requestUnlinkChild(child.id), isLoading: loading, className: "text-red-500 hover:text-red-600 text-xs", children: "Unlink" })] }, child.id)))] }))] }), _jsxs(Row, { gap: "sm", className: "flex-wrap", children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setShowAddModal(true), children: "Add child listing" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestDissolveGroup, isLoading: loading, className: "text-red-500 hover:text-red-600", children: "Dissolve group" })] })] })), !isGroupParent && groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { className: "text-sm text-zinc-600 dark:text-zinc-400", children: ["Part of:", " ", _jsx("span", { className: "font-medium text-zinc-800 dark:text-zinc-200", children: groupTitle ?? groupParentSlug })] }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["Parent listing: ", _jsx("code", { className: "font-mono", children: groupParentSlug })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestLeaveGroup, isLoading: loading, className: "text-red-500 hover:text-red-600 w-fit", children: "Leave group" })] }))] })), (groupChildSlugs?.length ?? 0) >= 4 ? (_jsx(SideDrawer, { isOpen: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, linkTarget: linkTarget, setLinkTarget: setLinkTarget, loadLinkOptions: loadLinkOptions, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })) : (_jsx(Modal, { open: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", size: "lg", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, linkTarget: linkTarget, setLinkTarget: setLinkTarget, loadLinkOptions: loadLinkOptions, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })), confirmAction && (_jsx(ConfirmDeleteModal, { isOpen: true, title: confirmAction.title, message: confirmAction.message, onConfirm: confirmAction.onConfirm, onClose: () => setConfirmAction(null), isDeleting: loading }))] }));
189
205
  }
190
206
  function AddChildContent({ addTab, setAddTab, createForm, setCreateForm, productSlug, linkTarget, setLinkTarget, loadLinkOptions, onAddCreate, onAddLink, loading, }) {
191
207
  return (_jsx(Stack, { gap: "md", children: _jsxs(Tabs, { value: addTab, onChange: (v) => setAddTab(v), children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "create", children: "Create new child" }), _jsx(TabsTrigger, { value: "link", children: "Link existing" })] }), _jsx(TabsContent, { value: "create", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(FormField, { name: "childTitle", label: "Title *", type: "text", value: createForm.title, onChange: (v) => setCreateForm({ ...createForm, title: v }), placeholder: `${productSlug}-part` }), _jsx(FormField, { name: "childPrice", label: "Price (\u20B9) *", type: "number", value: createForm.price, onChange: (v) => setCreateForm({ ...createForm, price: v }), placeholder: "0" }), _jsx(Select, { label: "Condition", value: createForm.condition, onValueChange: (v) => setCreateForm({ ...createForm, condition: v }), options: CONDITION_OPTIONS }), _jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400/80", children: "Other fields (category, brand, shipping, return policy) are inherited from this parent listing. Need more control? Edit the full listing after saving." }), _jsx(Button, { type: "button", onClick: onAddCreate, isLoading: loading, disabled: !createForm.title || !createForm.price, children: "Create and link child" })] }) }), _jsx(TabsContent, { value: "link", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Search your existing products or pre-orders. Auctions cannot be linked." }), _jsx(DynamicSelect, { value: linkTarget, onChange: (v) => setLinkTarget(v), loadOptions: loadLinkOptions, placeholder: "Search listings\u2026", searchPlaceholder: "Type title or slug\u2026", noResultsText: "No matching listings found", ariaLabel: "Listing to link" }), _jsx(Button, { type: "button", onClick: onAddLink, isLoading: loading, disabled: !linkTarget, children: "Link to group" })] }) })] }) }));
@@ -67,11 +67,11 @@ export function MakeOfferButton({ productId, listedPrice, currency, minOfferPerc
67
67
  });
68
68
  }
69
69
  if (state === "success") {
70
- return (_jsxs(Div, { className: `rounded-xl border border-emerald-200 dark:border-emerald-800 bg-emerald-50 dark:bg-emerald-900/20 p-4 text-center space-y-1 ${className}`, children: [_jsx(Span, { className: "text-lg", children: "\uD83C\uDF89" }), _jsx(Text, { className: "text-sm font-medium text-emerald-800 dark:text-emerald-300", children: "Offer sent!" }), _jsx(Text, { className: "text-xs text-emerald-700 dark:text-emerald-400", children: "The seller will review your offer and respond shortly." })] }));
70
+ return (_jsxs(Div, { className: `rounded-xl border border-emerald-200 dark:border-emerald-800 bg-success-surface p-4 text-center space-y-1 ${className}`, children: [_jsx(Span, { className: "text-lg", children: "\uD83C\uDF89" }), _jsx(Text, { className: "text-sm font-medium text-success", children: "Offer sent!" }), _jsx(Text, { className: "text-xs text-success", children: "The seller will review your offer and respond shortly." })] }));
71
71
  }
72
72
  if (state === "pending") {
73
73
  return (_jsxs(Div, { className: `rounded-xl border border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-900/20 p-4 text-center space-y-1 ${className}`, children: [_jsx(Span, { className: "text-lg", children: "\u23F3" }), _jsx(Text, { className: "text-sm font-medium text-amber-800 dark:text-amber-300", children: "Offer Pending" }), _jsx(Text, { className: "text-xs text-amber-700 dark:text-amber-400", children: "You already have an offer on this item. Check My Offers for updates." })] }));
74
74
  }
75
75
  const modalOpen = state === "confirm" || state === "loading" || state === "error";
76
- return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to make an offer. Please log in or create an account to continue." }), _jsx(Button, { variant: "ghost", size: "md", className: `w-full border border-zinc-300 dark:border-zinc-600 ${className}`, onClick: handleOpenConfirm, children: "Make Offer" }), _jsx(Modal, { isOpen: modalOpen, onClose: handleCancel, size: "md", title: "Make an offer", actions: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: handleCancel, disabled: isPending, children: "Cancel" }), _jsx(Button, { variant: "primary", size: "sm", onClick: handleSubmit, disabled: isPending || offerAmount < minOffer, children: isPending ? "Sending…" : `Send offer of ${fmt(offerAmount)}` })] }), children: _jsxs(Stack, { gap: "md", children: [_jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["Listed at ", fmt(listedPrice), " \u00B7 Minimum offer: ", fmt(minOffer)] }), _jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-400", children: "Your offer amount" }), _jsx(Input, { type: "number", value: String(offerAmount), onChange: (e) => handleAmountChange(e.target.value), min: minOffer, max: listedPrice - 1, step: 1, "aria-label": "Offer amount" }), _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: ["Must be between ", fmt(minOffer), " and ", fmt(listedPrice - 1)] })] }), _jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-400", children: "Note to seller (optional)" }), _jsx(Input, { type: "text", value: buyerNote, onChange: (e) => setBuyerNote(e.target.value), placeholder: "E.g. Bundle deal, long-time fan\u2026", maxLength: 300, "aria-label": "Note to seller" })] }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "The seller will accept, decline, or suggest a counter price." }), state === "error" && (_jsx(Text, { className: "text-xs text-red-600 dark:text-red-400", children: errorMsg }))] }) })] }));
76
+ return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to make an offer. Please log in or create an account to continue." }), _jsx(Button, { variant: "ghost", size: "md", className: `w-full border border-zinc-300 dark:border-zinc-600 ${className}`, onClick: handleOpenConfirm, children: "Make Offer" }), _jsx(Modal, { isOpen: modalOpen, onClose: handleCancel, size: "md", title: "Make an offer", actions: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: handleCancel, disabled: isPending, children: "Cancel" }), _jsx(Button, { variant: "primary", size: "sm", onClick: handleSubmit, disabled: isPending || offerAmount < minOffer, children: isPending ? "Sending…" : `Send offer of ${fmt(offerAmount)}` })] }), children: _jsxs(Stack, { gap: "md", children: [_jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["Listed at ", fmt(listedPrice), " \u00B7 Minimum offer: ", fmt(minOffer)] }), _jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-400", children: "Your offer amount" }), _jsx(Input, { type: "number", value: String(offerAmount), onChange: (e) => handleAmountChange(e.target.value), min: minOffer, max: listedPrice - 1, step: 1, "aria-label": "Offer amount" }), _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: ["Must be between ", fmt(minOffer), " and ", fmt(listedPrice - 1)] })] }), _jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-400", children: "Note to seller (optional)" }), _jsx(Input, { type: "text", value: buyerNote, onChange: (e) => setBuyerNote(e.target.value), placeholder: "E.g. Bundle deal, long-time fan\u2026", maxLength: 300, "aria-label": "Note to seller" })] }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "The seller will accept, decline, or suggest a counter price." }), state === "error" && (_jsx(Text, { className: "text-xs text-error", children: errorMsg }))] }) })] }));
77
77
  }
@@ -33,12 +33,12 @@ function statusLabel(s) {
33
33
  function statusClass(s) {
34
34
  switch (s) {
35
35
  case "open":
36
- return "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300";
36
+ return "bg-success-surface text-success";
37
37
  case "closed":
38
38
  return "bg-zinc-200 text-zinc-700 dark:bg-zinc-700 dark:text-zinc-200";
39
39
  case "pending":
40
40
  default:
41
- return "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300";
41
+ return "bg-warning-surface text-warning";
42
42
  }
43
43
  }
44
44
  function stripIsWon(items) {
@@ -11,7 +11,7 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
11
11
  * Min 3 items, max 16 (mirrors PrizeDrawItem schema constraints).
12
12
  */
13
13
  import { useCallback } from "react";
14
- import { Button, Div, FormField, Heading, Row, Stack, Text } from "../../../ui";
14
+ import { Button, Div, FormField, Heading, Row, Span, Stack, Text } from "../../../ui";
15
15
  import { ImageUpload } from "../../media";
16
16
  const MIN_ITEMS = 3;
17
17
  const MAX_ITEMS = 16;
@@ -84,7 +84,7 @@ export function PrizeDrawItemsEditor({ items, onChange, onUploadImage, onUploadV
84
84
  const nextImgs = (cur.images ?? []).filter((_, i) => i !== slot);
85
85
  update(index, { images: nextImgs });
86
86
  };
87
- return (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Heading, { level: 3, children: ["Prize Items (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "secondary", onClick: addItem, disabled: anyWon || items.length >= MAX_ITEMS, children: ["+ Add prize (", items.length, "/", MAX_ITEMS, ")"] })] }), _jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: ["Add between ", MIN_ITEMS, " and ", MAX_ITEMS, " prizes. Each entry will reveal exactly one of these. Items marked won during a prior reveal cannot be edited or removed."] }), anyWon ? (_jsxs(Div, { className: "rounded border border-red-400/40 bg-red-50 px-3 py-2 text-sm text-red-900 dark:bg-red-900/30 dark:text-red-100", children: [_jsx("strong", { children: "Draw locked." }), " At least one prize has been revealed \u2014 this listing can no longer be edited. To run a similar draw, clone it into a new prize-draw listing."] })) : warning ? (_jsx(Div, { className: "rounded border border-yellow-400/40 bg-yellow-50 px-3 py-2 text-sm text-yellow-900 dark:bg-yellow-900/30 dark:text-yellow-100", children: warning })) : null, _jsx(Stack, { gap: "md", children: items.map((it, index) => {
87
+ return (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Heading, { level: 3, children: ["Prize Items (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "secondary", onClick: addItem, disabled: anyWon || items.length >= MAX_ITEMS, children: ["+ Add prize (", items.length, "/", MAX_ITEMS, ")"] })] }), _jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: ["Add between ", MIN_ITEMS, " and ", MAX_ITEMS, " prizes. Each entry will reveal exactly one of these. Items marked won during a prior reveal cannot be edited or removed."] }), anyWon ? (_jsxs(Div, { className: "rounded border border-red-400/40 bg-error-surface px-3 py-2 text-sm text-error", children: [_jsx(Span, { weight: "bold", children: "Draw locked." }), " At least one prize has been revealed \u2014 this listing can no longer be edited. To run a similar draw, clone it into a new prize-draw listing."] })) : warning ? (_jsx(Div, { className: "rounded border border-yellow-400/40 bg-yellow-50 px-3 py-2 text-sm text-yellow-900 dark:bg-yellow-900/30 dark:text-yellow-100", children: warning })) : null, _jsx(Stack, { gap: "md", children: items.map((it, index) => {
88
88
  const locked = it.isWon || anyWon;
89
89
  return (_jsxs(Div, { className: `relative rounded-lg border border-[var(--appkit-color-border)] p-4 ${locked ? "opacity-60" : ""}`, children: [locked ? (_jsx(Div, { className: "absolute inset-0 z-10 flex items-center justify-center bg-black/10 dark:bg-black/40 rounded-lg pointer-events-none", children: _jsx(Text, { className: "rounded bg-red-600 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-white", children: "Won \u2014 locked" }) })) : null, _jsxs(Row, { justify: "between", align: "center", className: "mb-3", children: [_jsxs(Heading, { level: 4, children: ["Prize #", it.itemNumber] }), _jsxs(Row, { gap: "xs", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => move(index, -1), disabled: locked || index === 0, "aria-label": "Move up", children: "\u2191" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => move(index, 1), disabled: locked || index === items.length - 1, "aria-label": "Move down", children: "\u2193" }), _jsx(Button, { type: "button", variant: "danger", size: "sm", onClick: () => removeItem(index), disabled: locked || items.length <= MIN_ITEMS, children: "Remove" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(FormField, { name: `item-${index}-title`, label: "Title", type: "text", value: it.title, onChange: (v) => update(index, { title: v }), disabled: locked, placeholder: "e.g. PSA 9 Charizard Base Set Holo" }), _jsx(FormField, { name: `item-${index}-description`, label: "Description (optional)", type: "textarea", value: it.description ?? "", onChange: (v) => update(index, { description: v }), disabled: locked, placeholder: "What makes this prize special?" }), _jsxs(Row, { gap: "md", children: [_jsx(Div, { className: "flex-1", children: _jsx(FormField, { name: `item-${index}-condition`, label: "Condition", type: "select", value: it.condition, onChange: (v) => update(index, { condition: v }), disabled: locked, options: CONDITION_OPTIONS }) }), _jsx(Div, { className: "flex-1", children: _jsx(FormField, { name: `item-${index}-value`, label: "Estimated value (\u20B9)", type: "number", value: it.estimatedValue != null
90
90
  ? String(Math.round(it.estimatedValue / 100))
@@ -94,6 +94,6 @@ export function PrizeDrawItemsEditor({ items, onChange, onUploadImage, onUploadV
94
94
  const existing = (it.images ?? [])[slot];
95
95
  return (_jsxs(Div, { className: "w-40", children: [_jsx(ImageUpload, { currentImage: existing, label: existing ? "Replace" : `Image ${slot + 1}`, onUpload: (file) => onUploadImage(file, it.itemNumber), onChange: (url) => setImage(index, slot, url) }), existing && !locked ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeImage(index, slot), children: "Remove" })) : null] }, slot));
96
96
  }) })] }), onUploadVideo ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-sm font-medium", children: "Video (optional)" }), _jsx("input", { type: "file", accept: "video/*", disabled: locked, onChange: (e) => handleVideoFileChange(e, index, it.itemNumber) }), it.video?.url ? (_jsxs(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] truncate", children: ["Current: ", it.video.url] })) : null] })) : null] })] }, `prize-item-${it.itemNumber}-${index}`));
97
- }) }), items.length < MIN_ITEMS ? (_jsxs(Div, { className: "rounded border border-red-400/40 bg-red-50 px-3 py-2 text-sm text-red-900 dark:bg-red-900/30 dark:text-red-100", children: ["At least ", MIN_ITEMS, " prizes are required."] })) : null] }));
97
+ }) }), items.length < MIN_ITEMS ? (_jsxs(Div, { className: "rounded border border-red-400/40 bg-error-surface px-3 py-2 text-sm text-error", children: ["At least ", MIN_ITEMS, " prizes are required."] })) : null] }));
98
98
  }
99
99
  export default PrizeDrawItemsEditor;
@@ -15,7 +15,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
15
  * nor LetItRip admins can influence the outcome.
16
16
  */
17
17
  import { useCallback, useEffect, useRef, useState } from "react";
18
- import { Button, Div, Heading, LoginRequiredModal, Modal, Stack, Text, useToast } from "../../../ui";
18
+ import { Button, Div, Heading, LoginRequiredModal, Modal, Span, Stack, Text, useToast } from "../../../ui";
19
19
  import { isAuthError } from "../../../utils/auth-error";
20
20
  import { PrizeDrawCollage } from "./PrizeDrawCollage";
21
21
  const REVEAL_DURATION_MS = 3200; // total animation length
@@ -143,8 +143,8 @@ export function PrizeRevealModal({ open, onClose, items, orderId, productId, onR
143
143
  }, REVEAL_DURATION_MS);
144
144
  }, [onReveal, orderId, phase, productId, startAnimation]);
145
145
  const winnerImg = winner?.images?.[0];
146
- return (_jsxs(Modal, { isOpen: open, onClose: onClose, title: "Prize Reveal", size: "lg", children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to reveal your prize. Please log in or create an account to continue." }), _jsxs(Stack, { gap: "md", children: [phase === "refunded" ? (_jsxs(Div, { className: "rounded border border-yellow-400/40 bg-yellow-50 px-4 py-3 text-yellow-900 dark:bg-yellow-900/30 dark:text-yellow-100", children: [_jsx(Heading, { level: 3, className: "mb-1", children: "Pool exhausted \u2014 you've been refunded" }), _jsx(Text, { className: "text-sm", children: "Every prize in this draw was already claimed by the time your entry rolled. Your order has been marked refunded automatically." })] })) : null, phase === "error" ? (_jsxs(Div, { className: "rounded border border-red-400/40 bg-red-50 px-4 py-3 text-red-900 dark:bg-red-900/30 dark:text-red-100", children: [_jsx(Text, { className: "text-sm font-semibold", children: "Something went wrong" }), _jsx(Text, { className: "text-sm", children: errorMessage })] })) : null, _jsx(PrizeDrawCollage, { items: items, hideWonState: true, highlightItemNumber: highlight }), phase === "idle" ? (_jsx(Button, { type: "button", variant: "primary", size: "lg", onClick: handleRevealClick, children: "\u2728 Reveal my prize" })) : null, phase === "revealing" ? (_jsxs(Div, { className: "rounded bg-[var(--appkit-color-surface-muted)] p-4 text-center", children: [_jsx(Text, { className: "text-lg font-semibold", children: "Rolling\u2026" }), _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "The winner was locked by the server before this animation started. Hang tight \u2014 we're just making it look pretty." })] })) : null, phase === "won" && winner ? (_jsxs(Div, { className: "rounded-lg border-2 border-[var(--appkit-color-primary)] bg-[var(--appkit-color-surface)] p-4 text-center", children: [_jsx(Text, { className: "text-xs uppercase tracking-wider text-[var(--appkit-color-text-muted)]", children: "You won" }), _jsxs(Heading, { level: 2, className: "my-2", children: ["#", winner.itemNumber, " \u2014 ", winner.title] }), winnerImg ? (
146
+ return (_jsxs(Modal, { isOpen: open, onClose: onClose, title: "Prize Reveal", size: "lg", children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to reveal your prize. Please log in or create an account to continue." }), _jsxs(Stack, { gap: "md", children: [phase === "refunded" ? (_jsxs(Div, { className: "rounded border border-yellow-400/40 bg-yellow-50 px-4 py-3 text-yellow-900 dark:bg-yellow-900/30 dark:text-yellow-100", children: [_jsx(Heading, { level: 3, className: "mb-1", children: "Pool exhausted \u2014 you've been refunded" }), _jsx(Text, { className: "text-sm", children: "Every prize in this draw was already claimed by the time your entry rolled. Your order has been marked refunded automatically." })] })) : null, phase === "error" ? (_jsxs(Div, { className: "rounded border border-red-400/40 bg-error-surface px-4 py-3 text-error", children: [_jsx(Text, { className: "text-sm font-semibold", children: "Something went wrong" }), _jsx(Text, { className: "text-sm", children: errorMessage })] })) : null, _jsx(PrizeDrawCollage, { items: items, hideWonState: true, highlightItemNumber: highlight }), phase === "idle" ? (_jsx(Button, { type: "button", variant: "primary", size: "lg", onClick: handleRevealClick, children: "\u2728 Reveal my prize" })) : null, phase === "revealing" ? (_jsxs(Div, { className: "rounded bg-[var(--appkit-color-surface-muted)] p-4 text-center", children: [_jsx(Text, { className: "text-lg font-semibold", children: "Rolling\u2026" }), _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "The winner was locked by the server before this animation started. Hang tight \u2014 we're just making it look pretty." })] })) : null, phase === "won" && winner ? (_jsxs(Div, { className: "rounded-lg border-2 border-[var(--appkit-color-primary)] bg-[var(--appkit-color-surface)] p-4 text-center", children: [_jsx(Text, { className: "text-xs uppercase tracking-wider text-[var(--appkit-color-text-muted)]", children: "You won" }), _jsxs(Heading, { level: 2, className: "my-2", children: ["#", winner.itemNumber, " \u2014 ", winner.title] }), winnerImg ? (
147
147
  /* eslint-disable-next-line @next/next/no-img-element */
148
- _jsx("img", { src: winnerImg, alt: winner.title, className: "mx-auto max-h-64 rounded" })) : null, winner.estimatedValue != null ? (_jsxs(Text, { className: "mt-2 text-sm text-[var(--appkit-color-text-muted)]", children: ["Estimated value: \u20B9", (winner.estimatedValue / 100).toLocaleString("en-IN")] })) : null] })) : null, _jsxs(Div, { className: "rounded border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-muted)] px-3 py-2 text-xs text-[var(--appkit-color-text-muted)]", children: [_jsx("strong", { className: "text-[var(--appkit-color-text)]", children: "Fairness guarantee:" }), " ", "Winners are picked by ", _jsx("code", { children: "crypto.randomInt" }), " running on LetItRip's server before the animation starts. The animation is theatrical \u2014 neither the store nor LetItRip staff can influence the outcome.", " ", effectiveRngUrl ? (_jsx("a", { href: effectiveRngUrl, target: "_blank", rel: "noreferrer noopener", className: "underline", children: "View RNG source code \u2192" })) : null] })] })] }));
148
+ _jsx("img", { src: winnerImg, alt: winner.title, className: "mx-auto max-h-64 rounded" })) : null, winner.estimatedValue != null ? (_jsxs(Text, { className: "mt-2 text-sm text-[var(--appkit-color-text-muted)]", children: ["Estimated value: \u20B9", (winner.estimatedValue / 100).toLocaleString("en-IN")] })) : null] })) : null, _jsxs(Div, { className: "rounded border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-muted)] px-3 py-2 text-xs text-[var(--appkit-color-text-muted)]", children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Fairness guarantee:" }), " ", "Winners are picked by ", _jsx("code", { children: "crypto.randomInt" }), " running on LetItRip's server before the animation starts. The animation is theatrical \u2014 neither the store nor LetItRip staff can influence the outcome.", " ", effectiveRngUrl ? (_jsx("a", { href: effectiveRngUrl, target: "_blank", rel: "noreferrer noopener", className: "underline", children: "View RNG source code \u2192" })) : null] })] })] }));
149
149
  }
150
150
  export default PrizeRevealModal;
@@ -241,8 +241,8 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
241
241
  : condition === "used"
242
242
  ? "Used"
243
243
  : condition })), _jsx(Heading, { level: 1, className: "text-xl font-bold leading-snug text-zinc-900 dark:text-zinc-50 sm:text-2xl", children: title || "Untitled Product" })] }), avgRating !== null ? (_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(StarRating, { value: avgRating }), _jsxs(Span, { className: "text-sm text-zinc-600 dark:text-zinc-400", children: [avgRating.toFixed(1), reviewCount ? ` (${reviewCount} reviews)` : ""] }), _jsxs(Span, { className: `ml-auto rounded-full px-2.5 py-0.5 text-xs font-medium ${inStock
244
- ? "bg-emerald-50 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400"
245
- : "bg-red-50 text-red-700 dark:bg-red-900/30 dark:text-red-400"}`, children: [inStock ? "✓ In Stock" : "✗ Out of Stock", inStock && effectiveStock !== null && effectiveStock <= 10
244
+ ? "bg-success-surface text-success"
245
+ : "bg-error-surface text-error"}`, children: [inStock ? "✓ In Stock" : "✗ Out of Stock", inStock && effectiveStock !== null && effectiveStock <= 10
246
246
  ? ` — only ${effectiveStock} left`
247
247
  : ""] })] })) : (_jsxs(Span, { className: `inline-flex w-fit items-center gap-1.5 rounded-full px-3 py-1 text-xs font-medium ${inStock
248
248
  ? "bg-emerald-50 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400"
@@ -262,7 +262,7 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
262
262
  codAvailable: "Cash on Delivery",
263
263
  wishlistCount: (n) => `${n} wishlisted`,
264
264
  categoryProductCount: (n, cat) => `${n} in ${cat}`,
265
- } }), productFeatures && features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures })), !productFeatures && 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 && (_jsx(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-3", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] uppercase tracking-wide text-zinc-400 dark:text-zinc-400 mb-0.5", children: "Sold by" }), _jsx(Text, { className: "text-sm font-semibold text-zinc-800 dark:text-zinc-200", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-3 py-1.5 text-xs font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderActions: () => (_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: [formattedPrice && (_jsxs(Div, { children: [_jsxs(Row, { align: "baseline", gap: "sm", wrap: true, children: [_jsx(Text, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formattedPrice }), formattedOriginal && discount && (_jsxs(_Fragment, { children: [_jsx(Span, { className: "text-sm text-zinc-400 line-through dark:text-zinc-400", children: formattedOriginal }), _jsxs(Span, { className: "rounded-full bg-red-500 px-2 py-0.5 text-xs font-bold text-white", children: ["-", discount, "%"] })] }))] }), inStock && effectiveStock !== null && effectiveStock <= 10 && (_jsxs(Text, { className: "mt-1 text-xs text-amber-600 dark:text-amber-400", children: ["Only ", effectiveStock, " left \u2014 order soon!"] }))] })), _jsxs(Stack, { gap: "sm", children: [renderPrimaryActions ? (renderPrimaryActions({
265
+ } }), productFeatures && features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures })), !productFeatures && 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 && (_jsx(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-3", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] uppercase tracking-wide text-zinc-400 dark:text-zinc-400 mb-0.5", children: "Sold by" }), _jsx(Text, { className: "text-sm font-semibold text-zinc-800 dark:text-zinc-200", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-3 py-1.5 text-xs font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderActions: () => (_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: [formattedPrice && (_jsxs(Div, { children: [_jsxs(Row, { align: "baseline", gap: "sm", wrap: true, children: [_jsx(Text, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formattedPrice }), formattedOriginal && discount && (_jsxs(_Fragment, { children: [_jsx(Span, { className: "text-sm text-zinc-400 line-through dark:text-zinc-400", children: formattedOriginal }), _jsxs(Span, { className: "rounded-full bg-red-500 px-2 py-0.5 text-xs font-bold text-white", children: ["-", discount, "%"] })] }))] }), inStock && effectiveStock !== null && effectiveStock <= 10 && (_jsxs(Text, { className: "mt-1 text-xs text-warning", children: ["Only ", effectiveStock, " left \u2014 order soon!"] }))] })), _jsxs(Stack, { gap: "sm", children: [renderPrimaryActions ? (renderPrimaryActions({
266
266
  productId: product.id,
267
267
  productSlug: product.slug ?? slug,
268
268
  productTitle: product.title,
@@ -278,7 +278,7 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
278
278
  price,
279
279
  currency,
280
280
  minOfferPercent,
281
- })] }), (shippingInfo || returnPolicy || freeShipping) && (_jsxs(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4 space-y-2.5", children: [freeShipping && (_jsxs(Row, { align: "start", gap: "sm", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-emerald-500", children: "\uD83D\uDE9A" }), _jsxs(Div, { children: [_jsx(Text, { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: "Free Delivery" }), shippingInfo && (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: shippingInfo }))] })] })), !freeShipping && shippingInfo && (_jsxs(Row, { align: "start", gap: "sm", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-zinc-400", children: "\uD83D\uDCE6" }), _jsx(Text, { className: "text-xs text-zinc-600 dark:text-zinc-400", children: shippingInfo })] })), returnPolicy && (_jsxs(Row, { align: "start", gap: "sm", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-zinc-400", children: "\u21BA" }), _jsx(Text, { className: "text-xs text-zinc-600 dark:text-zinc-400", children: returnPolicy })] }))] })), tags.length > 0 && (_jsxs(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: [_jsx(Text, { className: "mb-2 text-xs font-medium text-zinc-500 dark:text-zinc-400", children: "Tags" }), _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: [
281
+ })] }), (shippingInfo || returnPolicy || freeShipping) && (_jsxs(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4 space-y-2.5", children: [freeShipping && (_jsxs(Row, { align: "start", gap: "sm", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-emerald-500", children: "\uD83D\uDE9A" }), _jsxs(Div, { children: [_jsx(Text, { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: "Free Delivery" }), shippingInfo && (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: shippingInfo }))] })] })), !freeShipping && shippingInfo && (_jsxs(Row, { align: "start", gap: "sm", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-zinc-400", children: "\uD83D\uDCE6" }), _jsx(Text, { className: "text-xs text-zinc-600 dark:text-zinc-400", children: shippingInfo })] })), returnPolicy && (_jsxs(Row, { align: "start", gap: "sm", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-zinc-400", children: "\u21BA" }), _jsx(Text, { className: "text-xs text-zinc-600 dark:text-zinc-400", children: returnPolicy })] }))] })), tags.length > 0 && (_jsxs(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: [_jsx(Text, { className: "mb-2 text-xs font-medium text-zinc-500 dark:text-zinc-400", children: "Tags" }), _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", justify: "center", className: "text-center", children: [
282
282
  { icon: "🔒", label: "Secure\nPayment" },
283
283
  { icon: "✓", label: "Verified\nSeller" },
284
284
  { icon: "⭐", label: "Quality\nGuarantee" },
@@ -12,7 +12,7 @@ const ROW_BASE_CLASS = "flex items-start gap-2 rounded-lg border px-3 py-2 text-
12
12
  const ROW_CHECKED_CLASS = "border-primary bg-primary/5 dark:bg-primary/10";
13
13
  const ROW_DISABLED_CLASS = "border-zinc-200 dark:border-slate-700 opacity-50 cursor-not-allowed";
14
14
  const ROW_DEFAULT_CLASS = "border-zinc-200 dark:border-slate-700 hover:bg-zinc-50 dark:hover:bg-slate-800";
15
- const OVER_LIMIT_BANNER_CLASS = "rounded-lg border border-red-200 dark:border-red-900/60 bg-red-50 dark:bg-red-950/40 px-3 py-2 text-xs text-red-700 dark:text-red-200";
15
+ const OVER_LIMIT_BANNER_CLASS = "rounded-lg border border-red-200 dark:border-red-900/60 bg-error-surface px-3 py-2 text-xs text-error";
16
16
  const EMPTY_STATE_CLASS = "rounded-lg border border-zinc-200 dark:border-slate-700 bg-white dark:bg-slate-900 px-3 py-4 text-xs text-zinc-500 dark:text-zinc-400 text-center";
17
17
  function applyProductType(list, productType) {
18
18
  if (productType === "all")
@@ -62,7 +62,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
62
62
  ].join(" "), children: _jsx("svg", { className: "h-4 w-4", fill: isWishlisted ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" }) }) }))] }), _jsxs(Div, { className: "flex flex-1 flex-col p-3 pt-2.5", children: [_jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-900 dark:text-white leading-snug`, children: product.title }), ((Array.isArray(product.categoryNames) ? product.categoryNames[0] : product.categoryName) || product.brand) && (_jsxs(Row, { className: "mt-1 gap-1 flex-wrap", children: [(Array.isArray(product.categoryNames) ? product.categoryNames[0] : product.categoryName) && (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-600 dark:text-zinc-400 truncate max-w-[100px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 7a2 2 0 012-2h14a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V7z" }) }), (Array.isArray(product.categoryNames) ? product.categoryNames[0] : product.categoryName)] })), product.brand && (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-600 dark:text-zinc-400 truncate max-w-[90px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" }) }), product.brand] }))] })), (() => {
63
63
  const seller = safeDisplayName(product.storeName, "");
64
64
  return seller ? (_jsxs(Text, { className: "mt-0.5 text-[11px] text-zinc-400 dark:text-zinc-400 truncate", children: ["by ", seller] })) : null;
65
- })(), product.rating !== undefined && (_jsxs(Row, { className: "mt-1 gap-1 items-center", children: [_jsx(Span, { className: "text-[11px] text-amber-400", children: "\u2605" }), _jsxs(Span, { className: "text-[11px] text-zinc-500 dark:text-zinc-400", children: [product.rating.toFixed(1), product.reviewCount ? ` (${product.reviewCount})` : ""] })] })), _jsxs(Div, { className: "mt-auto pt-2", children: [_jsxs(Row, { className: "items-baseline gap-2", children: [_jsx(Span, { className: "text-base font-bold text-primary dark:text-primary-400", children: formatCurrency(product.price, getDefaultCurrency()) }), product.originalPrice && product.originalPrice > product.price && (_jsx(Span, { className: "text-xs text-zinc-400 line-through dark:text-zinc-400", children: formatCurrency(product.originalPrice, getDefaultCurrency()) }))] }), (() => {
65
+ })(), product.rating !== undefined && (_jsxs(Row, { className: "mt-1 gap-1", children: [_jsx(Span, { className: "text-[11px] text-amber-400", children: "\u2605" }), _jsxs(Span, { className: "text-[11px] text-zinc-500 dark:text-zinc-400", children: [product.rating.toFixed(1), product.reviewCount ? ` (${product.reviewCount})` : ""] })] })), _jsxs(Div, { className: "mt-auto pt-2", children: [_jsxs(Row, { align: "baseline", className: "gap-2", children: [_jsx(Span, { className: "text-base font-bold text-primary dark:text-primary-400", children: formatCurrency(product.price, getDefaultCurrency()) }), product.originalPrice && product.originalPrice > product.price && (_jsx(Span, { className: "text-xs text-zinc-400 line-through dark:text-zinc-400", children: formatCurrency(product.originalPrice, getDefaultCurrency()) }))] }), (() => {
66
66
  const stock = product.stockCount ?? product.stockQuantity ?? product.availableQuantity;
67
67
  if (stock === undefined || isAuction)
68
68
  return null;
@@ -71,7 +71,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
71
71
  }
72
72
  const low = stock <= 5;
73
73
  return (_jsx(Text, { className: `mt-1 text-[11px] font-medium ${low
74
- ? "text-amber-600 dark:text-amber-400"
74
+ ? "text-warning"
75
75
  : "text-zinc-500 dark:text-zinc-400"}`, children: low ? `Only ${stock} left` : `${stock} in stock` }));
76
76
  })(), featuresList && product.features && product.features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: product.features, features: featuresList, maxVisible: PRODUCT_FEATURE_CARD_MAX_VISIBLE, className: "mt-2" })), (onAddToCart || onBuyNow) && (_jsxs(Div, { className: "mt-2 grid gap-1.5", style: { gridTemplateColumns: onBuyNow && onAddToCart ? "1fr 1fr" : "1fr" }, children: [onBuyNow && (_jsxs(Button, { type: "button", variant: "primary", size: "sm", onClick: (e) => {
77
77
  e.stopPropagation();
@@ -113,7 +113,7 @@ function ProductListRow({ product, onClick, onAddToWishlist, isWishlisted, }) {
113
113
  }
114
114
  const low = stock <= 5;
115
115
  return (_jsx(Span, { className: `text-[11px] font-medium ${low
116
- ? "text-amber-600 dark:text-amber-400"
116
+ ? "text-warning"
117
117
  : "text-zinc-400 dark:text-zinc-400"}`, children: low ? `Only ${stock} left` : `${stock} in stock` }));
118
118
  })()] })] }), onAddToWishlist && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => {
119
119
  e.stopPropagation();
@@ -256,6 +256,4 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
256
256
  /** Cloud Functions: returns refs for draft products older than `cutoff` (for 30-day prune). */
257
257
  getStaleDraftRefs(cutoff: Date): Promise<DocumentReference[]>;
258
258
  }
259
- export declare class ProductsRepository extends ProductRepository {
260
- }
261
259
  export declare const productRepository: ProductRepository;
@@ -573,6 +573,4 @@ ProductRepository.FILTER_ALIASES = {
573
573
  ].join(",");
574
574
  },
575
575
  };
576
- export class ProductsRepository extends ProductRepository {
577
- }
578
576
  export const productRepository = new ProductRepository();
@@ -4,6 +4,6 @@
4
4
  * Server-only entry point — repositories and API route handlers.
5
5
  */
6
6
  export * from "./actions";
7
- export { ProductRepository, ProductsRepository, productRepository, } from "./repository/products.repository";
7
+ export { ProductRepository, productRepository, } from "./repository/products.repository";
8
8
  export { GET, POST } from "./api/route";
9
9
  export { GET as productItemGET, PATCH as productItemPATCH, DELETE as productItemDELETE, } from "./api/[id]/route";
@@ -4,6 +4,6 @@
4
4
  * Server-only entry point — repositories and API route handlers.
5
5
  */
6
6
  export * from "./actions";
7
- export { ProductRepository, ProductsRepository, productRepository, } from "./repository/products.repository";
7
+ export { ProductRepository, productRepository, } from "./repository/products.repository";
8
8
  export { GET, POST } from "./api/route";
9
9
  export { GET as productItemGET, PATCH as productItemPATCH, DELETE as productItemDELETE, } from "./api/[id]/route";
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { Pencil, Trash2, ToggleLeft, ToggleRight } from "lucide-react";
5
- import { BaseListingCard, Div, Row, Span, Text } from "../../../ui";
5
+ import { BaseListingCard, ConfirmDeleteModal, Div, Row, Span, Text } from "../../../ui";
6
6
  import { useLongPress } from "../../../react/hooks/useLongPress";
7
7
  const TYPE_COLORS = {
8
8
  percentage: { card: "bg-purple-50 border-purple-200 dark:bg-purple-950/30 dark:border-purple-800", code: "bg-purple-100 dark:bg-purple-900/40 text-purple-800 dark:text-purple-200 border-purple-300 dark:border-purple-700" },
@@ -91,6 +91,7 @@ export function CouponCard({ coupon, labels: labelsProp, onCopy, className = "",
91
91
  const [copied, setCopied] = useState(false);
92
92
  const [claiming, setClaiming] = useState(false);
93
93
  const [busy, setBusy] = useState(null);
94
+ const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
94
95
  const longPress = useLongPress(() => onSelect?.(n.id, !isSelected));
95
96
  const expiry = formatDateSafe(n.expiresAt);
96
97
  const discountLabel = formatDiscount(n, labels);
@@ -144,11 +145,10 @@ export function CouponCard({ coupon, labels: labelsProp, onCopy, className = "",
144
145
  setBusy(null);
145
146
  }
146
147
  };
147
- const handleDelete = async () => {
148
+ const handleDeleteConfirmed = async () => {
148
149
  if (!onDelete)
149
150
  return;
150
- if (typeof window !== "undefined" && !window.confirm(labels.deleteConfirm))
151
- return;
151
+ setDeleteConfirmOpen(false);
152
152
  setBusy("delete");
153
153
  try {
154
154
  await onDelete(n.id);
@@ -158,8 +158,8 @@ export function CouponCard({ coupon, labels: labelsProp, onCopy, className = "",
158
158
  }
159
159
  };
160
160
  return (_jsxs(Div, { className: `group relative flex h-full flex-col rounded-xl border-2 p-4 ${colors.card} ${isSelected ? "ring-2 ring-primary" : ""} ${!n.isActive ? "opacity-70" : ""} ${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(n.id, !isSelected); }, label: isSelected ? "Deselect coupon" : "Select coupon", position: "top-2 right-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), (hasAdminActions || n.scope) && (_jsxs(Row, { gap: "xs", className: "mb-2 flex-wrap", children: [_jsx(Span, { className: `inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide ${n.isActive
161
- ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"
162
- : "bg-zinc-200 text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300"}`, children: n.isActive ? labels.active : labels.inactive }), n.scope && (_jsx(Span, { className: "inline-flex items-center rounded-full bg-zinc-200/70 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-zinc-700 dark:bg-zinc-700/60 dark:text-zinc-200", children: n.scope }))] })), _jsxs(Div, { className: "mb-3", children: [_jsx(Text, { className: "text-2xl font-extrabold tracking-tight leading-none", children: discountLabel }), n.name && (_jsx(Text, { className: "text-sm mt-0.5 font-medium opacity-80", children: n.name }))] }), n.description && (_jsx(Text, { className: "text-xs opacity-60 mb-3", children: n.description })), _jsxs("div", { className: `flex items-center gap-2 rounded-lg border border-dashed px-3 py-2 ${colors.code}`, children: [_jsx("span", { className: "flex-1 font-mono text-sm font-bold tracking-widest uppercase select-all", children: n.code || "—" }), n.code && (_jsx("button", { type: "button", onClick: handleCopy, className: "shrink-0 rounded-md px-2.5 py-1 text-xs font-semibold transition-all hover:opacity-80 active:scale-95 bg-white/60 dark:bg-black/20 border border-current/20", "aria-label": "Copy coupon code", children: copied ? labels.copied : labels.copy }))] }), showClaim && n.code && n.isActive && (_jsx("div", { className: "mt-2", children: _jsx("button", { type: "button", onClick: handleClaim, disabled: claiming, className: "w-full rounded-md px-3 py-1.5 text-xs font-semibold transition-all active:scale-95 bg-[var(--appkit-color-primary-700)] text-[var(--appkit-color-text-on-primary,#fff)] hover:bg-[var(--appkit-color-primary-800)] disabled:opacity-50", "aria-label": `Claim coupon ${n.code} and apply at checkout`, children: claiming ? labels.claiming : labels.claim }) })), _jsxs(Row, { wrap: true, gap: "sm", className: "text-xs opacity-60 mt-2", children: [n.minOrderAmount ? (_jsxs(Span, { children: [labels.minOrder, ": \u20B9", (n.minOrderAmount / 100).toFixed(0)] })) : null, expiry && (_jsxs(Span, { children: [labels.expires, ": ", expiry] })), n.usageLimit ? (_jsxs(Span, { children: [n.usageCount, "/", n.usageLimit, " ", labels.used] })) : n.usageCount > 0 ? (_jsxs(Span, { children: [n.usageCount, " ", labels.used] })) : null] }), hasAdminActions && (_jsxs(Row, { gap: "xs", className: "mt-auto pt-3 justify-end", children: [onEdit && (_jsx("button", { type: "button", onClick: () => onEdit(n.id), title: labels.edit, "aria-label": labels.edit, className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-white/60 dark:hover:bg-black/30 hover:text-zinc-800 dark:hover:text-zinc-200 transition-colors", children: _jsx(Pencil, { className: "h-4 w-4" }) })), onToggleActive && (_jsx("button", { type: "button", onClick: handleToggle, disabled: busy === "toggle", title: n.isActive ? labels.deactivate : labels.activate, "aria-label": n.isActive ? labels.deactivate : labels.activate, className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-white/60 dark:hover:bg-black/30 transition-colors disabled:opacity-50", children: n.isActive
161
+ ? "bg-success-surface text-success"
162
+ : "bg-zinc-200 text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300"}`, children: n.isActive ? labels.active : labels.inactive }), n.scope && (_jsx(Span, { className: "inline-flex items-center rounded-full bg-zinc-200/70 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-zinc-700 dark:bg-zinc-700/60 dark:text-zinc-200", children: n.scope }))] })), _jsxs(Div, { className: "mb-3", children: [_jsx(Text, { className: "text-2xl font-extrabold tracking-tight leading-none", children: discountLabel }), n.name && (_jsx(Text, { className: "text-sm mt-0.5 font-medium opacity-80", children: n.name }))] }), n.description && (_jsx(Text, { className: "text-xs opacity-60 mb-3", children: n.description })), _jsxs("div", { className: `flex items-center gap-2 rounded-lg border border-dashed px-3 py-2 ${colors.code}`, children: [_jsx("span", { className: "flex-1 font-mono text-sm font-bold tracking-widest uppercase select-all", children: n.code || "—" }), n.code && (_jsx("button", { type: "button", onClick: handleCopy, className: "shrink-0 rounded-md px-2.5 py-1 text-xs font-semibold transition-all hover:opacity-80 active:scale-95 bg-white/60 dark:bg-black/20 border border-current/20", "aria-label": "Copy coupon code", children: copied ? labels.copied : labels.copy }))] }), showClaim && n.code && n.isActive && (_jsx("div", { className: "mt-2", children: _jsx("button", { type: "button", onClick: handleClaim, disabled: claiming, className: "w-full rounded-md px-3 py-1.5 text-xs font-semibold transition-all active:scale-95 bg-[var(--appkit-color-primary-700)] text-[var(--appkit-color-text-on-primary,#fff)] hover:bg-[var(--appkit-color-primary-800)] disabled:opacity-50", "aria-label": `Claim coupon ${n.code} and apply at checkout`, children: claiming ? labels.claiming : labels.claim }) })), _jsxs(Row, { wrap: true, gap: "sm", className: "text-xs opacity-60 mt-2", children: [n.minOrderAmount ? (_jsxs(Span, { children: [labels.minOrder, ": \u20B9", (n.minOrderAmount / 100).toFixed(0)] })) : null, expiry && (_jsxs(Span, { children: [labels.expires, ": ", expiry] })), n.usageLimit ? (_jsxs(Span, { children: [n.usageCount, "/", n.usageLimit, " ", labels.used] })) : n.usageCount > 0 ? (_jsxs(Span, { children: [n.usageCount, " ", labels.used] })) : null] }), hasAdminActions && (_jsxs(Row, { gap: "xs", justify: "end", className: "mt-auto pt-3", children: [onEdit && (_jsx("button", { type: "button", onClick: () => onEdit(n.id), title: labels.edit, "aria-label": labels.edit, className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-white/60 dark:hover:bg-black/30 hover:text-zinc-800 dark:hover:text-zinc-200 transition-colors", children: _jsx(Pencil, { className: "h-4 w-4" }) })), onToggleActive && (_jsx("button", { type: "button", onClick: handleToggle, disabled: busy === "toggle", title: n.isActive ? labels.deactivate : labels.activate, "aria-label": n.isActive ? labels.deactivate : labels.activate, className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-white/60 dark:hover:bg-black/30 transition-colors disabled:opacity-50", children: n.isActive
163
163
  ? _jsx(ToggleRight, { className: "h-4 w-4 text-emerald-600 dark:text-emerald-400" })
164
- : _jsx(ToggleLeft, { className: "h-4 w-4" }) })), onDelete && (_jsx("button", { type: "button", onClick: handleDelete, disabled: busy === "delete", title: labels.delete, "aria-label": labels.delete, className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-red-50 dark:hover:bg-red-950/40 hover:text-red-600 dark:hover:text-red-400 transition-colors disabled:opacity-50", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }))] }));
164
+ : _jsx(ToggleLeft, { className: "h-4 w-4" }) })), onDelete && (_jsx("button", { type: "button", onClick: () => setDeleteConfirmOpen(true), disabled: busy === "delete", title: labels.delete, "aria-label": labels.delete, className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-red-50 dark:hover:bg-red-950/40 hover:text-red-600 dark:hover:text-red-400 transition-colors disabled:opacity-50", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })), deleteConfirmOpen && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Coupon", message: labels.deleteConfirm, onConfirm: handleDeleteConfirmed, onClose: () => setDeleteConfirmOpen(false), isDeleting: busy === "delete" }))] }));
165
165
  }
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useState, useCallback, useMemo } from "react";
4
4
  import { Search, SlidersHorizontal, X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
- import { Pagination, SortDropdown, Div, Text, Heading } from "../../../ui";
6
+ import { ListingFilterDrawer, Pagination, SortDropdown, Div, Text, Heading } from "../../../ui";
7
7
  import { usePromotions } from "../hooks/usePromotions";
8
8
  import { CouponCard } from "./CouponCard";
9
9
  import { TABLE_KEYS } from "../../../constants/table-keys";
@@ -34,6 +34,7 @@ export function CouponsIndexListing({ initialCoupons, storeSlug, storeId, }) {
34
34
  get: (key) => pendingFilters[key] ?? "",
35
35
  set: (key, value) => setPendingFilters((p) => ({ ...p, [key]: value })),
36
36
  }), [pendingFilters]);
37
+ const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
37
38
  const openFilters = useCallback(() => {
38
39
  setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
39
40
  setFilterOpen(true);
@@ -97,5 +98,5 @@ export function CouponsIndexListing({ initialCoupons, storeSlug, storeId, }) {
97
98
  minOrder: "Min. order",
98
99
  off: "OFF",
99
100
  freeShipping: "Free Shipping",
100
- } }, coupon.id))) })), totalPages > 1 && (_jsx("div", { className: "mt-8 flex justify-center", children: _jsx(Pagination, { currentPage: table.getNumber("page", 1), totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), !isLoading && total > 0 && (_jsx(Div, { className: "mt-4 text-center", children: _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: [total, " coupon", total !== 1 ? "s" : "", " available"] }) }))] }), 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: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _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" }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-6", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Discount Type" }), _jsxs("div", { className: "space-y-2", children: [COUPON_TYPES.map((t) => (_jsxs("label", { className: "flex items-center gap-2 cursor-pointer text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx("input", { type: "radio", name: "coupon-type", value: t.value, checked: pendingTable.get(TABLE_KEYS.TYPE) === t.value, onChange: () => { pendingTable.set(TABLE_KEYS.TYPE, t.value); }, className: "accent-primary" }), t.label] }, t.value))), pendingTable.get(TABLE_KEYS.TYPE) && (_jsx("button", { type: "button", onClick: () => { pendingTable.set(TABLE_KEYS.TYPE, ""); }, className: "text-xs text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 underline", children: "Clear type" }))] })] }), _jsxs(_Fragment, { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Valid Date Range" }), _jsxs("div", { className: "space-y-3", children: [_jsxs(_Fragment, { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "From date" }), _jsx("input", { type: "date", value: pendingTable.get(TABLE_KEYS.DATE_FROM) || "", onChange: (e) => { pendingTable.set(TABLE_KEYS.DATE_FROM, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] }), _jsxs(_Fragment, { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "To date" }), _jsx("input", { type: "date", value: pendingTable.get(TABLE_KEYS.DATE_TO) || "", onChange: (e) => { pendingTable.set(TABLE_KEYS.DATE_TO, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] })] })] })] }), _jsxs("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5 flex gap-2", children: [_jsx("button", { type: "button", onClick: clearPending, className: "flex-1 rounded-lg border border-zinc-300 dark:border-slate-600 py-2.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: "Clear all" }), _jsx("button", { type: "button", onClick: applyFilters, className: "flex-1 rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors", children: "Apply Filters" })] })] })] }))] }));
101
+ } }, coupon.id))) })), totalPages > 1 && (_jsx("div", { className: "mt-8 flex justify-center", children: _jsx(Pagination, { currentPage: table.getNumber("page", 1), totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), !isLoading && total > 0 && (_jsx(Div, { className: "mt-4 text-center", children: _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: [total, " coupon", total !== 1 ? "s" : "", " available"] }) }))] }), _jsxs(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearPending, activeCount: activeFilterCount, children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Discount Type" }), _jsxs("div", { className: "space-y-2", children: [COUPON_TYPES.map((t) => (_jsxs("label", { className: "flex items-center gap-2 cursor-pointer text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx("input", { type: "radio", name: "coupon-type", value: t.value, checked: pendingTable.get(TABLE_KEYS.TYPE) === t.value, onChange: () => { pendingTable.set(TABLE_KEYS.TYPE, t.value); }, className: "accent-primary" }), t.label] }, t.value))), pendingTable.get(TABLE_KEYS.TYPE) && (_jsx("button", { type: "button", onClick: () => { pendingTable.set(TABLE_KEYS.TYPE, ""); }, className: "text-xs text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 underline", children: "Clear type" }))] })] }), _jsxs(_Fragment, { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Valid Date Range" }), _jsxs("div", { className: "space-y-3", children: [_jsxs(_Fragment, { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "From date" }), _jsx("input", { type: "date", value: pendingTable.get(TABLE_KEYS.DATE_FROM) || "", onChange: (e) => { pendingTable.set(TABLE_KEYS.DATE_FROM, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] }), _jsxs(_Fragment, { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "To date" }), _jsx("input", { type: "date", value: pendingTable.get(TABLE_KEYS.DATE_TO) || "", onChange: (e) => { pendingTable.set(TABLE_KEYS.DATE_TO, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] })] })] })] })] }));
101
102
  }
@@ -11,5 +11,5 @@ export function PromotionsHero({ labels, heroBannerClass = "bg-gradient-to-br fr
11
11
  }
12
12
  /** @deprecated Use PromotionsHero + per-tab content in your page instead. */
13
13
  export function PromotionsView({ labels, hasContent, heroBannerClass = "bg-gradient-to-br from-rose-500 to-orange-500", renderCoupons, couponsCount = 0, renderDealsSection, renderFeaturedSection, }) {
14
- return (_jsxs(Div, { className: "min-h-screen bg-white dark:bg-slate-900", children: [_jsx(PromotionsHero, { labels: labels, heroBannerClass: heroBannerClass }), _jsxs(Div, { className: "max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-12 space-y-12", children: [!hasContent && (_jsxs(Div, { className: "text-center py-16", children: [_jsx(Heading, { level: 2, className: "mb-2", children: labels.emptyDeals }), _jsx(Text, { variant: "secondary", children: labels.checkBack })] })), hasContent && (_jsxs(Div, { className: "space-y-12", children: [renderCoupons && (_jsxs(Section, { children: [_jsxs(Div, { className: "mb-6", children: [_jsx(Heading, { level: 2, children: labels.couponsTitle }), couponsCount > 0 && (_jsx(Text, { variant: "secondary", className: "mt-1", children: labels.couponsSubtitle }))] }), couponsCount > 0 ? (renderCoupons()) : (_jsx(Text, { variant: "secondary", size: "sm", children: labels.emptyCoupons }))] })), renderDealsSection?.(), renderFeaturedSection?.()] }))] })] }));
14
+ return (_jsxs(Div, { surface: "default", className: "min-h-screen", children: [_jsx(PromotionsHero, { labels: labels, heroBannerClass: heroBannerClass }), _jsxs(Div, { className: "max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-12 space-y-12", children: [!hasContent && (_jsxs(Div, { className: "text-center py-16", children: [_jsx(Heading, { level: 2, className: "mb-2", children: labels.emptyDeals }), _jsx(Text, { variant: "secondary", children: labels.checkBack })] })), hasContent && (_jsxs(Div, { className: "space-y-12", children: [renderCoupons && (_jsxs(Section, { children: [_jsxs(Div, { className: "mb-6", children: [_jsx(Heading, { level: 2, children: labels.couponsTitle }), couponsCount > 0 && (_jsx(Text, { variant: "secondary", className: "mt-1", children: labels.couponsSubtitle }))] }), couponsCount > 0 ? (renderCoupons()) : (_jsx(Text, { variant: "secondary", size: "sm", children: labels.emptyCoupons }))] })), renderDealsSection?.(), renderFeaturedSection?.()] }))] })] }));
15
15
  }
@@ -77,8 +77,8 @@ export function ReviewDetailShell({ review, storeHref }) {
77
77
  ? String(ROUTES.PUBLIC.PROFILE(reviewerProfileId))
78
78
  : null;
79
79
  const currentImage = lightboxIdx !== null ? images[lightboxIdx] : null;
80
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: "border-b border-neutral-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 pb-8 pt-10", children: _jsxs("div", { className: "mx-auto max-w-3xl px-4", children: [_jsxs("div", { className: "mb-4 flex items-center gap-3", children: [_jsx(StarRating, { value: review.rating, size: "lg", readOnly: true }), _jsxs("span", { className: "text-2xl font-bold text-neutral-900 dark:text-white", children: [review.rating, ".0"] }), review.verified && (_jsx("span", { className: "inline-flex items-center gap-1 rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400", children: "\u2713 Verified Purchase" })), review.featured && (_jsx("span", { className: "inline-flex items-center gap-1 rounded-full bg-yellow-100 px-3 py-1 text-xs font-semibold text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400", children: "\u2605 Featured" }))] }), review.title && (_jsx(Heading, { level: 1, className: "text-2xl font-bold text-neutral-900 dark:text-white mb-4 leading-snug", children: review.title })), _jsxs("div", { className: "flex items-center gap-3", children: [review.userAvatar ? (_jsx("div", { role: "img", "aria-label": displayName, className: "h-11 w-11 flex-shrink-0 rounded-full bg-center bg-cover ring-2 ring-white dark:ring-zinc-800", style: { backgroundImage: `url(${review.userAvatar})` } })) : (_jsx("div", { className: "flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-full bg-primary/10 text-base font-bold text-primary ring-2 ring-white dark:ring-zinc-800", children: initials })), _jsxs("div", { className: "min-w-0", children: [reviewerHref ? (_jsx(Link, { href: reviewerHref, className: "text-sm font-semibold text-neutral-900 dark:text-white hover:text-primary transition-colors", children: displayName })) : (_jsx("span", { className: "text-sm font-semibold text-neutral-900 dark:text-white", children: review.isAnonymous ? "Anonymous" : displayName })), date && (_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400 mt-0.5", children: date }))] })] })] }) }), _jsxs("div", { className: "mx-auto max-w-3xl px-4 py-8 space-y-8", children: [review.comment && (_jsx(Section, { children: _jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-neutral dark:prose-invert max-w-none prose-p:leading-relaxed prose-headings:font-semibold prose-img:rounded-lg prose-a:text-primary", className: "text-neutral-700 dark:text-zinc-300" }) })), images.length > 0 && (_jsxs(Section, { children: [_jsxs(Heading, { level: 2, className: "text-sm font-semibold uppercase tracking-wide text-zinc-400 dark:text-zinc-400 mb-3", children: ["Photos (", images.length, ")"] }), _jsx("div", { className: "grid grid-cols-3 sm:grid-cols-4 gap-2", children: images.map((img, i) => (_jsxs("button", { type: "button", onClick: () => setLightboxIdx(i), "aria-label": `View photo ${i + 1}`, className: "group relative aspect-square overflow-hidden rounded-xl border border-neutral-200 dark:border-zinc-700 bg-neutral-100 dark:bg-zinc-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary", children: [_jsx("div", { className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${img.thumbnailUrl ?? img.url})` } }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity bg-black/30", children: _jsx("span", { className: "text-white text-xl", children: "\uD83D\uDD0D" }) })] }, i))) })] })), review.video && (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "text-sm font-semibold uppercase tracking-wide text-zinc-400 dark:text-zinc-400 mb-3", children: "Video" }), _jsx("div", { className: "overflow-hidden rounded-xl border border-neutral-200 dark:border-zinc-700 bg-black aspect-video", children: _jsx("video", { src: review.video.url, poster: review.video.thumbnailUrl, controls: true, className: "h-full w-full", preload: "metadata" }) })] })), _jsxs(Section, { className: "flex items-center gap-4 py-4 border-t border-neutral-100 dark:border-zinc-800", children: [_jsx("div", { className: "text-sm text-neutral-500 dark:text-zinc-400", children: helpfulCount > 0 && (_jsxs("span", { children: [_jsx("strong", { className: "text-neutral-900 dark:text-white", children: helpfulCount }), " ", helpfulCount === 1 ? "person" : "people", " found this helpful"] })) }), _jsxs("button", { type: "button", onClick: handleVote, disabled: voted || voting, className: `ml-auto flex items-center gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-colors ${voted
81
- ? "border-emerald-200 bg-emerald-50 text-emerald-600 dark:border-emerald-800 dark:bg-emerald-900/20 dark:text-emerald-400 cursor-default"
80
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "border-b border-neutral-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 pb-8 pt-10", children: _jsxs("div", { className: "mx-auto max-w-3xl px-4", children: [_jsxs("div", { className: "mb-4 flex items-center gap-3", children: [_jsx(StarRating, { value: review.rating, size: "lg", readOnly: true }), _jsxs("span", { className: "text-2xl font-bold text-neutral-900 dark:text-white", children: [review.rating, ".0"] }), review.verified && (_jsx("span", { className: "inline-flex items-center gap-1 rounded-full bg-success-surface px-3 py-1 text-xs font-semibold text-success", children: "\u2713 Verified Purchase" })), review.featured && (_jsx("span", { className: "inline-flex items-center gap-1 rounded-full bg-yellow-100 px-3 py-1 text-xs font-semibold text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400", children: "\u2605 Featured" }))] }), review.title && (_jsx(Heading, { level: 1, className: "text-2xl font-bold text-neutral-900 dark:text-white mb-4 leading-snug", children: review.title })), _jsxs("div", { className: "flex items-center gap-3", children: [review.userAvatar ? (_jsx("div", { role: "img", "aria-label": displayName, className: "h-11 w-11 flex-shrink-0 rounded-full bg-center bg-cover ring-2 ring-white dark:ring-zinc-800", style: { backgroundImage: `url(${review.userAvatar})` } })) : (_jsx("div", { className: "flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-full bg-primary/10 text-base font-bold text-primary ring-2 ring-white dark:ring-zinc-800", children: initials })), _jsxs("div", { className: "min-w-0", children: [reviewerHref ? (_jsx(Link, { href: reviewerHref, className: "text-sm font-semibold text-neutral-900 dark:text-white hover:text-primary transition-colors", children: displayName })) : (_jsx("span", { className: "text-sm font-semibold text-neutral-900 dark:text-white", children: review.isAnonymous ? "Anonymous" : displayName })), date && (_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400 mt-0.5", children: date }))] })] })] }) }), _jsxs("div", { className: "mx-auto max-w-3xl px-4 py-8 space-y-8", children: [review.comment && (_jsx(Section, { children: _jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-neutral dark:prose-invert max-w-none prose-p:leading-relaxed prose-headings:font-semibold prose-img:rounded-lg prose-a:text-primary", className: "text-neutral-700 dark:text-zinc-300" }) })), images.length > 0 && (_jsxs(Section, { children: [_jsxs(Heading, { level: 2, className: "text-sm font-semibold uppercase tracking-wide text-zinc-400 dark:text-zinc-400 mb-3", children: ["Photos (", images.length, ")"] }), _jsx("div", { className: "grid grid-cols-3 sm:grid-cols-4 gap-2", children: images.map((img, i) => (_jsxs("button", { type: "button", onClick: () => setLightboxIdx(i), "aria-label": `View photo ${i + 1}`, className: "group relative aspect-square overflow-hidden rounded-xl border border-neutral-200 dark:border-zinc-700 bg-neutral-100 dark:bg-zinc-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary", children: [_jsx("div", { className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${img.thumbnailUrl ?? img.url})` } }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity bg-black/30", children: _jsx("span", { className: "text-white text-xl", children: "\uD83D\uDD0D" }) })] }, i))) })] })), review.video && (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "text-sm font-semibold uppercase tracking-wide text-zinc-400 dark:text-zinc-400 mb-3", children: "Video" }), _jsx("div", { className: "overflow-hidden rounded-xl border border-neutral-200 dark:border-zinc-700 bg-black aspect-video", children: _jsx("video", { src: review.video.url, poster: review.video.thumbnailUrl, controls: true, className: "h-full w-full", preload: "metadata" }) })] })), _jsxs(Section, { className: "flex items-center gap-4 py-4 border-t border-neutral-100 dark:border-zinc-800", children: [_jsx("div", { className: "text-sm text-neutral-500 dark:text-zinc-400", children: helpfulCount > 0 && (_jsxs("span", { children: [_jsx(Span, { weight: "bold", className: "text-neutral-900 dark:text-white", children: helpfulCount }), " ", helpfulCount === 1 ? "person" : "people", " found this helpful"] })) }), _jsxs("button", { type: "button", onClick: handleVote, disabled: voted || voting, className: `ml-auto flex items-center gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-colors ${voted
81
+ ? "border-emerald-200 bg-success-surface text-success dark:border-emerald-800 cursor-default"
82
82
  : "border-neutral-300 dark:border-zinc-600 text-neutral-700 dark:text-zinc-200 hover:border-primary hover:text-primary dark:hover:border-primary dark:hover:text-primary disabled:opacity-50"}`, children: [_jsx("span", { "aria-hidden": "true", children: voted ? "✓" : "👍" }), voted ? "Marked helpful" : voting ? "Saving…" : "Helpful?"] })] }), _jsxs(Section, { className: "grid gap-3 sm:grid-cols-3", children: [productHref && (_jsxs(Link, { href: productHref, className: CLS_RELATED_LINK, children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-orange-100 dark:bg-orange-900/30 text-xl", children: "\uD83D\uDCE6" }), _jsxs("div", { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Product" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: review.productTitle ?? "View Product" })] })] })), sellerHref && (_jsxs(Link, { href: sellerHref, className: CLS_RELATED_LINK, children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-blue-100 dark:bg-blue-900/30 text-xl", children: "\uD83C\uDFEA" }), _jsxs("div", { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Seller" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: "View Seller" })] })] })), reviewerHref ? (_jsxs(Link, { href: reviewerHref, className: CLS_RELATED_LINK, children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-purple-100 dark:bg-purple-900/30 text-xl", children: "\uD83D\uDC64" }), _jsxs("div", { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Reviewer" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: displayName })] })] })) : (_jsxs("div", { className: "flex items-center gap-3 rounded-xl border border-neutral-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 p-4", children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-purple-100 dark:bg-purple-900/30 text-xl", children: "\uD83D\uDC64" }), _jsxs("div", { className: "min-w-0", children: [_jsx(Span, { className: "block text-xs text-zinc-400 dark:text-zinc-400 mb-0.5", children: "Reviewer" }), _jsx(Span, { className: "block text-sm font-medium text-neutral-900 dark:text-white truncate", children: "Anonymous" })] })] }))] })] }), lightboxIdx !== null && currentImage && (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/95", onClick: closeLightbox, role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", children: [_jsx("button", { type: "button", onClick: closeLightbox, "aria-label": "Close lightbox", className: "absolute top-4 right-4 z-10 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/20 transition-colors text-xl", children: "\u00D7" }), _jsxs("div", { className: "absolute top-4 left-1/2 -translate-x-1/2 text-white/70 text-sm", children: [lightboxIdx + 1, " / ", images.length] }), images.length > 1 && (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); prevImage(); }, "aria-label": "Previous image", className: "absolute left-4 top-1/2 -translate-y-1/2 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/25 transition-colors text-2xl", children: "\u2039" })), _jsx("div", { className: "max-h-[85vh] max-w-[85vw] flex items-center justify-center", onClick: (e) => e.stopPropagation(), children: _jsx("img", { src: currentImage.url, alt: `Review photo ${lightboxIdx + 1}`, className: "max-h-[85vh] max-w-[85vw] rounded-lg object-contain shadow-2xl" }) }), images.length > 1 && (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); nextImage(); }, "aria-label": "Next image", className: "absolute right-4 top-1/2 -translate-y-1/2 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/25 transition-colors text-2xl", children: "\u203A" })), images.length > 1 && (_jsx("div", { className: "absolute bottom-4 left-0 right-0 flex justify-center gap-2 px-4", children: images.map((img, i) => (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); setLightboxIdx(i); }, "aria-label": `Go to image ${i + 1}`, className: `h-12 w-12 flex-shrink-0 rounded-md bg-center bg-cover border-2 transition-all ${i === lightboxIdx
83
83
  ? "border-white scale-110"
84
84
  : "border-transparent opacity-60 hover:opacity-100"}`, style: { backgroundImage: `url(${img.thumbnailUrl ?? img.url})` } }, i))) }))] }))] }));