@mohasinac/appkit 4.2.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/dist/_internal/server/features/auth/actions.d.ts +0 -1
  2. package/dist/_internal/server/features/auth/actions.js +0 -1
  3. package/dist/_internal/server/features/bundles/data.d.ts +22 -4
  4. package/dist/_internal/server/features/bundles/data.js +41 -4
  5. package/dist/_internal/server/features/bundles/index.d.ts +1 -1
  6. package/dist/_internal/server/features/bundles/index.js +1 -1
  7. package/dist/_internal/server/features/checkout/actions.js +7 -78
  8. package/dist/_internal/server/features/checkout/prize-bundle-gates.d.ts +5 -16
  9. package/dist/_internal/server/features/checkout/prize-bundle-gates.js +5 -30
  10. package/dist/_internal/server/features/orders/adapters.js +1 -14
  11. package/dist/_internal/server/features/products/actions.js +8 -3
  12. package/dist/_internal/server/features/products/service.d.ts +15 -0
  13. package/dist/_internal/server/features/products/service.js +41 -0
  14. package/dist/_internal/server/functions/firestore.d.ts +3 -1
  15. package/dist/_internal/server/functions/firestore.js +17 -1
  16. package/dist/_internal/server/functions/scheduled.d.ts +2 -5
  17. package/dist/_internal/server/functions/scheduled.js +6 -30
  18. package/dist/_internal/server/jobs/core/bundleStockSync.js +25 -7
  19. package/dist/_internal/server/jobs/core/countersReconcile.js +7 -1
  20. package/dist/_internal/server/jobs/core/index.d.ts +4 -4
  21. package/dist/_internal/server/jobs/core/index.js +4 -4
  22. package/dist/_internal/server/jobs/core/onPrizeDrawPaymentConfirmed.d.ts +19 -0
  23. package/dist/_internal/server/jobs/core/onPrizeDrawPaymentConfirmed.js +30 -0
  24. package/dist/_internal/server/jobs/core/paymentWindowTimeout.d.ts +2 -2
  25. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +2 -2
  26. package/dist/_internal/server/jobs/core/prizeDrawAssignWinner.d.ts +20 -0
  27. package/dist/_internal/server/jobs/core/prizeDrawAssignWinner.js +113 -0
  28. package/dist/_internal/server/jobs/core/prizeDrawExpiryReveal.d.ts +14 -0
  29. package/dist/_internal/server/jobs/core/prizeDrawExpiryReveal.js +74 -0
  30. package/dist/_internal/server/jobs/core/prizeDrawSoldOutReveal.d.ts +22 -0
  31. package/dist/_internal/server/jobs/core/prizeDrawSoldOutReveal.js +68 -0
  32. package/dist/_internal/server/jobs/handlers/index.d.ts +3 -4
  33. package/dist/_internal/server/jobs/handlers/index.js +8 -6
  34. package/dist/_internal/server/jobs/handlers/onPrizeDrawPaymentConfirmed.d.ts +3 -0
  35. package/dist/_internal/server/jobs/handlers/onPrizeDrawPaymentConfirmed.js +11 -0
  36. package/dist/_internal/server/jobs/handlers/prizeDrawExpiryReveal.d.ts +2 -0
  37. package/dist/_internal/server/jobs/handlers/prizeDrawExpiryReveal.js +2 -0
  38. package/dist/_internal/server/jobs/handlers/prizeDrawSoldOutReveal.d.ts +3 -0
  39. package/dist/_internal/server/jobs/handlers/prizeDrawSoldOutReveal.js +4 -0
  40. package/dist/_internal/shared/actions/action-registry.js +40 -10
  41. package/dist/_internal/shared/checkout/rules/_defaults.js +1 -2
  42. package/dist/_internal/shared/checkout/rules/_limits.d.ts +3 -0
  43. package/dist/_internal/shared/checkout/rules/_limits.js +3 -0
  44. package/dist/_internal/shared/checkout/rules/_registry.d.ts +2 -1
  45. package/dist/_internal/shared/checkout/rules/_registry.js +2 -2
  46. package/dist/_internal/shared/checkout/rules/classified.rule.js +0 -1
  47. package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
  48. package/dist/_internal/shared/checkout/rules/index.js +1 -1
  49. package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +20 -1
  50. package/dist/_internal/shared/checkout/rules/types.d.ts +17 -7
  51. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +2 -0
  52. package/dist/_internal/shared/features/categories/bundle-copy.js +2 -0
  53. package/dist/_internal/shared/features/categories/bundle-pricing.d.ts +23 -0
  54. package/dist/_internal/shared/features/categories/bundle-pricing.js +25 -0
  55. package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -1
  56. package/dist/constants/api-endpoints.d.ts +6 -9
  57. package/dist/constants/api-endpoints.js +5 -5
  58. package/dist/constants/field-names.d.ts +17 -1
  59. package/dist/constants/field-names.js +17 -1
  60. package/dist/features/account/hooks/useNotifications.js +5 -0
  61. package/dist/features/admin/actions/admin-actions.js +6 -0
  62. package/dist/features/admin/actions/notification-actions.js +1 -2
  63. package/dist/features/admin/components/AdminBlogEditorView.js +1 -1
  64. package/dist/features/admin/components/AdminPrizeDrawsView.js +3 -3
  65. package/dist/features/admin/components/AdminSiteSettingsView.js +33 -6
  66. package/dist/features/admin/components/AdminTesterChecklistView.js +74 -22
  67. package/dist/features/admin/hooks/useAdminListing.d.ts +4 -0
  68. package/dist/features/admin/hooks/useAdminListing.js +4 -2
  69. package/dist/features/admin/schemas/firestore.d.ts +13 -5
  70. package/dist/features/admin/schemas/firestore.js +1 -2
  71. package/dist/features/auctions/actions/bid-actions.js +3 -1
  72. package/dist/features/auctions/components/AuctionBidsTable.d.ts +6 -1
  73. package/dist/features/auctions/components/AuctionBidsTable.js +8 -4
  74. package/dist/features/auctions/components/AuctionDetailPageView.js +10 -13
  75. package/dist/features/auctions/components/CollapsibleBidHistory.d.ts +23 -2
  76. package/dist/features/auctions/components/CollapsibleBidHistory.js +21 -3
  77. package/dist/features/auctions/components/LiveBidPrice.d.ts +19 -0
  78. package/dist/features/auctions/components/LiveBidPrice.js +17 -0
  79. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -1
  80. package/dist/features/auctions/components/PlaceBidFormClient.js +16 -1
  81. package/dist/features/auctions/hooks/useBids.d.ts +33 -0
  82. package/dist/features/auctions/hooks/useBids.js +32 -0
  83. package/dist/features/auctions/hooks/useLiveAuctionBid.d.ts +20 -0
  84. package/dist/features/auctions/hooks/useLiveAuctionBid.js +23 -0
  85. package/dist/features/auth/checkout-value-otp.d.ts +7 -10
  86. package/dist/features/auth/checkout-value-otp.js +14 -10
  87. package/dist/features/auth/server.d.ts +0 -1
  88. package/dist/features/auth/server.js +0 -1
  89. package/dist/features/blog/api/[slug]/route.d.ts +5 -0
  90. package/dist/features/blog/api/[slug]/route.js +12 -3
  91. package/dist/features/blog/components/BlogPostView.d.ts +2 -0
  92. package/dist/features/blog/components/BlogPostView.js +5 -4
  93. package/dist/features/blog/hooks/useBlog.d.ts +2 -0
  94. package/dist/features/blog/hooks/useBlog.js +2 -0
  95. package/dist/features/blog/repository/blog.repository.d.ts +7 -0
  96. package/dist/features/blog/repository/blog.repository.js +44 -0
  97. package/dist/features/blog/schemas/firestore.d.ts +3 -0
  98. package/dist/features/blog/schemas/firestore.js +1 -0
  99. package/dist/features/blog/types/index.d.ts +2 -0
  100. package/dist/features/cart/components/index.d.ts +0 -2
  101. package/dist/features/cart/components/index.js +0 -1
  102. package/dist/features/cart/hooks/useCartCount.js +22 -5
  103. package/dist/features/cart/hooks/useGuestCart.js +11 -1
  104. package/dist/features/cart/utils/guest-cart.d.ts +4 -0
  105. package/dist/features/cart/utils/guest-cart.js +10 -0
  106. package/dist/features/cart/utils/pending-ops.d.ts +17 -0
  107. package/dist/features/cart/utils/pending-ops.js +46 -5
  108. package/dist/features/categories/components/BrandDetailPageView.js +6 -2
  109. package/dist/features/categories/components/BundleDetailView.js +4 -2
  110. package/dist/features/categories/components/CategoryDetailPageView.js +10 -2
  111. package/dist/features/categories/schemas/firestore.d.ts +7 -0
  112. package/dist/features/checkout/actions/index.d.ts +0 -1
  113. package/dist/features/checkout/actions/index.js +0 -1
  114. package/dist/features/checkout/schemas/firestore.d.ts +2 -2
  115. package/dist/features/events/actions/event-actions.d.ts +2 -0
  116. package/dist/features/events/actions/event-actions.js +5 -0
  117. package/dist/features/events/components/EventCard.js +1 -2
  118. package/dist/features/events/components/RelatedEventsCarousel.d.ts +15 -0
  119. package/dist/features/events/components/RelatedEventsCarousel.js +13 -0
  120. package/dist/features/events/components/index.d.ts +2 -0
  121. package/dist/features/events/components/index.js +1 -0
  122. package/dist/features/events/repository/events.repository.d.ts +6 -0
  123. package/dist/features/events/repository/events.repository.js +21 -0
  124. package/dist/features/history/repository/user-history.repository.d.ts +1 -1
  125. package/dist/features/history/utils/guest-history.d.ts +1 -1
  126. package/dist/features/homepage/components/FeaturedBundlesSection.js +4 -2
  127. package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
  128. package/dist/features/homepage/components/SectionCarousel.js +1 -1
  129. package/dist/features/homepage/components/WelcomeSection.js +1 -1
  130. package/dist/features/layout/AppLayoutShell.js +1 -1
  131. package/dist/features/layout/NavbarLayout.js +1 -1
  132. package/dist/features/layout/TitleBarLayout.js +2 -2
  133. package/dist/features/media/MediaVideo.d.ts +7 -0
  134. package/dist/features/media/MediaVideo.js +36 -5
  135. package/dist/features/orders/components/OrdersList.js +1 -5
  136. package/dist/features/orders/schemas/firestore.d.ts +2 -8
  137. package/dist/features/orders/schemas/index.d.ts +3 -32
  138. package/dist/features/orders/schemas/index.js +1 -4
  139. package/dist/features/orders/types/index.d.ts +6 -9
  140. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
  141. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +2 -1
  142. package/dist/features/products/actions/product-actions.js +13 -10
  143. package/dist/features/products/components/MarketplaceBundleCard.d.ts +1 -1
  144. package/dist/features/products/components/MarketplaceBundleCard.js +7 -4
  145. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  146. package/dist/features/products/components/PrizeDrawItemsEditor.js +2 -2
  147. package/dist/features/products/components/PrizeDrawWinnerMappingView.d.ts +14 -0
  148. package/dist/features/products/components/PrizeDrawWinnerMappingView.js +37 -0
  149. package/dist/features/products/components/PrizeRevealModal.d.ts +10 -23
  150. package/dist/features/products/components/PrizeRevealModal.js +18 -147
  151. package/dist/features/products/components/ProductDetailPageView.js +72 -30
  152. package/dist/features/products/components/ProductForm.js +7 -14
  153. package/dist/features/products/components/ProductGalleryClient.d.ts +10 -1
  154. package/dist/features/products/components/ProductGalleryClient.js +29 -10
  155. package/dist/features/products/components/ProductsIndexListing.js +2 -1
  156. package/dist/features/products/components/RelatedProductsCarousel.js +2 -2
  157. package/dist/features/products/components/ShowGroupSection.js +2 -2
  158. package/dist/features/products/components/SublistingCarouselSection.js +1 -1
  159. package/dist/features/products/components/index.d.ts +4 -2
  160. package/dist/features/products/components/index.js +1 -0
  161. package/dist/features/products/constants/action-defs.d.ts +0 -1
  162. package/dist/features/products/constants/action-defs.js +0 -2
  163. package/dist/features/products/repository/products.repository.d.ts +6 -0
  164. package/dist/features/products/repository/products.repository.js +20 -1
  165. package/dist/features/products/schemas/firestore.d.ts +26 -2
  166. package/dist/features/products/schemas/index.d.ts +11 -3
  167. package/dist/features/products/schemas/index.js +10 -2
  168. package/dist/features/products/types/index.d.ts +2 -1
  169. package/dist/features/reviews/actions/review-actions.js +1 -0
  170. package/dist/features/reviews/components/ReviewDetailPageView.js +8 -1
  171. package/dist/features/reviews/components/ReviewDetailShell.js +2 -1
  172. package/dist/features/reviews/components/ReviewsList.js +2 -1
  173. package/dist/features/reviews/schemas/firestore.d.ts +3 -0
  174. package/dist/features/reviews/schemas/index.d.ts +6 -0
  175. package/dist/features/reviews/schemas/index.js +2 -0
  176. package/dist/features/reviews/types/index.d.ts +3 -0
  177. package/dist/features/scams/actions/scam-actions.d.ts +3 -0
  178. package/dist/features/scams/actions/scam-actions.js +3 -2
  179. package/dist/features/scams/components/ScamProfileView.d.ts +2 -1
  180. package/dist/features/scams/components/ScamProfileView.js +2 -2
  181. package/dist/features/scams/repository/scammer.repository.d.ts +6 -0
  182. package/dist/features/scams/repository/scammer.repository.js +24 -0
  183. package/dist/features/seller/actions/seller-actions.js +8 -0
  184. package/dist/features/seller/components/SellerPrizeDrawsView.js +4 -0
  185. package/dist/features/stores/components/StoreDetailLayoutView.js +6 -6
  186. package/dist/features/stores/components/StoreNavTabs.d.ts +10 -1
  187. package/dist/features/stores/components/StoreNavTabs.js +21 -9
  188. package/dist/features/tester/actions/index.d.ts +1 -0
  189. package/dist/features/tester/actions/index.js +1 -0
  190. package/dist/features/tester/actions/leaderboard-actions.d.ts +3 -0
  191. package/dist/features/tester/actions/leaderboard-actions.js +5 -0
  192. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +26 -4
  193. package/dist/features/tester/components/AdminTesterFeedbackListView.js +13 -0
  194. package/dist/features/tester/components/BugHunterLeaderboardView.d.ts +23 -0
  195. package/dist/features/tester/components/BugHunterLeaderboardView.js +18 -0
  196. package/dist/features/tester/components/index.d.ts +2 -0
  197. package/dist/features/tester/components/index.js +1 -0
  198. package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +12 -1
  199. package/dist/features/tester/repository/tester-checklist-item.repository.js +80 -0
  200. package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +4 -0
  201. package/dist/features/tester/repository/tester-checklist-response.repository.js +4 -0
  202. package/dist/features/tester/schemas/firestore.d.ts +22 -1
  203. package/dist/features/tester/schemas/firestore.js +8 -0
  204. package/dist/features/tester/seed-data/categories-tester-seed-data.js +42 -7
  205. package/dist/features/tester/seed-data/events-tester-seed-data.js +23 -0
  206. package/dist/features/tester/seed-data/orders-tester-seed-data.js +69 -31
  207. package/dist/features/tester/seed-data/products-tester-seed-data.js +152 -8
  208. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +249 -14
  209. package/dist/features/wishlist/hooks/useGuestWishlist.js +11 -1
  210. package/dist/features/wishlist/hooks/useWishlistCount.js +16 -2
  211. package/dist/features/wishlist/utils/guest-wishlist.d.ts +5 -0
  212. package/dist/features/wishlist/utils/guest-wishlist.js +11 -0
  213. package/dist/index.d.ts +19 -22
  214. package/dist/index.js +25 -58
  215. package/dist/jobs.d.ts +1 -1
  216. package/dist/jobs.js +2 -2
  217. package/dist/next/routing/route-map.d.ts +2 -0
  218. package/dist/next/routing/route-map.js +1 -0
  219. package/dist/schemas/registry.d.ts +6 -26
  220. package/dist/seed/bids-seed-data.js +14 -2
  221. package/dist/seed/blog-posts-seed-data.js +2 -1
  222. package/dist/seed/categories-seed-data.js +11 -2
  223. package/dist/seed/events-seed-data.js +11 -11
  224. package/dist/seed/faq-seed-data.js +5 -1
  225. package/dist/seed/products-auctions-seed-data.js +2 -2
  226. package/dist/seed/products-preorders-seed-data.d.ts +2 -1
  227. package/dist/seed/products-prize-draws-seed-data.js +56 -10
  228. package/dist/seed/products-standard-seed-data.js +82 -3
  229. package/dist/seed/scammers-seed-data.js +75 -2
  230. package/dist/seed/site-settings-seed-data.js +5 -2
  231. package/dist/server-entry.d.ts +0 -1
  232. package/dist/server-entry.js +0 -1
  233. package/dist/server.d.ts +4 -20
  234. package/dist/server.js +10 -57
  235. package/dist/styles.css +1 -1
  236. package/dist/tailwind-utilities.css +1 -1
  237. package/dist/ui/components/BaseListingCard.js +1 -1
  238. package/dist/ui/components/ImageLightbox.d.ts +9 -0
  239. package/dist/ui/components/ImageLightbox.js +6 -3
  240. package/dist/ui/components/ListingToolbar.d.ts +18 -1
  241. package/dist/ui/components/ListingToolbar.js +5 -2
  242. package/dist/ui/components/StickyToolbar.js +13 -8
  243. package/package.json +1 -1
@@ -3,8 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
4
4
  import { sortBy } from "@mohasinac/appkit";
5
5
  import React from "react";
6
- import { Pencil } from "lucide-react";
7
- import { Badge, Button, FilterChipGroup, ListingLayout, Span, Text, TextLink } from "../../../ui";
6
+ import { Pencil, ListChecks } from "lucide-react";
7
+ import { Badge, Button, FilterChipGroup, ListingLayout, Row, Span, Text, TextLink } from "../../../ui";
8
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
9
  import { ADMIN_BULK_ACTIONS, ROW_ACTION_META } from "../../products/constants/action-defs";
10
10
  import { ADMIN_PRODUCT_STATUS_TABS } from "../constants/filter-tabs";
@@ -101,7 +101,7 @@ export function AdminPrizeDrawsView({ children, ...props }) {
101
101
  destructive: ROW_ACTION_META[id].destructive,
102
102
  onClick: () => selection.clearSelection(),
103
103
  })),
104
- renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsx(TextLink, { href: String(ROUTES.ADMIN.PRIZE_DRAWS_EDIT(row.id)), "aria-label": "Edit", children: _jsx(Pencil, { className: "w-4 h-4" }) }) })),
104
+ renderRowActions: (row) => (_jsxs(Row, { gap: "xs", children: [_jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsx(TextLink, { href: String(ROUTES.ADMIN.PRIZE_DRAWS_EDIT(row.id)), "aria-label": "Edit", children: _jsx(Pencil, { className: "w-4 h-4" }) }) }), _jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsx(TextLink, { href: String(ROUTES.ADMIN.PRIZE_DRAWS_ENTRIES(row.id)), "aria-label": "View entries", children: _jsx(ListChecks, { className: "w-4 h-4" }) }) })] })),
105
105
  renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_PRODUCT_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })),
106
106
  };
107
107
  return _jsx(DataListingView, { config: config });
@@ -102,8 +102,15 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
102
102
  const [watermarkType, setWatermarkType] = React.useState("text");
103
103
  const [watermarkText, setWatermarkText] = React.useState("letitrip.in");
104
104
  const [watermarkImageUrl, setWatermarkImageUrl] = React.useState("");
105
- const [watermarkSize, setWatermarkSize] = React.useState(30);
106
- const [watermarkOpacity, setWatermarkOpacity] = React.useState(20);
105
+ const [watermarkSize, setWatermarkSize] = React.useState(10);
106
+ const [watermarkOpacity, setWatermarkOpacity] = React.useState(10);
107
+ // 4 corners + center are picked from the Select; "custom" is only ever set
108
+ // by the offset toggle below, never a 6th Select option (keeps the picker
109
+ // at 5 options — see PaginatedSelect rule for >5).
110
+ const [watermarkPosition, setWatermarkPosition] = React.useState("center");
111
+ const [watermarkUseCustomOffset, setWatermarkUseCustomOffset] = React.useState(false);
112
+ const [watermarkOffsetX, setWatermarkOffsetX] = React.useState(0);
113
+ const [watermarkOffsetY, setWatermarkOffsetY] = React.useState(0);
107
114
  const watermarkPreviewRef = React.useRef(null);
108
115
  React.useEffect(() => {
109
116
  const el = watermarkPreviewRef.current;
@@ -300,8 +307,13 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
300
307
  setWatermarkType(s.watermark?.type ?? "text");
301
308
  setWatermarkText(s.watermark?.text ?? "letitrip.in");
302
309
  setWatermarkImageUrl(s.watermark?.imageUrl ?? "");
303
- setWatermarkSize(s.watermark?.size ?? 30);
304
- setWatermarkOpacity(s.watermark?.opacity ?? 20);
310
+ setWatermarkSize(s.watermark?.size ?? 10);
311
+ setWatermarkOpacity(s.watermark?.opacity ?? 10);
312
+ const loadedPosition = s.watermark?.position ?? "center";
313
+ setWatermarkUseCustomOffset(loadedPosition === "custom");
314
+ setWatermarkPosition(loadedPosition === "custom" ? "center" : loadedPosition);
315
+ setWatermarkOffsetX(s.watermark?.offsetX ?? 0);
316
+ setWatermarkOffsetY(s.watermark?.offsetY ?? 0);
305
317
  setPlatformFeePercent(s.commissions?.platformFeePercent ?? 5);
306
318
  setGstPercent(s.commissions?.gstPercent ?? 18);
307
319
  setMinimumTransactionFee(s.commissions?.minimumTransactionFee ?? 0);
@@ -485,7 +497,16 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
485
497
  seo: { defaultTitle: seoTitle, defaultDescription: seoDescription, defaultOgImage: seoOgImage, noIndex: seoNoIndex, canonicalBaseUrl: canonicalUrl },
486
498
  contact: { email: supportEmail, phone: supportPhone, address: supportAddress, supportHours, whatsappNumber: whatsapp },
487
499
  socialLinks: { instagram, twitter, facebook, youtube, linkedin, pinterest },
488
- watermark: { type: watermarkType, text: watermarkText, imageUrl: watermarkImageUrl, size: watermarkSize, opacity: watermarkOpacity },
500
+ watermark: {
501
+ type: watermarkType,
502
+ text: watermarkText,
503
+ imageUrl: watermarkImageUrl,
504
+ size: watermarkSize,
505
+ opacity: watermarkOpacity,
506
+ position: watermarkUseCustomOffset ? "custom" : watermarkPosition,
507
+ offsetX: watermarkOffsetX,
508
+ offsetY: watermarkOffsetY,
509
+ },
489
510
  commissions: { platformFeePercent, gstPercent, minimumTransactionFee, gatewayFeePercent, payoutHoldDays, minPayoutAmount, auctionListingFee, preOrderListingFee, featuredSlotFee, promotedSlotFee, whatsappNotifyFeeEnabled, whatsappNotifyFee, giftWrapFeeEnabled, giftWrapFee, shipmentProtectionFeeEnabled, shipmentProtectionFeePercent, shipmentProtectionFeeMin, codDepositPercent, sellerShippingFixed, platformShippingPercent, platformShippingFixedMin },
490
511
  laborRate: { hourlyRate: laborHourlyRate, maxHoursPerDay: laborMaxHoursPerDay },
491
512
  emi: {
@@ -621,7 +642,13 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
621
642
  } }) })] })] })] }) }), _jsx(TabsContent, { value: "themes", children: _jsx(Form, { onSubmit: (e) => {
622
643
  e.preventDefault();
623
644
  saveAllMutation.mutate();
624
- }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: _jsx(ThemeManagerView, { value: themeRegistry, onChange: setThemeRegistry, previewOrigin: "/" }) }) }), _jsx(TabsContent, { value: "announcement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Toggle, { label: "Show announcement bar", checked: announcementEnabled, onChange: setAnnouncementEnabled }), _jsx(Input, { label: "Announcement text", value: announcementText, onChange: (e) => setAnnouncementText(e.target.value), placeholder: "\uD83C\uDF89 Free shipping on orders \u20B9999+", disabled: !announcementEnabled }), _jsx(Input, { label: "Link URL (optional)", value: announcementLink, onChange: (e) => setAnnouncementLink(e.target.value), placeholder: String(ROUTES.PUBLIC.PRODUCTS), disabled: !announcementEnabled }), _jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Background color" }), _jsx(Input, { type: "color", value: announcementBg || "#1d4ed8", onChange: (e) => setAnnouncementBg(e.target.value), className: "h-10 w-32 cursor-pointer", bare: true, disabled: !announcementEnabled }), " "] })] }) }), _jsx(TabsContent, { value: "seo", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Input, { label: "Default meta title", value: seoTitle, onChange: (e) => setSeoTitle(e.target.value), placeholder: "LetItRip \u2014 Buy, Sell & Auction Collectibles in India", maxLength: 60, helperText: "Max 60 chars. Use {page} token for dynamic insertion." }), _jsx(Input, { label: "Default meta description", value: seoDescription, onChange: (e) => setSeoDescription(e.target.value), placeholder: "India's largest collectibles marketplace\u2026", maxLength: 160, helperText: "Max 160 chars." }), _jsx(ImageUpload, { label: "Default OG image", currentImage: seoOgImage, onUpload: (file) => upload(file, "store"), onChange: setSeoOgImage }), _jsx(Input, { label: "Canonical base URL", value: canonicalUrl, onChange: (e) => setCanonicalUrl(e.target.value), placeholder: "https://letitrip.in" }), _jsx(Toggle, { label: "Robots noindex (disables search indexing \u2014 use carefully)", checked: seoNoIndex, onChange: setSeoNoIndex })] }) }), _jsx(TabsContent, { value: "contact", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Support email", value: supportEmail, onChange: (e) => setSupportEmail(e.target.value), type: "email", placeholder: "support@letitrip.in" }), _jsx(Input, { label: "Support phone", value: supportPhone, onChange: (e) => setSupportPhone(e.target.value), placeholder: "+91 XXXXX XXXXX" })] }), _jsx(Input, { label: "Physical address", value: supportAddress, onChange: (e) => setSupportAddress(e.target.value), placeholder: "Mumbai, Maharashtra, India" }), _jsx(Input, { label: "Support hours", value: supportHours, onChange: (e) => setSupportHours(e.target.value), placeholder: "Mon\u2013Fri, 10 AM \u2013 6 PM IST" }), _jsx(Text, { className: "pt-[0.5rem]", color: "muted", size: "sm", weight: "medium", children: "Social links" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Instagram URL", value: instagram, onChange: (e) => setInstagram(e.target.value), placeholder: "https://instagram.com/letitrip" }), _jsx(Input, { label: "Twitter / X URL", value: twitter, onChange: (e) => setTwitter(e.target.value), placeholder: "https://twitter.com/letitrip" }), _jsx(Input, { label: "Facebook URL", value: facebook, onChange: (e) => setFacebook(e.target.value), placeholder: "https://facebook.com/letitrip" }), _jsx(Input, { label: "YouTube URL", value: youtube, onChange: (e) => setYoutube(e.target.value), placeholder: "https://youtube.com/@letitrip" }), _jsx(Input, { label: "WhatsApp number", value: whatsapp, onChange: (e) => setWhatsapp(e.target.value), placeholder: "+91XXXXXXXXXX" }), _jsx(Input, { label: "LinkedIn URL", value: linkedin, onChange: (e) => setLinkedin(e.target.value), placeholder: "https://linkedin.com/company/letitrip" }), _jsx(Input, { label: "Pinterest URL", value: pinterest, onChange: (e) => setPinterest(e.target.value), placeholder: "https://pinterest.com/letitrip" })] })] }) }), _jsx(TabsContent, { value: "watermark", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Select, { label: "Watermark type", options: [{ label: "Text", value: "text" }, { label: "Image", value: "image" }], value: watermarkType, onValueChange: (v) => setWatermarkType(v) }), watermarkType === "text" ? (_jsx(Input, { label: "Watermark text", value: watermarkText, onChange: (e) => setWatermarkText(e.target.value), placeholder: "letitrip.in" })) : (_jsx(ImageUpload, { label: "Watermark image", currentImage: watermarkImageUrl, onUpload: (file) => upload(file, "store"), onChange: setWatermarkImageUrl })), _jsx(Slider, { label: `Size — ${watermarkSize}% of image width`, value: watermarkSize, onChange: setWatermarkSize, min: 5, max: 100, step: 5 }), _jsx(Slider, { label: `Opacity — ${watermarkOpacity}%`, value: watermarkOpacity, onChange: setWatermarkOpacity, min: 5, max: 100, step: 5 }), _jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Preview (text watermark only)" }), _jsx(Row, { surface: "default", justify: "end", align: "end", className: `relative h-32 ${__O.hidden}`, rounded: "default", children: _jsx(Span, { ref: watermarkPreviewRef, weight: "medium", color: "faint", padding: "1", className: "select-none", children: watermarkText }) })] })] }) }), _jsx(TabsContent, { value: "fees", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Platform fee \u2014 our cut (%)", helperText: "% charged on order value. Buyer pays this.", value: String(platformFeePercent), onChange: (e) => setPlatformFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "GST on platform fee (%)", helperText: "Applied to our fee only (not full order). Usually 18%.", value: String(gstPercent), onChange: (e) => setGstPercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "Razorpay gateway cost (%)", helperText: "Gateway's own fee \u2014 absorbed by platform, not passed through.", value: String(gatewayFeePercent), onChange: (e) => setGatewayFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 10, step: 0.01 }), _jsx(Input, { label: "Minimum transaction fee (\u20B9)", helperText: "Per-transaction floor. Total charge will never be below base + this.", value: String(minimumTransactionFee), onChange: (e) => setMinimumTransactionFee(parseFloat(e.target.value) || 0), type: "number", min: 0, step: 0.01 }), _jsx(Input, { label: "Seller payout hold (days)", value: String(payoutHoldDays), onChange: (e) => setPayoutHoldDays(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Minimum payout amount (\u20B9)", value: String(minPayoutAmount), onChange: (e) => setMinPayoutAmount(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Auction listing fee (\u20B9)", value: String(auctionListingFee), onChange: (e) => setAuctionListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Pre-order listing fee (\u20B9)", value: String(preOrderListingFee), onChange: (e) => setPreOrderListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Featured slot fee (\u20B9)", value: String(featuredSlotFee), onChange: (e) => setFeaturedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Promoted slot fee (\u20B9)", value: String(promotedSlotFee), onChange: (e) => setPromotedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "COD deposit (%)", helperText: "% of order collected upfront on COD orders; remainder paid on delivery.", value: String(codDepositPercent), onChange: (e) => setCodDepositPercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 })] }), _jsx(Toggle, { checked: whatsappNotifyFeeEnabled, onChange: setWhatsappNotifyFeeEnabled, label: "Offer the WhatsApp order-updates addon at checkout" }), _jsx(Grid, { cols: 2, gap: "md", children: _jsx(Input, { label: "WhatsApp addon fee (\u20B9)", helperText: "Flat fee charged to the buyer when they opt in. Requires WhatsApp credentials + at least one approved template \u2014 see the WhatsApp tab.", value: String(whatsappNotifyFee), onChange: (e) => setWhatsappNotifyFee(parseFloat(e.target.value) || 0), type: "number", min: 0, disabled: !whatsappNotifyFeeEnabled }) }), _jsx(Toggle, { checked: giftWrapFeeEnabled, onChange: setGiftWrapFeeEnabled, label: "Offer the gift-wrap addon at checkout" }), _jsx(Grid, { cols: 2, gap: "md", children: _jsx(Input, { label: "Gift wrap fee (\u20B9)", helperText: "Flat fee charged to the buyer when they opt in.", value: String(giftWrapFee), onChange: (e) => setGiftWrapFee(parseFloat(e.target.value) || 0), type: "number", min: 0, disabled: !giftWrapFeeEnabled }) }), _jsx(Toggle, { checked: shipmentProtectionFeeEnabled, onChange: setShipmentProtectionFeeEnabled, label: "Offer the shipment-protection addon at checkout" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Shipment protection (%)", helperText: "% of order subtotal, charged when the buyer opts in.", value: String(shipmentProtectionFeePercent), onChange: (e) => setShipmentProtectionFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1, disabled: !shipmentProtectionFeeEnabled }), _jsx(Input, { label: "Shipment protection minimum (\u20B9)", helperText: "Floor for the shipment-protection fee.", value: String(shipmentProtectionFeeMin), onChange: (e) => setShipmentProtectionFeeMin(parseFloat(e.target.value) || 0), type: "number", min: 0, disabled: !shipmentProtectionFeeEnabled })] })] }) }), _jsx(TabsContent, { value: "procurement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Used to compute each procurement shipment's labor cost (hours spent \u00D7 hourly rate) and its estimated processing time (hours spent \u00F7 max hours/day)." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Hourly rate (\u20B9)", helperText: "Your effective hourly wage for sorting/categorizing/listing a shipment.", value: String(laborHourlyRate), onChange: (e) => setLaborHourlyRate(Math.round((parseFloat(e.target.value) || 0) * 100) / 100), type: "number", min: 0 }), _jsx(Input, { label: "Max hours per day", helperText: "Used only to project how many days a shipment's tracked hours will take.", value: String(laborMaxHoursPerDay), onChange: (e) => setLaborMaxHoursPerDay(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 24 })] })] }) }), _jsx(TabsContent, { value: "emi", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "A seller must also opt in via their own Payout Settings for EMI to appear at checkout on their items. See the \"How EMI Works\" public page for buyer-facing copy." }), _jsx(Toggle, { checked: emiEnabled, onChange: setEmiEnabled, label: "Enable EMI platform-wide" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Minimum order value (\u20B9)", helperText: "A seller's cart subtotal must exceed this for EMI to appear as an option.", value: String(emiMinOrderValue), onChange: (e) => setEmiMinOrderValue(Math.round((parseFloat(e.target.value) || 0) * 100) / 100), type: "number", min: 0 }), _jsx(Input, { label: "Tenure options (months, comma-separated)", helperText: "e.g. 2,3,4,5,6", value: emiTenureOptionsText, onChange: (e) => setEmiTenureOptionsText(e.target.value) }), _jsx(Input, { label: "Token / down-payment (%)", helperText: "Collected upfront at checkout.", value: String(emiTokenPercent), onChange: (e) => setEmiTokenPercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100 }), _jsx(Input, { label: "Billing day (1\u201310)", helperText: "Day of month each installment is due.", value: String(emiBillingDay), onChange: (e) => setEmiBillingDay(parseFloat(e.target.value) || 1), type: "number", min: 1, max: 10 }), _jsx(Input, { label: "Surcharge (% of principal per month)", helperText: "The 'excess EMI fee' the buyer pays for spreading payment.", value: String(emiSurchargePercentPerMonth), onChange: (e) => setEmiSurchargePercentPerMonth(parseFloat(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Seller's share of surcharge (%)", helperText: "The rest of the surcharge goes to the platform.", value: String(emiSurchargeSellerSharePercent), onChange: (e) => setEmiSurchargeSellerSharePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100 })] })] }) }), _jsx(TabsContent, { value: "gst", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Required before enabling GST-inclusive checkout (P-8). Also set a GST rate + HSN code on each taxable product for the tax breakdown to appear." }), _jsx(Toggle, { checked: gstEnabled, onChange: setGstEnabled, label: "Enable GST on checkout" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "GSTIN", helperText: "15-character GST Identification Number.", value: gstin, onChange: (e) => setGstin(e.target.value), placeholder: "29AAAAA0000A1Z5", maxLength: 15 }), _jsx(Input, { label: "Legal name", helperText: "Registered business name for invoices.", value: gstLegalName, onChange: (e) => setGstLegalName(e.target.value), placeholder: "LetItRip Collectibles Pvt Ltd" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Registered address" }), _jsx(Textarea, { value: gstAddress, onChange: (e) => setGstAddress(e.target.value), placeholder: "Registered business address for GST invoices\u2026", rows: 3 })] })] }) }), _jsx(TabsContent, { value: "featureflags", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "lg", children: [_jsx(Text, { size: "xs", color: "muted", children: "Site-wide platform feature toggles. Disabling a flag hides that feature's nav entries and pages \u2014 existing data is untouched, it just becomes unreachable until re-enabled." }), _jsx(Grid, { cols: 2, gap: "md", children: FEATURE_FLAG_META.map((flag) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Toggle, { label: `${flag.icon} ${flag.labelKey}`, checked: featureFlags[flag.key] ?? false, onChange: (v) => setFeatureFlags((prev) => ({ ...prev, [flag.key]: v })) }), _jsx(Text, { size: "xs", color: "muted", children: flag.descKey })] }, flag.key))) }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Listing types" }), _jsx(Text, { size: "xs", color: "muted", children: "Disabled types are hidden from listings and reject create/add-to-cart." }), _jsx(Grid, { cols: 2, gap: "sm", children: LISTING_TYPE_KEYS.map((key) => (_jsx(Toggle, { label: key, checked: listingTypeFlags[key] ?? true, onChange: (v) => setListingTypeFlags((prev) => ({ ...prev, [key]: v })) }, key))) })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Category types" }), _jsx(Grid, { cols: 2, gap: "sm", children: CATEGORY_TYPE_KEYS.map((key) => (_jsx(Toggle, { label: key, checked: categoryTypeFlags[key] ?? true, onChange: (v) => setCategoryTypeFlags((prev) => ({ ...prev, [key]: v })) }, key))) })] })] }) }), _jsx(TabsContent, { value: "integrations", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Keys are masked in transit and stored encrypted. Click Reveal to view." }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Razorpay" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Razorpay Key ID", value: razorpayKeyId, onChange: setRazorpayKeyId, placeholder: "rzp_live_\u2026" }), _jsx(MaskedInput, { label: "Razorpay Secret", value: razorpaySecret, onChange: setRazorpaySecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "SMTP / Email" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "SMTP host", value: smtpHost, onChange: (e) => setSmtpHost(e.target.value), placeholder: "smtp.sendgrid.net" }), _jsx(Input, { label: "SMTP port", value: smtpPort, onChange: (e) => setSmtpPort(e.target.value), type: "number", placeholder: "587" }), _jsx(Input, { label: "SMTP user", value: smtpUser, onChange: (e) => setSmtpUser(e.target.value), placeholder: "apikey" }), _jsx(MaskedInput, { label: "SMTP password", value: smtpPassword, onChange: setSmtpPassword, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsx(Input, { label: "From address", value: smtpFrom, onChange: (e) => setSmtpFrom(e.target.value), placeholder: "noreply@letitrip.in", type: "email" })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Analytics & Tracking" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Google Analytics ID", value: gaMeasurementId, onChange: (e) => setGaMeasurementId(e.target.value), placeholder: "G-XXXXXXXXXX" }), _jsx(Input, { label: "Facebook Pixel ID", value: fbPixelId, onChange: (e) => setFbPixelId(e.target.value), placeholder: "XXXXXXXXXXXXXXXX" }), _jsx(Input, { label: "GTM Container ID", value: gtmContainerId, onChange: (e) => setGtmContainerId(e.target.value), placeholder: "GTM-XXXXXXX" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Meta (Instagram & Facebook Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Used by the Social Feed section to fetch Instagram and Facebook posts via Meta Graph API v19." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Page Access Token", value: metaPageAccessToken, onChange: setMetaPageAccessToken, placeholder: "EAAxxxxxxx\u2026" }), _jsx(Input, { label: "Page ID (or handle)", value: metaPageId, onChange: (e) => setMetaPageId(e.target.value), placeholder: "letitrip" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "TikTok for Developers (Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Client credentials + long-lived access token from TikTok for Developers. Used to list your account's public videos." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Client Key", value: tiktokClientKey, onChange: setTiktokClientKey, placeholder: "aw\u2026" }), _jsx(MaskedInput, { label: "Client Secret", value: tiktokClientSecret, onChange: setTiktokClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" }), _jsx(MaskedInput, { label: "Access Token (long-lived)", value: tiktokAccessToken, onChange: setTiktokAccessToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "DeviantArt OAuth2 (Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Client credentials for DeviantArt gallery fetching (client-credentials OAuth2 flow \u2014 no user login required)." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Client ID", value: deviantartClientId, onChange: setDeviantartClientId, placeholder: "1234" }), _jsx(MaskedInput, { label: "Client Secret", value: deviantartClientSecret, onChange: setDeviantartClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] })] }) }), _jsx(TabsContent, { value: "shipping", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Payment methods" }), _jsx(Toggle, { label: "Razorpay (online card/UPI) enabled \u2014 disabled by default, manual payment is the default", checked: razorpayEnabled, onChange: setRazorpayEnabled }), _jsx(Toggle, { label: "Manual UPI/bank transfer enabled", checked: upiManualEnabled, onChange: setUpiManualEnabled }), _jsx(Toggle, { label: "Cash on delivery (COD) enabled", checked: codEnabled, onChange: setCodEnabled }), _jsx(Input, { label: "Free shipping threshold (\u20B9)", value: String(freeShippingThreshold), onChange: (e) => setFreeShippingThreshold(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Orders above this amount get free shipping." }), _jsx(Select, { label: "Default carrier", options: CARRIER_OPTIONS, value: defaultCarrier, onValueChange: setDefaultCarrier }), _jsx(Input, { label: "Max delivery radius (km, 0 = no limit)", value: String(maxDeliveryRadius), onChange: (e) => setMaxDeliveryRadius(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "High-value checkout OTP threshold (\u20B9)", helperText: "Carts at or above this subtotal require an extra OTP verification step before payment (0 = disabled).", value: String(otpCheckoutThreshold), onChange: (e) => setOtpCheckoutThreshold(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "pt-[var(--appkit-space-2)]", children: "Seller shipping charge defaults" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Seller shipping fixed (\u20B9)", helperText: "Flat shipping charge collected on the seller's behalf.", value: String(sellerShippingFixed), onChange: (e) => setSellerShippingFixed(parseFloat(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Platform shipping (%)", helperText: "Platform's own shipping markup, as a % of order value.", value: String(platformShippingPercent), onChange: (e) => setPlatformShippingPercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "Platform shipping minimum (\u20B9)", helperText: "Floor for the platform shipping markup.", value: String(platformShippingFixedMin), onChange: (e) => setPlatformShippingFixedMin(parseFloat(e.target.value) || 0), type: "number", min: 0 })] })] }) }), _jsx(TabsContent, { value: "auction", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Input, { label: "Minimum bid increment (\u20B9)", value: String(minBidIncrement), onChange: (e) => setMinBidIncrement(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Global default \u2014 individual auctions may override." }), _jsx(Input, { label: "Auto-extend window (minutes before end)", value: String(autoExtendWindow), onChange: (e) => setAutoExtendWindow(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Extend auction end time if a bid arrives within this window." }), _jsx(Input, { label: "Settlement grace period (hours)", value: String(settlementGrace), onChange: (e) => setSettlementGrace(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Time winner has to pay before the auction is re-listed." })] }) }), _jsx(TabsContent, { value: "limits", children: _jsx(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Max products per store", value: String(maxProductsPerStore), onChange: (e) => setMaxProductsPerStore(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max images per product", value: String(maxImagesPerProduct), onChange: (e) => setMaxImagesPerProduct(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max video size (MB)", value: String(maxVideoSizeMb), onChange: (e) => setMaxVideoSizeMb(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max custom fields per product", value: String(maxCustomFields), onChange: (e) => setMaxCustomFields(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Max custom sections per product", value: String(maxCustomSections), onChange: (e) => setMaxCustomSections(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Order cancellation window (hours)", value: String(orderCancelWindow), onChange: (e) => setOrderCancelWindow(parseInt(e.target.value) || 0), type: "number", min: 0 })] }) }) }), _jsx(TabsContent, { value: "whatsapp", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Platform-level WhatsApp Business Cloud API credentials. Used for automated purchase announcements to admin numbers when orders are placed. Store owners configure their own credentials in Store \u2192 WhatsApp." }), _jsx(Input, { label: "Phone Number ID", value: waPhoneNumberId, onChange: (e) => setWaPhoneNumberId(e.target.value), placeholder: "987654321098765", helperText: "From Meta for Developers \u2192 App \u2192 WhatsApp \u2192 API Setup" }), _jsx(MaskedInput, { label: "Cloud API System User Token", value: waCloudApiToken, onChange: setWaCloudApiToken, placeholder: "EAAxxxxxxxx\u2026", helperText: "Long-lived system user access token with WhatsApp Business permissions" }), _jsx(Input, { label: "Admin Notify Numbers", value: waAdminNotifyNumbers, onChange: (e) => setWaAdminNotifyNumbers(e.target.value), placeholder: "919876543210,918765432109", helperText: "Comma-separated, digits-only, include country code. These receive a WhatsApp message when any order is placed." }), _jsx(Text, { size: "xs", color: "muted", children: "Approved Meta message templates for the buyer-facing WhatsApp order-updates addon (Fees tab). Meta rejects business-initiated free-text WhatsApp messages outside a 24h customer-service window \u2014 a real template name here is required for that notification type to actually deliver. Leave blank to skip WhatsApp for that type (in-app + email still fire)." }), _jsx(Input, { label: "Template language code", value: waTemplateLanguage, onChange: (e) => setWaTemplateLanguage(e.target.value), placeholder: "en", helperText: "BCP-47 code the templates below were approved in, e.g. en or en_US." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Order placed template name", value: waTemplateOrderPlaced, onChange: (e) => setWaTemplateOrderPlaced(e.target.value), placeholder: "order_placed_update" }), _jsx(Input, { label: "Order confirmed template name", value: waTemplateOrderConfirmed, onChange: (e) => setWaTemplateOrderConfirmed(e.target.value), placeholder: "order_confirmed_update" }), _jsx(Input, { label: "Order shipped template name", value: waTemplateOrderShipped, onChange: (e) => setWaTemplateOrderShipped(e.target.value), placeholder: "order_shipped_update" }), _jsx(Input, { label: "Order delivered template name", value: waTemplateOrderDelivered, onChange: (e) => setWaTemplateOrderDelivered(e.target.value), placeholder: "order_delivered_update" }), _jsx(Input, { label: "Order cancelled template name", value: waTemplateOrderCancelled, onChange: (e) => setWaTemplateOrderCancelled(e.target.value), placeholder: "order_cancelled_update" }), _jsx(Input, { label: "Refund initiated template name", value: waTemplateRefundInitiated, onChange: (e) => setWaTemplateRefundInitiated(e.target.value), placeholder: "refund_initiated_update" })] })] }) }), _jsx(TabsContent, { value: "notifications", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "lg", children: [_jsx(Text, { size: "xs", color: "muted", children: "In-app notifications are always on. Enable external channels below to let the platform fan out to email, WhatsApp, or SMS. Users can further restrict which types they receive." }), _jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "md", children: [_jsxs(Row, { justify: "between", gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "In-app (notification bell)" }), _jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "semibold", rounded: "full", padding: "pill-xs", children: "Always on" })] }), _jsx(Text, { size: "xs", color: "muted", children: "Displayed in the notification bell and inbox. Cannot be disabled." })] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "Email notifications", checked: notifEmailEnabled, onChange: setNotifEmailEnabled }), notifEmailEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send email", options: PRIORITY_OPTIONS, value: notifEmailMinPriority, onValueChange: setNotifEmailMinPriority }), _jsx(Text, { size: "xs", weight: "medium", color: "muted", className: "pt-[0.25rem]", children: "Resend API (for transactional email)" }), _jsx(MaskedInput, { label: "Resend API Key", value: resendApiKey, onChange: setResendApiKey, placeholder: "re_live_\u2026", helperText: "Get your key at resend.com \u2014 used for all transactional notifications." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "From email", value: notifFromEmail, onChange: (e) => setNotifFromEmail(e.target.value), placeholder: "noreply@letitrip.in", type: "email" }), _jsx(Input, { label: "From name", value: notifFromName, onChange: (e) => setNotifFromName(e.target.value), placeholder: "LetItRip" })] }), _jsx(Text, { size: "xs", weight: "medium", color: "muted", className: "pt-[0.25rem]", children: "Notification types (leave empty to send all types)" }), _jsx(PaginatedSelect, { multiple: true, options: NOTIF_TYPE_OPTIONS, value: notifEmailTypes, onChange: (values) => setNotifEmailTypes(values), placeholder: "All notification types" })] }))] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "WhatsApp notifications", checked: notifWhatsappEnabled, onChange: setNotifWhatsappEnabled }), notifWhatsappEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send WhatsApp", options: PRIORITY_OPTIONS, value: notifWhatsappMinPriority, onValueChange: setNotifWhatsappMinPriority }), _jsx(Toggle, { label: "Enable WhatsApp OTP (for login and verification)", checked: notifWhatsappOtpEnabled, onChange: setNotifWhatsappOtpEnabled }), _jsx(Text, { size: "xs", color: "muted", children: "WhatsApp credentials are configured in the WhatsApp tab (\u246C). OTP messages use the same phone number." }), _jsx(Text, { size: "xs", weight: "medium", color: "muted", className: "pt-[0.25rem]", children: "Notification types (leave empty to send all types)" }), _jsx(PaginatedSelect, { multiple: true, options: NOTIF_TYPE_OPTIONS, value: notifWhatsappTypes, onChange: (values) => setNotifWhatsappTypes(values), placeholder: "All notification types" })] }))] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "SMS notifications", checked: notifSmsEnabled, onChange: setNotifSmsEnabled }), notifSmsEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send SMS", options: PRIORITY_OPTIONS, value: notifSmsMinPriority, onValueChange: setNotifSmsMinPriority }), _jsx(Text, { size: "xs", color: "muted", children: "SMS gateway credentials (e.g. Twilio, MSG91) can be configured in the Integrations tab once an SMS provider is connected." })] }))] })] }) }), _jsx(TabsContent, { value: "legal", children: _jsx(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [
645
+ }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: _jsx(ThemeManagerView, { value: themeRegistry, onChange: setThemeRegistry, previewOrigin: "/" }) }) }), _jsx(TabsContent, { value: "announcement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Toggle, { label: "Show announcement bar", checked: announcementEnabled, onChange: setAnnouncementEnabled }), _jsx(Input, { label: "Announcement text", value: announcementText, onChange: (e) => setAnnouncementText(e.target.value), placeholder: "\uD83C\uDF89 Free shipping on orders \u20B9999+", disabled: !announcementEnabled }), _jsx(Input, { label: "Link URL (optional)", value: announcementLink, onChange: (e) => setAnnouncementLink(e.target.value), placeholder: String(ROUTES.PUBLIC.PRODUCTS), disabled: !announcementEnabled }), _jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Background color" }), _jsx(Input, { type: "color", value: announcementBg || "#1d4ed8", onChange: (e) => setAnnouncementBg(e.target.value), className: "h-10 w-32 cursor-pointer", bare: true, disabled: !announcementEnabled }), " "] })] }) }), _jsx(TabsContent, { value: "seo", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Input, { label: "Default meta title", value: seoTitle, onChange: (e) => setSeoTitle(e.target.value), placeholder: "LetItRip \u2014 Buy, Sell & Auction Collectibles in India", maxLength: 60, helperText: "Max 60 chars. Use {page} token for dynamic insertion." }), _jsx(Input, { label: "Default meta description", value: seoDescription, onChange: (e) => setSeoDescription(e.target.value), placeholder: "India's largest collectibles marketplace\u2026", maxLength: 160, helperText: "Max 160 chars." }), _jsx(ImageUpload, { label: "Default OG image", currentImage: seoOgImage, onUpload: (file) => upload(file, "store"), onChange: setSeoOgImage }), _jsx(Input, { label: "Canonical base URL", value: canonicalUrl, onChange: (e) => setCanonicalUrl(e.target.value), placeholder: "https://letitrip.in" }), _jsx(Toggle, { label: "Robots noindex (disables search indexing \u2014 use carefully)", checked: seoNoIndex, onChange: setSeoNoIndex })] }) }), _jsx(TabsContent, { value: "contact", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Support email", value: supportEmail, onChange: (e) => setSupportEmail(e.target.value), type: "email", placeholder: "support@letitrip.in" }), _jsx(Input, { label: "Support phone", value: supportPhone, onChange: (e) => setSupportPhone(e.target.value), placeholder: "+91 XXXXX XXXXX" })] }), _jsx(Input, { label: "Physical address", value: supportAddress, onChange: (e) => setSupportAddress(e.target.value), placeholder: "Mumbai, Maharashtra, India" }), _jsx(Input, { label: "Support hours", value: supportHours, onChange: (e) => setSupportHours(e.target.value), placeholder: "Mon\u2013Fri, 10 AM \u2013 6 PM IST" }), _jsx(Text, { className: "pt-[0.5rem]", color: "muted", size: "sm", weight: "medium", children: "Social links" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Instagram URL", value: instagram, onChange: (e) => setInstagram(e.target.value), placeholder: "https://instagram.com/letitrip" }), _jsx(Input, { label: "Twitter / X URL", value: twitter, onChange: (e) => setTwitter(e.target.value), placeholder: "https://twitter.com/letitrip" }), _jsx(Input, { label: "Facebook URL", value: facebook, onChange: (e) => setFacebook(e.target.value), placeholder: "https://facebook.com/letitrip" }), _jsx(Input, { label: "YouTube URL", value: youtube, onChange: (e) => setYoutube(e.target.value), placeholder: "https://youtube.com/@letitrip" }), _jsx(Input, { label: "WhatsApp number", value: whatsapp, onChange: (e) => setWhatsapp(e.target.value), placeholder: "+91XXXXXXXXXX" }), _jsx(Input, { label: "LinkedIn URL", value: linkedin, onChange: (e) => setLinkedin(e.target.value), placeholder: "https://linkedin.com/company/letitrip" }), _jsx(Input, { label: "Pinterest URL", value: pinterest, onChange: (e) => setPinterest(e.target.value), placeholder: "https://pinterest.com/letitrip" })] })] }) }), _jsx(TabsContent, { value: "watermark", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Select, { label: "Watermark type", options: [{ label: "Text", value: "text" }, { label: "Image", value: "image" }], value: watermarkType, onValueChange: (v) => setWatermarkType(v) }), watermarkType === "text" ? (_jsx(Input, { label: "Watermark text", value: watermarkText, onChange: (e) => setWatermarkText(e.target.value), placeholder: "letitrip.in" })) : (_jsx(ImageUpload, { label: "Watermark image", currentImage: watermarkImageUrl, onUpload: (file) => upload(file, "store"), onChange: setWatermarkImageUrl })), _jsx(Slider, { label: `Size — ${watermarkSize}% of image width`, value: watermarkSize, onChange: setWatermarkSize, min: 5, max: 100, step: 5 }), _jsx(Slider, { label: `Opacity — ${watermarkOpacity}%`, value: watermarkOpacity, onChange: setWatermarkOpacity, min: 5, max: 100, step: 5 }), _jsx(Select, { label: "Position", options: [
646
+ { label: "Center", value: "center" },
647
+ { label: "Top left", value: "top-left" },
648
+ { label: "Top right", value: "top-right" },
649
+ { label: "Bottom left", value: "bottom-left" },
650
+ { label: "Bottom right", value: "bottom-right" },
651
+ ], value: watermarkPosition, onValueChange: (v) => setWatermarkPosition(v), disabled: watermarkUseCustomOffset }), _jsx(Toggle, { checked: watermarkUseCustomOffset, onChange: setWatermarkUseCustomOffset, label: "Use a custom X/Y offset instead" }), watermarkUseCustomOffset && (_jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "X offset (%) \u2014 + right / \u2212 left of center", value: String(watermarkOffsetX), onChange: (e) => setWatermarkOffsetX(Math.max(-45, Math.min(45, parseInt(e.target.value) || 0))), type: "number", min: -45, max: 45 }), _jsx(Input, { label: "Y offset (%) \u2014 + down / \u2212 up from center", value: String(watermarkOffsetY), onChange: (e) => setWatermarkOffsetY(Math.max(-45, Math.min(45, parseInt(e.target.value) || 0))), type: "number", min: -45, max: 45 })] })), _jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Preview (text watermark only)" }), _jsx(Row, { surface: "default", justify: "end", align: "end", className: `relative h-32 ${__O.hidden}`, rounded: "default", children: _jsx(Span, { ref: watermarkPreviewRef, weight: "medium", color: "faint", padding: "1", className: "select-none", children: watermarkText }) })] })] }) }), _jsx(TabsContent, { value: "fees", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Platform fee \u2014 our cut (%)", helperText: "% charged on order value. Buyer pays this.", value: String(platformFeePercent), onChange: (e) => setPlatformFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "GST on platform fee (%)", helperText: "Applied to our fee only (not full order). Usually 18%.", value: String(gstPercent), onChange: (e) => setGstPercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "Razorpay gateway cost (%)", helperText: "Gateway's own fee \u2014 absorbed by platform, not passed through.", value: String(gatewayFeePercent), onChange: (e) => setGatewayFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 10, step: 0.01 }), _jsx(Input, { label: "Minimum transaction fee (\u20B9)", helperText: "Per-transaction floor. Total charge will never be below base + this.", value: String(minimumTransactionFee), onChange: (e) => setMinimumTransactionFee(parseFloat(e.target.value) || 0), type: "number", min: 0, step: 0.01 }), _jsx(Input, { label: "Seller payout hold (days)", value: String(payoutHoldDays), onChange: (e) => setPayoutHoldDays(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Minimum payout amount (\u20B9)", value: String(minPayoutAmount), onChange: (e) => setMinPayoutAmount(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Auction listing fee (\u20B9)", value: String(auctionListingFee), onChange: (e) => setAuctionListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Pre-order listing fee (\u20B9)", value: String(preOrderListingFee), onChange: (e) => setPreOrderListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Featured slot fee (\u20B9)", value: String(featuredSlotFee), onChange: (e) => setFeaturedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Promoted slot fee (\u20B9)", value: String(promotedSlotFee), onChange: (e) => setPromotedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "COD deposit (%)", helperText: "% of order collected upfront on COD orders; remainder paid on delivery.", value: String(codDepositPercent), onChange: (e) => setCodDepositPercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 })] }), _jsx(Toggle, { checked: whatsappNotifyFeeEnabled, onChange: setWhatsappNotifyFeeEnabled, label: "Offer the WhatsApp order-updates addon at checkout" }), _jsx(Grid, { cols: 2, gap: "md", children: _jsx(Input, { label: "WhatsApp addon fee (\u20B9)", helperText: "Flat fee charged to the buyer when they opt in. Requires WhatsApp credentials + at least one approved template \u2014 see the WhatsApp tab.", value: String(whatsappNotifyFee), onChange: (e) => setWhatsappNotifyFee(parseFloat(e.target.value) || 0), type: "number", min: 0, disabled: !whatsappNotifyFeeEnabled }) }), _jsx(Toggle, { checked: giftWrapFeeEnabled, onChange: setGiftWrapFeeEnabled, label: "Offer the gift-wrap addon at checkout" }), _jsx(Grid, { cols: 2, gap: "md", children: _jsx(Input, { label: "Gift wrap fee (\u20B9)", helperText: "Flat fee charged to the buyer when they opt in.", value: String(giftWrapFee), onChange: (e) => setGiftWrapFee(parseFloat(e.target.value) || 0), type: "number", min: 0, disabled: !giftWrapFeeEnabled }) }), _jsx(Toggle, { checked: shipmentProtectionFeeEnabled, onChange: setShipmentProtectionFeeEnabled, label: "Offer the shipment-protection addon at checkout" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Shipment protection (%)", helperText: "% of order subtotal, charged when the buyer opts in.", value: String(shipmentProtectionFeePercent), onChange: (e) => setShipmentProtectionFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1, disabled: !shipmentProtectionFeeEnabled }), _jsx(Input, { label: "Shipment protection minimum (\u20B9)", helperText: "Floor for the shipment-protection fee.", value: String(shipmentProtectionFeeMin), onChange: (e) => setShipmentProtectionFeeMin(parseFloat(e.target.value) || 0), type: "number", min: 0, disabled: !shipmentProtectionFeeEnabled })] })] }) }), _jsx(TabsContent, { value: "procurement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Used to compute each procurement shipment's labor cost (hours spent \u00D7 hourly rate) and its estimated processing time (hours spent \u00F7 max hours/day)." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Hourly rate (\u20B9)", helperText: "Your effective hourly wage for sorting/categorizing/listing a shipment.", value: String(laborHourlyRate), onChange: (e) => setLaborHourlyRate(Math.round((parseFloat(e.target.value) || 0) * 100) / 100), type: "number", min: 0 }), _jsx(Input, { label: "Max hours per day", helperText: "Used only to project how many days a shipment's tracked hours will take.", value: String(laborMaxHoursPerDay), onChange: (e) => setLaborMaxHoursPerDay(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 24 })] })] }) }), _jsx(TabsContent, { value: "emi", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "A seller must also opt in via their own Payout Settings for EMI to appear at checkout on their items. See the \"How EMI Works\" public page for buyer-facing copy." }), _jsx(Toggle, { checked: emiEnabled, onChange: setEmiEnabled, label: "Enable EMI platform-wide" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Minimum order value (\u20B9)", helperText: "A seller's cart subtotal must exceed this for EMI to appear as an option.", value: String(emiMinOrderValue), onChange: (e) => setEmiMinOrderValue(Math.round((parseFloat(e.target.value) || 0) * 100) / 100), type: "number", min: 0 }), _jsx(Input, { label: "Tenure options (months, comma-separated)", helperText: "e.g. 2,3,4,5,6", value: emiTenureOptionsText, onChange: (e) => setEmiTenureOptionsText(e.target.value) }), _jsx(Input, { label: "Token / down-payment (%)", helperText: "Collected upfront at checkout.", value: String(emiTokenPercent), onChange: (e) => setEmiTokenPercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100 }), _jsx(Input, { label: "Billing day (1\u201310)", helperText: "Day of month each installment is due.", value: String(emiBillingDay), onChange: (e) => setEmiBillingDay(parseFloat(e.target.value) || 1), type: "number", min: 1, max: 10 }), _jsx(Input, { label: "Surcharge (% of principal per month)", helperText: "The 'excess EMI fee' the buyer pays for spreading payment.", value: String(emiSurchargePercentPerMonth), onChange: (e) => setEmiSurchargePercentPerMonth(parseFloat(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Seller's share of surcharge (%)", helperText: "The rest of the surcharge goes to the platform.", value: String(emiSurchargeSellerSharePercent), onChange: (e) => setEmiSurchargeSellerSharePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100 })] })] }) }), _jsx(TabsContent, { value: "gst", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Required before enabling GST-inclusive checkout (P-8). Also set a GST rate + HSN code on each taxable product for the tax breakdown to appear." }), _jsx(Toggle, { checked: gstEnabled, onChange: setGstEnabled, label: "Enable GST on checkout" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "GSTIN", helperText: "15-character GST Identification Number.", value: gstin, onChange: (e) => setGstin(e.target.value), placeholder: "29AAAAA0000A1Z5", maxLength: 15 }), _jsx(Input, { label: "Legal name", helperText: "Registered business name for invoices.", value: gstLegalName, onChange: (e) => setGstLegalName(e.target.value), placeholder: "LetItRip Collectibles Pvt Ltd" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Registered address" }), _jsx(Textarea, { value: gstAddress, onChange: (e) => setGstAddress(e.target.value), placeholder: "Registered business address for GST invoices\u2026", rows: 3 })] })] }) }), _jsx(TabsContent, { value: "featureflags", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "lg", children: [_jsx(Text, { size: "xs", color: "muted", children: "Site-wide platform feature toggles. Disabling a flag hides that feature's nav entries and pages \u2014 existing data is untouched, it just becomes unreachable until re-enabled." }), _jsx(Grid, { cols: 2, gap: "md", children: FEATURE_FLAG_META.map((flag) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Toggle, { label: `${flag.icon} ${flag.labelKey}`, checked: featureFlags[flag.key] ?? false, onChange: (v) => setFeatureFlags((prev) => ({ ...prev, [flag.key]: v })) }), _jsx(Text, { size: "xs", color: "muted", children: flag.descKey })] }, flag.key))) }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Listing types" }), _jsx(Text, { size: "xs", color: "muted", children: "Disabled types are hidden from listings and reject create/add-to-cart." }), _jsx(Grid, { cols: 2, gap: "sm", children: LISTING_TYPE_KEYS.map((key) => (_jsx(Toggle, { label: key, checked: listingTypeFlags[key] ?? true, onChange: (v) => setListingTypeFlags((prev) => ({ ...prev, [key]: v })) }, key))) })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Category types" }), _jsx(Grid, { cols: 2, gap: "sm", children: CATEGORY_TYPE_KEYS.map((key) => (_jsx(Toggle, { label: key, checked: categoryTypeFlags[key] ?? true, onChange: (v) => setCategoryTypeFlags((prev) => ({ ...prev, [key]: v })) }, key))) })] })] }) }), _jsx(TabsContent, { value: "integrations", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Keys are masked in transit and stored encrypted. Click Reveal to view." }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Razorpay" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Razorpay Key ID", value: razorpayKeyId, onChange: setRazorpayKeyId, placeholder: "rzp_live_\u2026" }), _jsx(MaskedInput, { label: "Razorpay Secret", value: razorpaySecret, onChange: setRazorpaySecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "SMTP / Email" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "SMTP host", value: smtpHost, onChange: (e) => setSmtpHost(e.target.value), placeholder: "smtp.sendgrid.net" }), _jsx(Input, { label: "SMTP port", value: smtpPort, onChange: (e) => setSmtpPort(e.target.value), type: "number", placeholder: "587" }), _jsx(Input, { label: "SMTP user", value: smtpUser, onChange: (e) => setSmtpUser(e.target.value), placeholder: "apikey" }), _jsx(MaskedInput, { label: "SMTP password", value: smtpPassword, onChange: setSmtpPassword, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsx(Input, { label: "From address", value: smtpFrom, onChange: (e) => setSmtpFrom(e.target.value), placeholder: "noreply@letitrip.in", type: "email" })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Analytics & Tracking" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Google Analytics ID", value: gaMeasurementId, onChange: (e) => setGaMeasurementId(e.target.value), placeholder: "G-XXXXXXXXXX" }), _jsx(Input, { label: "Facebook Pixel ID", value: fbPixelId, onChange: (e) => setFbPixelId(e.target.value), placeholder: "XXXXXXXXXXXXXXXX" }), _jsx(Input, { label: "GTM Container ID", value: gtmContainerId, onChange: (e) => setGtmContainerId(e.target.value), placeholder: "GTM-XXXXXXX" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Meta (Instagram & Facebook Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Used by the Social Feed section to fetch Instagram and Facebook posts via Meta Graph API v19." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Page Access Token", value: metaPageAccessToken, onChange: setMetaPageAccessToken, placeholder: "EAAxxxxxxx\u2026" }), _jsx(Input, { label: "Page ID (or handle)", value: metaPageId, onChange: (e) => setMetaPageId(e.target.value), placeholder: "letitrip" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "TikTok for Developers (Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Client credentials + long-lived access token from TikTok for Developers. Used to list your account's public videos." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Client Key", value: tiktokClientKey, onChange: setTiktokClientKey, placeholder: "aw\u2026" }), _jsx(MaskedInput, { label: "Client Secret", value: tiktokClientSecret, onChange: setTiktokClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" }), _jsx(MaskedInput, { label: "Access Token (long-lived)", value: tiktokAccessToken, onChange: setTiktokAccessToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "DeviantArt OAuth2 (Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Client credentials for DeviantArt gallery fetching (client-credentials OAuth2 flow \u2014 no user login required)." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Client ID", value: deviantartClientId, onChange: setDeviantartClientId, placeholder: "1234" }), _jsx(MaskedInput, { label: "Client Secret", value: deviantartClientSecret, onChange: setDeviantartClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] })] }) }), _jsx(TabsContent, { value: "shipping", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Payment methods" }), _jsx(Toggle, { label: "Razorpay (online card/UPI) enabled \u2014 disabled by default, manual payment is the default", checked: razorpayEnabled, onChange: setRazorpayEnabled }), _jsx(Toggle, { label: "Manual UPI/bank transfer enabled", checked: upiManualEnabled, onChange: setUpiManualEnabled }), _jsx(Toggle, { label: "Cash on delivery (COD) enabled", checked: codEnabled, onChange: setCodEnabled }), _jsx(Input, { label: "Free shipping threshold (\u20B9)", value: String(freeShippingThreshold), onChange: (e) => setFreeShippingThreshold(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Orders above this amount get free shipping." }), _jsx(Select, { label: "Default carrier", options: CARRIER_OPTIONS, value: defaultCarrier, onValueChange: setDefaultCarrier }), _jsx(Input, { label: "Max delivery radius (km, 0 = no limit)", value: String(maxDeliveryRadius), onChange: (e) => setMaxDeliveryRadius(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "High-value checkout OTP threshold (\u20B9)", helperText: "Carts at or above this subtotal require an extra OTP verification step before payment (0 = disabled).", value: String(otpCheckoutThreshold), onChange: (e) => setOtpCheckoutThreshold(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "pt-[var(--appkit-space-2)]", children: "Seller shipping charge defaults" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Seller shipping fixed (\u20B9)", helperText: "Flat shipping charge collected on the seller's behalf.", value: String(sellerShippingFixed), onChange: (e) => setSellerShippingFixed(parseFloat(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Platform shipping (%)", helperText: "Platform's own shipping markup, as a % of order value.", value: String(platformShippingPercent), onChange: (e) => setPlatformShippingPercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "Platform shipping minimum (\u20B9)", helperText: "Floor for the platform shipping markup.", value: String(platformShippingFixedMin), onChange: (e) => setPlatformShippingFixedMin(parseFloat(e.target.value) || 0), type: "number", min: 0 })] })] }) }), _jsx(TabsContent, { value: "auction", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Input, { label: "Minimum bid increment (\u20B9)", value: String(minBidIncrement), onChange: (e) => setMinBidIncrement(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Global default \u2014 individual auctions may override." }), _jsx(Input, { label: "Auto-extend window (minutes before end)", value: String(autoExtendWindow), onChange: (e) => setAutoExtendWindow(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Extend auction end time if a bid arrives within this window." }), _jsx(Input, { label: "Settlement grace period (hours)", value: String(settlementGrace), onChange: (e) => setSettlementGrace(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Time winner has to pay before the auction is re-listed." })] }) }), _jsx(TabsContent, { value: "limits", children: _jsx(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Max products per store", value: String(maxProductsPerStore), onChange: (e) => setMaxProductsPerStore(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max images per product", value: String(maxImagesPerProduct), onChange: (e) => setMaxImagesPerProduct(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max video size (MB)", value: String(maxVideoSizeMb), onChange: (e) => setMaxVideoSizeMb(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max custom fields per product", value: String(maxCustomFields), onChange: (e) => setMaxCustomFields(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Max custom sections per product", value: String(maxCustomSections), onChange: (e) => setMaxCustomSections(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Order cancellation window (hours)", value: String(orderCancelWindow), onChange: (e) => setOrderCancelWindow(parseInt(e.target.value) || 0), type: "number", min: 0 })] }) }) }), _jsx(TabsContent, { value: "whatsapp", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Platform-level WhatsApp Business Cloud API credentials. Used for automated purchase announcements to admin numbers when orders are placed. Store owners configure their own credentials in Store \u2192 WhatsApp." }), _jsx(Input, { label: "Phone Number ID", value: waPhoneNumberId, onChange: (e) => setWaPhoneNumberId(e.target.value), placeholder: "987654321098765", helperText: "From Meta for Developers \u2192 App \u2192 WhatsApp \u2192 API Setup" }), _jsx(MaskedInput, { label: "Cloud API System User Token", value: waCloudApiToken, onChange: setWaCloudApiToken, placeholder: "EAAxxxxxxxx\u2026", helperText: "Long-lived system user access token with WhatsApp Business permissions" }), _jsx(Input, { label: "Admin Notify Numbers", value: waAdminNotifyNumbers, onChange: (e) => setWaAdminNotifyNumbers(e.target.value), placeholder: "919876543210,918765432109", helperText: "Comma-separated, digits-only, include country code. These receive a WhatsApp message when any order is placed." }), _jsx(Text, { size: "xs", color: "muted", children: "Approved Meta message templates for the buyer-facing WhatsApp order-updates addon (Fees tab). Meta rejects business-initiated free-text WhatsApp messages outside a 24h customer-service window \u2014 a real template name here is required for that notification type to actually deliver. Leave blank to skip WhatsApp for that type (in-app + email still fire)." }), _jsx(Input, { label: "Template language code", value: waTemplateLanguage, onChange: (e) => setWaTemplateLanguage(e.target.value), placeholder: "en", helperText: "BCP-47 code the templates below were approved in, e.g. en or en_US." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Order placed template name", value: waTemplateOrderPlaced, onChange: (e) => setWaTemplateOrderPlaced(e.target.value), placeholder: "order_placed_update" }), _jsx(Input, { label: "Order confirmed template name", value: waTemplateOrderConfirmed, onChange: (e) => setWaTemplateOrderConfirmed(e.target.value), placeholder: "order_confirmed_update" }), _jsx(Input, { label: "Order shipped template name", value: waTemplateOrderShipped, onChange: (e) => setWaTemplateOrderShipped(e.target.value), placeholder: "order_shipped_update" }), _jsx(Input, { label: "Order delivered template name", value: waTemplateOrderDelivered, onChange: (e) => setWaTemplateOrderDelivered(e.target.value), placeholder: "order_delivered_update" }), _jsx(Input, { label: "Order cancelled template name", value: waTemplateOrderCancelled, onChange: (e) => setWaTemplateOrderCancelled(e.target.value), placeholder: "order_cancelled_update" }), _jsx(Input, { label: "Refund initiated template name", value: waTemplateRefundInitiated, onChange: (e) => setWaTemplateRefundInitiated(e.target.value), placeholder: "refund_initiated_update" })] })] }) }), _jsx(TabsContent, { value: "notifications", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "lg", children: [_jsx(Text, { size: "xs", color: "muted", children: "In-app notifications are always on. Enable external channels below to let the platform fan out to email, WhatsApp, or SMS. Users can further restrict which types they receive." }), _jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "md", children: [_jsxs(Row, { justify: "between", gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "In-app (notification bell)" }), _jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "semibold", rounded: "full", padding: "pill-xs", children: "Always on" })] }), _jsx(Text, { size: "xs", color: "muted", children: "Displayed in the notification bell and inbox. Cannot be disabled." })] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "Email notifications", checked: notifEmailEnabled, onChange: setNotifEmailEnabled }), notifEmailEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send email", options: PRIORITY_OPTIONS, value: notifEmailMinPriority, onValueChange: setNotifEmailMinPriority }), _jsx(Text, { size: "xs", weight: "medium", color: "muted", className: "pt-[0.25rem]", children: "Resend API (for transactional email)" }), _jsx(MaskedInput, { label: "Resend API Key", value: resendApiKey, onChange: setResendApiKey, placeholder: "re_live_\u2026", helperText: "Get your key at resend.com \u2014 used for all transactional notifications." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "From email", value: notifFromEmail, onChange: (e) => setNotifFromEmail(e.target.value), placeholder: "noreply@letitrip.in", type: "email" }), _jsx(Input, { label: "From name", value: notifFromName, onChange: (e) => setNotifFromName(e.target.value), placeholder: "LetItRip" })] }), _jsx(Text, { size: "xs", weight: "medium", color: "muted", className: "pt-[0.25rem]", children: "Notification types (leave empty to send all types)" }), _jsx(PaginatedSelect, { multiple: true, options: NOTIF_TYPE_OPTIONS, value: notifEmailTypes, onChange: (values) => setNotifEmailTypes(values), placeholder: "All notification types" })] }))] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "WhatsApp notifications", checked: notifWhatsappEnabled, onChange: setNotifWhatsappEnabled }), notifWhatsappEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send WhatsApp", options: PRIORITY_OPTIONS, value: notifWhatsappMinPriority, onValueChange: setNotifWhatsappMinPriority }), _jsx(Toggle, { label: "Enable WhatsApp OTP (for login and verification)", checked: notifWhatsappOtpEnabled, onChange: setNotifWhatsappOtpEnabled }), _jsx(Text, { size: "xs", color: "muted", children: "WhatsApp credentials are configured in the WhatsApp tab (\u246C). OTP messages use the same phone number." }), _jsx(Text, { size: "xs", weight: "medium", color: "muted", className: "pt-[0.25rem]", children: "Notification types (leave empty to send all types)" }), _jsx(PaginatedSelect, { multiple: true, options: NOTIF_TYPE_OPTIONS, value: notifWhatsappTypes, onChange: (values) => setNotifWhatsappTypes(values), placeholder: "All notification types" })] }))] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "SMS notifications", checked: notifSmsEnabled, onChange: setNotifSmsEnabled }), notifSmsEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send SMS", options: PRIORITY_OPTIONS, value: notifSmsMinPriority, onValueChange: setNotifSmsMinPriority }), _jsx(Text, { size: "xs", color: "muted", children: "SMS gateway credentials (e.g. Twilio, MSG91) can be configured in the Integrations tab once an SMS provider is connected." })] }))] })] }) }), _jsx(TabsContent, { value: "legal", children: _jsx(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [
625
652
  ["Terms of Service", termsHtml, setTermsHtml],
626
653
  ["Privacy Policy", privacyHtml, setPrivacyHtml],
627
654
  ["Refund Policy", refundHtml, setRefundHtml],
@@ -3,12 +3,17 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
4
4
  import { sortBy } from "@mohasinac/appkit";
5
5
  import React, { useMemo } from "react";
6
- import { FilterChipGroup, Heading, ListingLayout, Span, Text } from "../../../ui";
6
+ import { useQueryClient } from "@tanstack/react-query";
7
+ import { FilterChipGroup, Heading, ListingLayout, RowActionMenu, Span, Text } from "../../../ui";
8
+ import { apiClient } from "../../../http";
9
+ import { useApiMutation } from "../../../client";
7
10
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
11
+ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
8
12
  import { ADMIN_BULK_ACTIONS, ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
9
13
  import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
10
14
  import { DataListingView } from "./DataListingView";
11
15
  import { AdminTesterChecklistItemEditorView } from "./AdminTesterChecklistItemEditorView";
16
+ const LISTING_QUERY_KEY = ["admin", "tester-checklist-items", "listing"];
12
17
  const COLUMNS = [
13
18
  {
14
19
  key: "primary",
@@ -36,14 +41,28 @@ const COLUMNS = [
36
41
  },
37
42
  ];
38
43
  export function AdminTesterChecklistView({ children, onBulkDelete, ...props }) {
44
+ const queryClient = useQueryClient();
45
+ const reopenMutation = useApiMutation({
46
+ mutationFn: async (id) => {
47
+ await apiClient.post(ADMIN_ENDPOINTS.TESTER_CHECKLIST_ITEM_REOPEN(id), {});
48
+ },
49
+ successMessage: "Case reopened for retest.",
50
+ onSuccess: () => {
51
+ queryClient.invalidateQueries({ queryKey: LISTING_QUERY_KEY });
52
+ },
53
+ });
39
54
  const config = useMemo(() => ({
40
55
  portal: "admin",
41
56
  title: "Tester Checklist",
42
57
  searchPlaceholder: "Search test cases, groups, or pages",
43
58
  emptyLabel: "No checklist items found",
44
- filterKeys: ["isActive"],
59
+ filterKeys: ["isActive", "bugConfirmed"],
60
+ // Bug-confirmed (and reopened-away) cases are always isActive:false —
61
+ // default to Active so they don't clutter the catalog; "Inactive" /
62
+ // "All" remain an explicit escape hatch via the filter chips below.
63
+ filterDefaults: { isActive: "true" },
45
64
  defaultSort: sortBy("order", "ASC"),
46
- queryKey: ["admin", "tester-checklist-items", "listing"],
65
+ queryKey: LISTING_QUERY_KEY,
47
66
  endpoint: ADMIN_ENDPOINTS.TESTER_CHECKLIST_ITEMS,
48
67
  sortOptions: [
49
68
  { value: "order", label: "Order" },
@@ -52,20 +71,43 @@ export function AdminTesterChecklistView({ children, onBulkDelete, ...props }) {
52
71
  { value: "label", label: "Test case A–Z" },
53
72
  ],
54
73
  columns: COLUMNS,
55
- mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
56
- id: toStringValue(item.id, `checklist-${index}`),
57
- primary: toStringValue(item.label, "Untitled test case"),
58
- secondary: `${toStringValue(item.groupLabel, "Uncategorized")} / ${toStringValue(item.pageLabel, "")}`,
59
- phase: typeof item.phase === "number" ? item.phase : 1,
60
- status: toStringValue(typeof item.isActive === "boolean"
61
- ? item.isActive
62
- ? "Active"
63
- : "Inactive"
64
- : item.status, "Active"),
65
- updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
66
- })),
74
+ mapRows: (response) => toRecordArray(response.items).map((item, index) => {
75
+ const bugConfirmed = item.bugConfirmed === true;
76
+ const version = typeof item.version === "number" ? item.version : 1;
77
+ const status = bugConfirmed
78
+ ? "Bug Confirmed"
79
+ : toStringValue(typeof item.isActive === "boolean"
80
+ ? item.isActive
81
+ ? "Active"
82
+ : "Inactive"
83
+ : item.status, "Active");
84
+ const secondary = bugConfirmed
85
+ ? `${toStringValue(item.groupLabel, "Uncategorized")} / ${toStringValue(item.pageLabel, "")} — 🐛 found by ${toStringValue(item.bugHunterName, "unknown tester")} (v${version})`
86
+ : `${toStringValue(item.groupLabel, "Uncategorized")} / ${toStringValue(item.pageLabel, "")}`;
87
+ return {
88
+ id: toStringValue(item.id, `checklist-${index}`),
89
+ primary: toStringValue(item.label, "Untitled test case"),
90
+ secondary,
91
+ phase: typeof item.phase === "number" ? item.phase : 1,
92
+ status,
93
+ updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
94
+ bugConfirmed,
95
+ supersededByItemId: item.supersededByItemId
96
+ ? toStringValue(item.supersededByItemId, "")
97
+ : undefined,
98
+ };
99
+ }),
67
100
  getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
68
- buildFilters: (state) => state.isActive ? sieveFilter("isActive", SIEVE_OP.EQ, state.isActive) : undefined,
101
+ buildFilters: (state) => {
102
+ const parts = [];
103
+ if (state.isActive && state.isActive !== "All") {
104
+ parts.push(sieveFilter("isActive", SIEVE_OP.EQ, state.isActive));
105
+ }
106
+ if (state.bugConfirmed && state.bugConfirmed !== "All") {
107
+ parts.push(sieveFilter("bugConfirmed", SIEVE_OP.EQ, state.bugConfirmed));
108
+ }
109
+ return parts.join(",") || undefined;
110
+ },
69
111
  primaryAction: {
70
112
  label: "Add Test Case",
71
113
  onClick: ({ openCreatePanel }) => openCreatePanel(),
@@ -86,14 +128,24 @@ export function AdminTesterChecklistView({ children, onBulkDelete, ...props }) {
86
128
  onClick: handlers[id],
87
129
  }));
88
130
  },
89
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: [
90
- { id: "All", label: "All" },
91
- { id: "true", label: "Active" },
92
- { id: "false", label: "Inactive" },
93
- ], value: pendingFilters.isActive || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isActive: v })) })),
131
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FilterChipGroup, { label: "Status", tabs: [
132
+ { id: "All", label: "All" },
133
+ { id: "true", label: "Active" },
134
+ { id: "false", label: "Inactive" },
135
+ ], value: pendingFilters.isActive || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isActive: v })) }), _jsx(FilterChipGroup, { label: "Bug status", tabs: [
136
+ { id: "All", label: "All" },
137
+ { id: "true", label: "Bug Confirmed" },
138
+ ], value: pendingFilters.bugConfirmed || "", onChange: (v) => setPendingFilters((p) => ({ ...p, bugConfirmed: v })) })] })),
139
+ renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
140
+ {
141
+ label: ACTIONS.ADMIN["reopen-checklist-item"].label,
142
+ disabled: !row.bugConfirmed || !!row.supersededByItemId,
143
+ onClick: () => reopenMutation.mutate(row.id),
144
+ },
145
+ ] })),
94
146
  renderEditor: ({ editId, closePanel }) => (_jsx(AdminTesterChecklistItemEditorView, { itemId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })),
95
147
  resolveEditorTitle: ({ isCreate }) => (isCreate ? "Add Test Case" : "Edit Test Case"),
96
- }), [onBulkDelete]);
148
+ }), [onBulkDelete, reopenMutation]);
97
149
  if (React.Children.count(children) > 0) {
98
150
  return (_jsxs(ListingLayout, { portal: "admin", ...props, children: [_jsx(Heading, { level: 1, className: "sr-only", children: "Tester Checklist" }), children] }));
99
151
  }
@@ -10,6 +10,10 @@ export interface AdminListingConfig<TResponse, TRow extends {
10
10
  getTotal?: (response: TResponse, rows: TRow[]) => number;
11
11
  buildFilters: (filterState: Record<string, string>) => string | undefined;
12
12
  initialView?: "grid" | "list" | "table";
13
+ /** Values used for filterKeys absent from the URL — lets a view default to
14
+ * a non-"All" filter state (e.g. hide inactive rows) on first load, while
15
+ * "Clear filters" (which writes an explicit "") still shows everything. */
16
+ filterDefaults?: Record<string, string>;
13
17
  }
14
18
  export declare function useAdminListing<TResponse, TRow extends {
15
19
  id: string;
@@ -6,13 +6,15 @@ import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
6
6
  import { useDataViewMode } from "../../account/hooks/useDataViewMode";
7
7
  import { useAdminListingData } from "./useAdminListingData";
8
8
  export function useAdminListing(config) {
9
- const { filterKeys, defaultSort, pageSize: defaultPageSize = 25, queryKey, endpoint, mapRows, getTotal, buildFilters, } = config;
9
+ const { filterKeys, defaultSort, pageSize: defaultPageSize = 25, queryKey, endpoint, mapRows, getTotal, buildFilters, filterDefaults = {}, } = config;
10
10
  // Persisted, viewport-aware view-mode: below 768px defaults to "list"
11
11
  // (AdminViewCards' one-full-width-card-per-row layout) unless the caller
12
12
  // explicitly pins hideTableView (grid-only views like coupons); the user's
13
13
  // own explicit choice, once made, always wins and persists across visits.
14
14
  const { view, setView } = useDataViewMode(config.initialView ?? "table");
15
- const table = useUrlTable({ defaults: { pageSize: String(defaultPageSize), sort: defaultSort } });
15
+ const table = useUrlTable({
16
+ defaults: { pageSize: String(defaultPageSize), sort: defaultSort, ...filterDefaults },
17
+ });
16
18
  // Reactive — table.getNumber reads the URL param, so a page-size selector
17
19
  // (Pagination's pageSize/onPageSizeChange) actually takes effect instead
18
20
  // of always falling back to the static config default.
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import type { AboutContentDocument } from "../../about/schemas/firestore";
6
6
  export declare const ADMIN_CHECKOUT_BYPASS_FLAG_KEY: "adminCheckoutBypass";
7
- export type NotificationType = "order_placed" | "order_confirmed" | "order_shipped" | "order_delivered" | "order_cancelled" | "bid_placed" | "bid_outbid" | "bid_won" | "bid_lost" | "review_approved" | "review_replied" | "product_available" | "promotion" | "system" | "welcome" | "account_action" | "offer_received" | "offer_responded" | "offer_expired" | "offer_counter_accepted" | "refund_initiated" | "prize_reveal_ready" | "prize_reveal_expired" | "prize_reveal_reminder" | "emi_installment_due_soon" | "emi_installment_overdue" | "payment_review";
7
+ export type NotificationType = "order_placed" | "order_confirmed" | "order_shipped" | "order_delivered" | "order_cancelled" | "bid_placed" | "bid_outbid" | "bid_won" | "bid_lost" | "review_approved" | "review_replied" | "product_available" | "promotion" | "system" | "welcome" | "account_action" | "offer_received" | "offer_responded" | "offer_expired" | "offer_counter_accepted" | "refund_initiated" | "prize_won" | "prize_reveal_expired" | "emi_installment_due_soon" | "emi_installment_overdue" | "payment_review";
8
8
  import type { BaseDocument } from "../../../_internal/shared/types/base-document";
9
9
  export type NotificationPriority = "low" | "normal" | "high";
10
10
  export interface NotificationDocument extends BaseDocument {
@@ -65,9 +65,8 @@ export declare const NOTIFICATION_FIELDS: {
65
65
  readonly OFFER_COUNTER_ACCEPTED: NotificationType;
66
66
  readonly REFUND_INITIATED: NotificationType;
67
67
  readonly ACCOUNT_ACTION: NotificationType;
68
- readonly PRIZE_REVEAL_READY: NotificationType;
68
+ readonly PRIZE_WON: NotificationType;
69
69
  readonly PRIZE_REVEAL_EXPIRED: NotificationType;
70
- readonly PRIZE_REVEAL_REMINDER: NotificationType;
71
70
  };
72
71
  readonly PRIORITY_VALUES: {
73
72
  readonly LOW: NotificationPriority;
@@ -489,7 +488,7 @@ export interface SiteSettingsDocument extends BaseDocument {
489
488
  /**
490
489
  * Image watermark configuration applied by the `/api/media/[...slug]` CDN
491
490
  * proxy. When absent the proxy falls back to text watermark "letitrip.in" at
492
- * 30% width with 20% opacity.
491
+ * 10% width with 10% opacity, centered.
493
492
  */
494
493
  watermark?: {
495
494
  /** `"text"` renders the `text` field; `"image"` overlays `imageUrl`. */
@@ -504,8 +503,17 @@ export interface SiteSettingsDocument extends BaseDocument {
504
503
  imageUrl?: string;
505
504
  /** Percentage of target image width (0–100). 0 disables the watermark. */
506
505
  size?: number;
507
- /** Percentage opacity (0–100). Default 20. */
506
+ /** Percentage opacity (0–100). Default 10. */
508
507
  opacity?: number;
508
+ /**
509
+ * Anchor preset: 4 corners + center, or `"custom"` to use `offsetX`/
510
+ * `offsetY` instead. Default `"center"`.
511
+ */
512
+ position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center" | "custom";
513
+ /** `position: "custom"` only — % of image width from center. +right / -left. Default 0. */
514
+ offsetX?: number;
515
+ /** `position: "custom"` only — % of image height from center. +down / -up. Default 0. */
516
+ offsetY?: number;
509
517
  };
510
518
  /** Encrypted provider credentials � never return raw to the client. */
511
519
  credentials?: SiteSettingsCredentials;
@@ -52,9 +52,8 @@ export const NOTIFICATION_FIELDS = {
52
52
  OFFER_COUNTER_ACCEPTED: "offer_counter_accepted",
53
53
  REFUND_INITIATED: "refund_initiated",
54
54
  ACCOUNT_ACTION: "account_action",
55
- PRIZE_REVEAL_READY: "prize_reveal_ready",
55
+ PRIZE_WON: "prize_won",
56
56
  PRIZE_REVEAL_EXPIRED: "prize_reveal_expired",
57
- PRIZE_REVEAL_REMINDER: "prize_reveal_reminder",
58
57
  },
59
58
  PRIORITY_VALUES: {
60
59
  LOW: "low",
@@ -130,13 +130,15 @@ export async function placeBid(userId, userEmail, input) {
130
130
  });
131
131
  });
132
132
  const finalVisibleForRtdb = newBidWins ? visibleBid : (bumpedPreviousVisible ?? prevVisible);
133
+ const finalBidCountForRtdb = (product.bidCount ?? 0) + 1;
133
134
  try {
134
135
  const rtdb = getAdminRealtimeDb();
135
136
  await rtdb.ref(`/auction-bids/${productId}`).set({
136
137
  currentBid: finalVisibleForRtdb,
138
+ bidCount: finalBidCountForRtdb,
137
139
  lastBid: {
138
140
  amount: finalVisibleForRtdb,
139
- bidderName: "Bidder",
141
+ bidderName: bid.userName,
140
142
  timestamp: Date.now(),
141
143
  },
142
144
  updatedAt: Date.now(),
@@ -9,5 +9,10 @@ export interface AuctionBidsTableProps {
9
9
  bids: BidDocument[];
10
10
  portal?: "buyer" | "store" | "admin";
11
11
  emptyLabel?: React.ReactNode;
12
+ /** Server-side pagination — the auction groups shown are already just the
13
+ * current page's worth of bids grouped by auction, not the full history. */
14
+ totalPages?: number;
15
+ currentPage?: number;
16
+ onPageChange?: (page: number) => void;
12
17
  }
13
- export declare function AuctionBidsTable({ bids, portal, emptyLabel, }: AuctionBidsTableProps): React.JSX.Element;
18
+ export declare function AuctionBidsTable({ bids, portal, emptyLabel, totalPages, currentPage, onPageChange, }: AuctionBidsTableProps): React.JSX.Element;
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useMemo, useState } from "react";
4
4
  import { ChevronRight } from "lucide-react";
5
5
  import { Badge, Button, Div, Row, Span, Stack, Text } from "../../../ui";
6
+ import { Pagination } from "../../../ui/components/Pagination";
6
7
  const __O = {
7
8
  hidden: "overflow-hidden",
8
9
  };
@@ -51,15 +52,18 @@ function groupByAuction(bids) {
51
52
  }
52
53
  function AuctionRow({ auction, portal, }) {
53
54
  const [expanded, setExpanded] = useState(false);
54
- const sorted = useMemo(() => [...auction.bids].sort((a, b) => b.bidAmount - a.bidAmount), [auction.bids]);
55
- const highest = sorted[0]?.bidAmount ?? 0;
55
+ // Most-recent bid first matches the caller's own newest-first ordering
56
+ // (see UserBidsPage) instead of re-sorting by amount, which silently threw
57
+ // that ordering away for the expanded per-auction row list.
58
+ const sorted = useMemo(() => [...auction.bids].sort((a, b) => +new Date(b.bidDate) - +new Date(a.bidDate)), [auction.bids]);
59
+ const highest = auction.bids.reduce((max, b) => Math.max(max, b.bidAmount), 0);
56
60
  const isWinning = auction.bids.some((b) => b.isWinning);
57
61
  return (_jsxs(Div, { className: `border border-[var(--appkit-color-border)] ${__O.hidden} bg-[var(--appkit-color-surface)]`, rounded: "xl", shadow: "sm", children: [_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setExpanded((v) => !v), gap: "lg", paddingX: "md", paddingY: "md", className: "w-full text-left", "aria-expanded": expanded, children: [_jsx(ChevronRight, { className: `shrink-0 text-[var(--appkit-color-text-muted)] transition-transform ${expanded ? "rotate-90" : ""}`, size: 16 }), _jsx(Div, { className: "flex-1 min-w-0", children: _jsx(Text, { className: "text-[var(--appkit-color-text)] line-clamp-1", size: "sm", weight: "semibold", children: auction.productTitle }) }), _jsxs(Row, { gap: "sm", className: "shrink-0", children: [isWinning && (_jsx(Badge, { variant: "active", children: "Winning" })), _jsxs(Text, { variant: "secondary", size: "xs", children: [auction.bids.length, " bid", auction.bids.length !== 1 ? "s" : ""] }), _jsx(Text, { className: "text-[var(--appkit-color-text)]", size: "sm", weight: "semibold", children: formatBidAmount(highest) })] })] }), expanded && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", children: [_jsxs(Div, { textSize: "xs", textWeight: "medium", paddingX: "x-md", paddingY: "y-xs", className: `grid text-[var(--appkit-color-text-muted)] uppercase tracking-wide border-b border-[var(--appkit-color-border-subtle)] ${portal === "buyer" ? "[grid-template-columns:1fr_auto_auto]" : "[grid-template-columns:1fr_1fr_auto_auto]"}`, children: [_jsx(Span, { children: portal === "buyer" ? "Amount" : "Bidder" }), portal !== "buyer" && _jsx(Span, { children: "Amount" }), _jsx(Span, { children: "Status" }), _jsx(Span, { className: "text-right", children: "Time" })] }), sorted.map((bid) => (_jsxs(Div, { layout: "grid", align: "center", paddingX: "x-md", paddingY: "y-xs-tall", className: `border-b border-[var(--appkit-color-border-subtle)] last:border-0 hover:bg-[var(--appkit-color-border-subtle)] transition-colors ${portal === "buyer" ? "[grid-template-columns:1fr_auto_auto]" : "[grid-template-columns:1fr_1fr_auto_auto]"}`, children: [portal !== "buyer" && (_jsx(Text, { className: "text-[var(--appkit-color-text)] truncate pr-[0.75rem]", size: "sm", children: bid.userName || bid.userId })), _jsx(Text, { className: "text-[var(--appkit-color-text)]", size: "sm", weight: "medium", children: formatBidAmount(bid.bidAmount) }), _jsx(Badge, { variant: STATUS_VARIANT[bid.status] ?? "pending", className: "capitalize", children: bid.status }), _jsx(Text, { variant: "secondary", size: "xs", align: "end", children: relDate(bid.bidDate) })] }, bid.id)))] }))] }));
58
62
  }
59
- export function AuctionBidsTable({ bids, portal = "buyer", emptyLabel = "No bids found.", }) {
63
+ export function AuctionBidsTable({ bids, portal = "buyer", emptyLabel = "No bids found.", totalPages, currentPage, onPageChange, }) {
60
64
  const auctions = useMemo(() => groupByAuction(bids), [bids]);
61
65
  if (auctions.length === 0) {
62
66
  return (_jsx(Div, { padding: "y-6xl", className: "text-left", children: _jsx(Text, { variant: "secondary", children: emptyLabel }) }));
63
67
  }
64
- return (_jsx(Stack, { gap: "3", children: auctions.map((auction) => (_jsx(AuctionRow, { auction: auction, portal: portal }, auction.productId))) }));
68
+ return (_jsxs(Stack, { gap: "3", children: [auctions.map((auction) => (_jsx(AuctionRow, { auction: auction, portal: portal }, auction.productId))), totalPages !== undefined && totalPages > 1 && currentPage !== undefined && onPageChange && (_jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: onPageChange }) }))] }));
65
69
  }