@mohasinac/appkit 2.7.16 → 2.7.18

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 (105) hide show
  1. package/dist/_internal/server/features/auctions/actions.js +1 -1
  2. package/dist/_internal/server/features/blog/actions.js +2 -2
  3. package/dist/_internal/server/features/brands/actions.js +2 -2
  4. package/dist/_internal/server/features/bundles/actions.d.ts +15 -0
  5. package/dist/_internal/server/features/bundles/actions.js +48 -0
  6. package/dist/_internal/server/features/bundles/index.d.ts +1 -0
  7. package/dist/_internal/server/features/bundles/index.js +1 -0
  8. package/dist/_internal/server/features/cart/actions.js +3 -3
  9. package/dist/_internal/server/features/checkout/actions.d.ts +4 -0
  10. package/dist/_internal/server/features/checkout/actions.js +23 -12
  11. package/dist/_internal/server/features/events/actions.js +3 -3
  12. package/dist/_internal/server/features/orders/actions.js +4 -4
  13. package/dist/_internal/server/features/payments/data.d.ts +5 -3
  14. package/dist/_internal/server/features/payments/data.js +13 -8
  15. package/dist/_internal/server/features/payments/index.d.ts +1 -1
  16. package/dist/_internal/server/features/payments/index.js +1 -1
  17. package/dist/_internal/server/features/pre-orders/actions.js +1 -1
  18. package/dist/_internal/server/features/products/actions.js +6 -6
  19. package/dist/_internal/server/features/promotions/actions.js +3 -3
  20. package/dist/_internal/shared/features/categories/bundle-config.d.ts +24 -0
  21. package/dist/_internal/shared/features/categories/bundle-config.js +28 -0
  22. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +7 -0
  23. package/dist/_internal/shared/features/categories/bundle-copy.js +9 -0
  24. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +69 -19
  25. package/dist/_internal/shared/features/categories/bundle-schemas.js +20 -2
  26. package/dist/_internal/shared/features/checkout/config.d.ts +1 -1
  27. package/dist/_internal/shared/features/checkout/config.js +1 -1
  28. package/dist/_internal/shared/features/payments/config.d.ts +2 -1
  29. package/dist/_internal/shared/features/payments/config.js +2 -1
  30. package/dist/_internal/shared/features/products/schema.d.ts +8 -8
  31. package/dist/constants/field-names.d.ts +8 -1
  32. package/dist/constants/field-names.js +9 -2
  33. package/dist/constants/table-keys.d.ts +3 -0
  34. package/dist/constants/table-keys.js +3 -0
  35. package/dist/features/admin/components/AdminSiteSettingsView.js +17 -15
  36. package/dist/features/admin/components/DashboardStats.js +20 -12
  37. package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +18 -2
  38. package/dist/features/admin/components/analytics/AdminStatCard.d.ts +7 -1
  39. package/dist/features/admin/components/analytics/AdminStatCard.js +13 -2
  40. package/dist/features/admin/schemas/firestore.d.ts +18 -4
  41. package/dist/features/admin/schemas/firestore.js +11 -1
  42. package/dist/features/categories/components/BundleBuyNowCta.d.ts +3 -1
  43. package/dist/features/categories/components/BundleBuyNowCta.js +3 -3
  44. package/dist/features/categories/components/BundleDynamicRuleEditor.d.ts +1 -1
  45. package/dist/features/categories/components/BundleDynamicRuleEditor.js +2 -1
  46. package/dist/features/categories/components/BundlesListView.d.ts +4 -1
  47. package/dist/features/categories/components/BundlesListView.js +2 -2
  48. package/dist/features/categories/components/CategoryBundlesListing.d.ts +4 -1
  49. package/dist/features/categories/components/CategoryBundlesListing.js +9 -8
  50. package/dist/features/categories/schemas/firestore.d.ts +29 -1
  51. package/dist/features/checkout/actions/checkout-actions.d.ts +11 -0
  52. package/dist/features/checkout/actions/checkout-actions.js +26 -0
  53. package/dist/features/filters/FilterFacetSection.d.ts +6 -1
  54. package/dist/features/filters/FilterFacetSection.js +25 -2
  55. package/dist/features/homepage/components/FeaturedBundlesSection.d.ts +4 -1
  56. package/dist/features/homepage/components/FeaturedBundlesSection.js +9 -8
  57. package/dist/features/orders/actions/refund-actions.js +10 -7
  58. package/dist/features/orders/schemas/firestore.d.ts +16 -13
  59. package/dist/features/orders/schemas/firestore.js +6 -5
  60. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
  61. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -1
  62. package/dist/features/products/components/AuctionsIndexListing.js +2 -1
  63. package/dist/features/products/components/ProductDetailPageView.js +3 -1
  64. package/dist/features/products/components/ProductDetailView.d.ts +3 -1
  65. package/dist/features/products/components/ProductDetailView.js +2 -2
  66. package/dist/features/products/components/ProductFilters.d.ts +10 -4
  67. package/dist/features/products/components/ProductFilters.js +20 -16
  68. package/dist/features/products/components/ProductGrid.js +3 -3
  69. package/dist/features/products/components/ProductsIndexListing.js +2 -1
  70. package/dist/features/products/constants/product-features.constants.js +5 -1
  71. package/dist/features/products/schemas/index.d.ts +2 -2
  72. package/dist/features/products/schemas/product-features.d.ts +1 -1
  73. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  74. package/dist/features/products/schemas/product-features.validators.js +4 -0
  75. package/dist/features/seller/components/SellerCouponEditorView.js +2 -2
  76. package/dist/features/seller/components/analytics/SellerAnalyticsStats.js +10 -4
  77. package/dist/features/shell/FormShell.js +1 -1
  78. package/dist/features/stores/components/StoreBundlesPageView.d.ts +4 -1
  79. package/dist/features/stores/components/StoreBundlesPageView.js +2 -2
  80. package/dist/features/stores/components/StoresIndexListing.js +2 -1
  81. package/dist/features/wishlist/hooks/useWishlistCount.d.ts +2 -2
  82. package/dist/features/wishlist/hooks/useWishlistCount.js +5 -8
  83. package/dist/providers/db-firebase/admin-app-lite.js +8 -2
  84. package/dist/providers/db-firebase/admin.js +8 -2
  85. package/dist/seed/addresses-seed-data.js +24 -7
  86. package/dist/seed/cart-seed-data.d.ts +10 -10
  87. package/dist/seed/cart-seed-data.js +39 -2
  88. package/dist/seed/grouped-listings-seed-data.js +47 -0
  89. package/dist/seed/orders-seed-data.js +150 -0
  90. package/dist/seed/products-standard-seed-data.js +42 -0
  91. package/dist/seed/site-settings-seed-data.js +11 -4
  92. package/dist/seed/users-seed-data.js +1 -1
  93. package/dist/seed/wishlists-seed-data.js +7 -0
  94. package/dist/server-entry.d.ts +2 -1
  95. package/dist/server-entry.js +2 -1
  96. package/dist/server.d.ts +2 -1
  97. package/dist/server.js +3 -1
  98. package/dist/tailwind-utilities.css +1 -1
  99. package/dist/ui/components/Breadcrumb.style.css +1 -1
  100. package/dist/ui/components/ListingLayout.style.css +1 -1
  101. package/dist/ui/components/Select.style.css +1 -1
  102. package/dist/ui/components/SideDrawer.style.css +55 -12
  103. package/dist/ui/components/Typography.style.css +2 -2
  104. package/dist/ui/rich-text/RichText.style.css +4 -5
  105. package/package.json +1 -1
@@ -70,10 +70,11 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
70
70
  const [watermarkImageUrl, setWatermarkImageUrl] = React.useState("");
71
71
  const [watermarkSize, setWatermarkSize] = React.useState(30);
72
72
  const [watermarkOpacity, setWatermarkOpacity] = React.useState(20);
73
- // ⑦ Fees
74
- const [platformCommission, setPlatformCommission] = React.useState(5);
75
- const [buyerFee, setBuyerFee] = React.useState(2);
76
- const [razorpayFee, setRazorpayFee] = React.useState(2.36);
73
+ // ⑦ Fees — all read/written under commissions key
74
+ const [platformFeePercent, setPlatformFeePercent] = React.useState(5);
75
+ const [gstPercent, setGstPercent] = React.useState(18);
76
+ const [minimumTransactionFee, setMinimumTransactionFee] = React.useState(0);
77
+ const [gatewayFeePercent, setGatewayFeePercent] = React.useState(2);
77
78
  const [payoutHoldDays, setPayoutHoldDays] = React.useState(7);
78
79
  const [minPayoutAmount, setMinPayoutAmount] = React.useState(100);
79
80
  const [auctionListingFee, setAuctionListingFee] = React.useState(0);
@@ -172,15 +173,16 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
172
173
  setWatermarkImageUrl(s.watermark?.imageUrl ?? "");
173
174
  setWatermarkSize(s.watermark?.size ?? 30);
174
175
  setWatermarkOpacity(s.watermark?.opacity ?? 20);
175
- setPlatformCommission(s.fees?.platformCommission ?? 5);
176
- setBuyerFee(s.fees?.buyerFee ?? 2);
177
- setRazorpayFee(s.fees?.razorpayFeePercent ?? 2.36);
178
- setPayoutHoldDays(s.fees?.payoutHoldDays ?? 7);
179
- setMinPayoutAmount(s.fees?.minPayoutAmount ?? 100);
180
- setAuctionListingFee(s.fees?.auctionListingFee ?? 0);
181
- setPreOrderListingFee(s.fees?.preOrderListingFee ?? 0);
182
- setFeaturedSlotFee(s.fees?.featuredSlotFee ?? 999);
183
- setPromotedSlotFee(s.fees?.promotedSlotFee ?? 499);
176
+ setPlatformFeePercent(s.commissions?.platformFeePercent ?? 5);
177
+ setGstPercent(s.commissions?.gstPercent ?? 18);
178
+ setMinimumTransactionFee(s.commissions?.minimumTransactionFee ?? 0);
179
+ setGatewayFeePercent(s.commissions?.gatewayFeePercent ?? 2);
180
+ setPayoutHoldDays(s.commissions?.payoutHoldDays ?? 7);
181
+ setMinPayoutAmount(s.commissions?.minPayoutAmount ?? 100);
182
+ setAuctionListingFee(s.commissions?.auctionListingFee ?? 0);
183
+ setPreOrderListingFee(s.commissions?.preOrderListingFee ?? 0);
184
+ setFeaturedSlotFee(s.commissions?.featuredSlotFee ?? 999);
185
+ setPromotedSlotFee(s.commissions?.promotedSlotFee ?? 499);
184
186
  setRazorpayKeyId(s.credentialsMasked?.razorpayKeyId ?? "");
185
187
  setRazorpaySecret(s.credentialsMasked?.razorpaySecret ?? "");
186
188
  setShiprocketToken(s.credentialsMasked?.shiprocketToken ?? "");
@@ -259,7 +261,7 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
259
261
  watermark: { type: watermarkType, text: watermarkText, imageUrl: watermarkImageUrl, size: watermarkSize, opacity: watermarkOpacity },
260
262
  }));
261
263
  const feesMutation = useSave("Fees", () => ({
262
- fees: { platformCommission, buyerFee, razorpayFeePercent: razorpayFee, payoutHoldDays, minPayoutAmount, auctionListingFee, preOrderListingFee, featuredSlotFee, promotedSlotFee },
264
+ commissions: { platformFeePercent, gstPercent, minimumTransactionFee, gatewayFeePercent, payoutHoldDays, minPayoutAmount, auctionListingFee, preOrderListingFee, featuredSlotFee, promotedSlotFee },
263
265
  }));
264
266
  const integrationsMutation = useSave("Integrations", () => ({
265
267
  credentials: {
@@ -328,7 +330,7 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
328
330
  ["limits", "⑪ Limits"],
329
331
  ["legal", "⑫ Legal"],
330
332
  ["whatsapp", "⑬ WhatsApp"],
331
- ].map(([value, label]) => (_jsx(TabsTrigger, { value: value, children: label }, value))) }), _jsx(TabsContent, { value: "about", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); aboutMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Override the About page hero and mission text. Leave blank to use the platform defaults." }), _jsx(Input, { label: "Hero title", value: aboutTitle, onChange: (e) => setAboutTitle(e.target.value), placeholder: "About LetItRip" }), _jsx(Input, { label: "Hero subtitle", value: aboutSubtitle, onChange: (e) => setAboutSubtitle(e.target.value), placeholder: "Connecting buyers, sellers, and bidders in one vibrant marketplace" }), _jsx(Input, { label: "Mission section title", value: aboutMissionTitle, onChange: (e) => setAboutMissionTitle(e.target.value), placeholder: "Our Mission" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Mission text" }), _jsx("textarea", { value: aboutMissionText, onChange: (e) => setAboutMissionText(e.target.value), placeholder: "LetItRip was built to democratise commerce\u2026", rows: 4, className: "w-full rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 p-3 text-sm text-zinc-800 dark:text-zinc-200 resize-y focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsx(Input, { label: "CTA banner title", value: aboutCtaTitle, onChange: (e) => setAboutCtaTitle(e.target.value), placeholder: "Ready to get started?" }), _jsx(GroupSaveButton, { isPending: aboutMutation.isPending })] }) }), _jsx(TabsContent, { value: "branding", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); brandingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Site name", value: siteName, onChange: (e) => setSiteName(e.target.value), placeholder: "LetItRip" }), _jsx(Input, { label: "Tagline", value: tagline, onChange: (e) => setTagline(e.target.value), placeholder: "India's Largest Collectibles Marketplace" }), _jsx(ImageUpload, { label: "Logo", currentImage: logoUrl, onUpload: (file) => upload(file, "store"), onChange: setLogoUrl }), _jsx(ImageUpload, { label: "Favicon", currentImage: faviconUrl, onUpload: (file) => upload(file, "store"), onChange: setFaviconUrl }), _jsxs("div", { className: "space-y-3 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4", children: [_jsx(Toggle, { label: "Maintenance mode", checked: maintenanceMode, onChange: setMaintenanceMode }), _jsx(Input, { label: "Maintenance message", value: maintenanceMessage, onChange: (e) => setMaintenanceMessage(e.target.value), placeholder: "We're back soon.", disabled: !maintenanceMode })] }), _jsx(GroupSaveButton, { isPending: brandingMutation.isPending })] }) }), _jsx(TabsContent, { value: "appearance", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); appearanceMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Primary color" }), _jsx("input", { type: "color", value: primaryColor || "#000000", onChange: (e) => setPrimaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Secondary color" }), _jsx("input", { type: "color", value: secondaryColor || "#000000", onChange: (e) => setSecondaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Accent color" }), _jsx("input", { type: "color", value: accentColor || "#000000", onChange: (e) => setAccentColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Select, { label: "Default theme", options: THEME_OPTIONS, value: defaultTheme, onValueChange: setDefaultTheme }), _jsx(Select, { label: "Font family", options: FONT_OPTIONS, value: fontFamily, onValueChange: setFontFamily })] }), _jsx(GroupSaveButton, { isPending: appearanceMutation.isPending })] }) }), _jsx(TabsContent, { value: "announcement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); announcementMutation.mutate(); }, className: "space-y-4 pt-4", 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: "/products", disabled: !announcementEnabled }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Background color" }), _jsx("input", { type: "color", value: announcementBg || "#1d4ed8", onChange: (e) => setAnnouncementBg(e.target.value), className: "h-10 w-32 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer", disabled: !announcementEnabled })] }), _jsx(GroupSaveButton, { isPending: announcementMutation.isPending })] }) }), _jsx(TabsContent, { value: "seo", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); seoMutation.mutate(); }, className: "space-y-4 pt-4", 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(GroupSaveButton, { isPending: seoMutation.isPending })] }) }), _jsx(TabsContent, { value: "contact", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); contactMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", 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("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400 pt-2", children: "Social links" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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(GroupSaveButton, { isPending: contactMutation.isPending })] }) }), _jsx(TabsContent, { value: "watermark", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); watermarkMutation.mutate(); }, className: "space-y-4 pt-4", 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("div", { className: "rounded-lg border border-zinc-200 dark:border-zinc-700 p-4 bg-zinc-50 dark:bg-zinc-800", children: [_jsx("p", { className: "text-xs text-zinc-500 mb-2", children: "Preview (text watermark only)" }), _jsx("div", { className: "relative bg-white dark:bg-zinc-900 rounded h-32 flex items-end justify-end overflow-hidden", children: _jsx("span", { className: "text-zinc-400 select-none p-2 font-medium", style: { fontSize: `${Math.max(10, watermarkSize / 5)}px`, opacity: watermarkOpacity / 100 }, children: watermarkText }) })] }), _jsx(GroupSaveButton, { isPending: watermarkMutation.isPending })] }) }), _jsx(TabsContent, { value: "fees", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); feesMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Platform commission (%)", value: String(platformCommission), onChange: (e) => setPlatformCommission(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "Buyer convenience fee (%)", value: String(buyerFee), onChange: (e) => setBuyerFee(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "Razorpay gateway fee (%)", value: String(razorpayFee), onChange: (e) => setRazorpayFee(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 10, 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(GroupSaveButton, { isPending: feesMutation.isPending })] }) }), _jsx(TabsContent, { value: "integrations", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); integrationsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Keys are masked in transit and stored encrypted. Click Reveal to view." }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Razorpay" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Shiprocket" }), _jsx(MaskedInput, { label: "Shiprocket API token", value: shiprocketToken, onChange: setShiprocketToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "SMTP / Email" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Analytics & Tracking" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Meta (Instagram & Facebook Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Used by the Social Feed section to fetch Instagram and Facebook posts via Meta Graph API v19." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "TikTok for Developers (Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Client credentials + long-lived access token from TikTok for Developers. Used to list your account's public videos." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "DeviantArt OAuth2 (Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Client credentials for DeviantArt gallery fetching (client-credentials OAuth2 flow \u2014 no user login required)." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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(GroupSaveButton, { isPending: integrationsMutation.isPending })] }) }), _jsx(TabsContent, { value: "shipping", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); shippingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_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(Toggle, { label: "Cash on delivery (COD) enabled", checked: codEnabled, onChange: setCodEnabled }), _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(GroupSaveButton, { isPending: shippingMutation.isPending })] }) }), _jsx(TabsContent, { value: "auction", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); auctionMutation.mutate(); }, className: "space-y-4 pt-4", 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(GroupSaveButton, { isPending: auctionMutation.isPending })] }) }), _jsx(TabsContent, { value: "limits", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); limitsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", 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(GroupSaveButton, { isPending: limitsMutation.isPending })] }) }), _jsx(TabsContent, { value: "whatsapp", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); whatsappMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", 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(GroupSaveButton, { isPending: whatsappMutation.isPending })] }) }), _jsx(TabsContent, { value: "legal", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); legalMutation.mutate(); }, className: "space-y-5 pt-4", children: [[
333
+ ].map(([value, label]) => (_jsx(TabsTrigger, { value: value, children: label }, value))) }), _jsx(TabsContent, { value: "about", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); aboutMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Override the About page hero and mission text. Leave blank to use the platform defaults." }), _jsx(Input, { label: "Hero title", value: aboutTitle, onChange: (e) => setAboutTitle(e.target.value), placeholder: "About LetItRip" }), _jsx(Input, { label: "Hero subtitle", value: aboutSubtitle, onChange: (e) => setAboutSubtitle(e.target.value), placeholder: "Connecting buyers, sellers, and bidders in one vibrant marketplace" }), _jsx(Input, { label: "Mission section title", value: aboutMissionTitle, onChange: (e) => setAboutMissionTitle(e.target.value), placeholder: "Our Mission" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Mission text" }), _jsx("textarea", { value: aboutMissionText, onChange: (e) => setAboutMissionText(e.target.value), placeholder: "LetItRip was built to democratise commerce\u2026", rows: 4, className: "w-full rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 p-3 text-sm text-zinc-800 dark:text-zinc-200 resize-y focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsx(Input, { label: "CTA banner title", value: aboutCtaTitle, onChange: (e) => setAboutCtaTitle(e.target.value), placeholder: "Ready to get started?" }), _jsx(GroupSaveButton, { isPending: aboutMutation.isPending })] }) }), _jsx(TabsContent, { value: "branding", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); brandingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Site name", value: siteName, onChange: (e) => setSiteName(e.target.value), placeholder: "LetItRip" }), _jsx(Input, { label: "Tagline", value: tagline, onChange: (e) => setTagline(e.target.value), placeholder: "India's Largest Collectibles Marketplace" }), _jsx(ImageUpload, { label: "Logo", currentImage: logoUrl, onUpload: (file) => upload(file, "store"), onChange: setLogoUrl }), _jsx(ImageUpload, { label: "Favicon", currentImage: faviconUrl, onUpload: (file) => upload(file, "store"), onChange: setFaviconUrl }), _jsxs("div", { className: "space-y-3 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4", children: [_jsx(Toggle, { label: "Maintenance mode", checked: maintenanceMode, onChange: setMaintenanceMode }), _jsx(Input, { label: "Maintenance message", value: maintenanceMessage, onChange: (e) => setMaintenanceMessage(e.target.value), placeholder: "We're back soon.", disabled: !maintenanceMode })] }), _jsx(GroupSaveButton, { isPending: brandingMutation.isPending })] }) }), _jsx(TabsContent, { value: "appearance", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); appearanceMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Primary color" }), _jsx("input", { type: "color", value: primaryColor || "#000000", onChange: (e) => setPrimaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Secondary color" }), _jsx("input", { type: "color", value: secondaryColor || "#000000", onChange: (e) => setSecondaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Accent color" }), _jsx("input", { type: "color", value: accentColor || "#000000", onChange: (e) => setAccentColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Select, { label: "Default theme", options: THEME_OPTIONS, value: defaultTheme, onValueChange: setDefaultTheme }), _jsx(Select, { label: "Font family", options: FONT_OPTIONS, value: fontFamily, onValueChange: setFontFamily })] }), _jsx(GroupSaveButton, { isPending: appearanceMutation.isPending })] }) }), _jsx(TabsContent, { value: "announcement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); announcementMutation.mutate(); }, className: "space-y-4 pt-4", 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: "/products", disabled: !announcementEnabled }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Background color" }), _jsx("input", { type: "color", value: announcementBg || "#1d4ed8", onChange: (e) => setAnnouncementBg(e.target.value), className: "h-10 w-32 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer", disabled: !announcementEnabled })] }), _jsx(GroupSaveButton, { isPending: announcementMutation.isPending })] }) }), _jsx(TabsContent, { value: "seo", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); seoMutation.mutate(); }, className: "space-y-4 pt-4", 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(GroupSaveButton, { isPending: seoMutation.isPending })] }) }), _jsx(TabsContent, { value: "contact", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); contactMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", 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("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400 pt-2", children: "Social links" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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(GroupSaveButton, { isPending: contactMutation.isPending })] }) }), _jsx(TabsContent, { value: "watermark", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); watermarkMutation.mutate(); }, className: "space-y-4 pt-4", 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("div", { className: "rounded-lg border border-zinc-200 dark:border-zinc-700 p-4 bg-zinc-50 dark:bg-zinc-800", children: [_jsx("p", { className: "text-xs text-zinc-500 mb-2", children: "Preview (text watermark only)" }), _jsx("div", { className: "relative bg-white dark:bg-zinc-900 rounded h-32 flex items-end justify-end overflow-hidden", children: _jsx("span", { className: "text-zinc-400 select-none p-2 font-medium", style: { fontSize: `${Math.max(10, watermarkSize / 5)}px`, opacity: watermarkOpacity / 100 }, children: watermarkText }) })] }), _jsx(GroupSaveButton, { isPending: watermarkMutation.isPending })] }) }), _jsx(TabsContent, { value: "fees", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); feesMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", 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(GroupSaveButton, { isPending: feesMutation.isPending })] }) }), _jsx(TabsContent, { value: "integrations", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); integrationsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Keys are masked in transit and stored encrypted. Click Reveal to view." }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Razorpay" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Shiprocket" }), _jsx(MaskedInput, { label: "Shiprocket API token", value: shiprocketToken, onChange: setShiprocketToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "SMTP / Email" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Analytics & Tracking" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Meta (Instagram & Facebook Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Used by the Social Feed section to fetch Instagram and Facebook posts via Meta Graph API v19." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "TikTok for Developers (Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Client credentials + long-lived access token from TikTok for Developers. Used to list your account's public videos." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "DeviantArt OAuth2 (Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Client credentials for DeviantArt gallery fetching (client-credentials OAuth2 flow \u2014 no user login required)." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", 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(GroupSaveButton, { isPending: integrationsMutation.isPending })] }) }), _jsx(TabsContent, { value: "shipping", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); shippingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_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(Toggle, { label: "Cash on delivery (COD) enabled", checked: codEnabled, onChange: setCodEnabled }), _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(GroupSaveButton, { isPending: shippingMutation.isPending })] }) }), _jsx(TabsContent, { value: "auction", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); auctionMutation.mutate(); }, className: "space-y-4 pt-4", 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(GroupSaveButton, { isPending: auctionMutation.isPending })] }) }), _jsx(TabsContent, { value: "limits", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); limitsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", 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(GroupSaveButton, { isPending: limitsMutation.isPending })] }) }), _jsx(TabsContent, { value: "whatsapp", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); whatsappMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", 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(GroupSaveButton, { isPending: whatsappMutation.isPending })] }) }), _jsx(TabsContent, { value: "legal", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); legalMutation.mutate(); }, className: "space-y-5 pt-4", children: [[
332
334
  ["Terms of Service", termsHtml, setTermsHtml],
333
335
  ["Privacy Policy", privacyHtml, setPrivacyHtml],
334
336
  ["Refund Policy", refundHtml, setRefundHtml],
@@ -1,21 +1,29 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Div, Text, Grid } from "../../../ui";
3
+ import { TrendingUp, ShoppingBag, Users, Package, Clock, Star } from "lucide-react";
4
+ import { Grid } from "../../../ui";
3
5
  import { formatCurrency } from "../../../utils/number.formatter";
4
6
  import { getDefaultCurrency } from "../../../core/baseline-resolver";
5
- function StatCard({ label, value, sub, color = "default" }) {
6
- const colorClass = {
7
- default: "bg-white dark:bg-slate-900",
8
- blue: "bg-blue-50 dark:bg-blue-900/20",
9
- green: "bg-green-50 dark:bg-green-900/20",
10
- amber: "bg-amber-50 dark:bg-amber-900/20",
11
- red: "bg-red-50 dark:bg-red-900/20",
12
- }[color];
13
- return (_jsxs(Div, { className: `rounded-xl border border-neutral-200 dark:border-slate-700 p-5 ${colorClass}`, children: [_jsx(Text, { className: "text-xs font-medium uppercase tracking-wide text-neutral-500 dark:text-zinc-400", children: label }), _jsx(Text, { className: "mt-2 text-2xl font-bold text-neutral-900 dark:text-zinc-100", children: value }), sub && _jsx(Text, { className: "mt-1 text-xs text-neutral-500 dark:text-zinc-400", children: sub })] }));
7
+ // Brand gradient stop colors (mirrors SiteLogo wordmark gradient)
8
+ const BRAND_FROM = "var(--appkit-color-primary-700, #1343de)";
9
+ const BRAND_MID = "var(--appkit-color-cobalt, #3570fc)";
10
+ const BRAND_TO = "var(--appkit-color-secondary-400, #84e122)";
11
+ function StatCard({ label, value, sub, icon: Icon, gradient, iconColor }) {
12
+ return (_jsxs("div", { className: "relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm hover:shadow-md transition-shadow", children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]", style: { background: gradient }, "aria-hidden": "true" }), _jsxs("div", { className: "px-5 pb-5 pt-6 flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)]", children: label }), _jsx("p", { className: "mt-2 text-2xl font-bold text-[var(--appkit-color-text)] tabular-nums leading-none", children: value }), sub && (_jsx("p", { className: "mt-1.5 text-xs text-[var(--appkit-color-text-muted)]", children: sub }))] }), _jsx("div", { className: "flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center", style: { background: gradient, opacity: 0.92 }, children: _jsx(Icon, { className: "w-5 h-5 text-white drop-shadow-sm" }) })] })] }));
13
+ }
14
+ function SkeletonCard() {
15
+ return (_jsxs("div", { className: "relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden p-5 animate-pulse", children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px] bg-[var(--appkit-color-border)]" }), _jsxs("div", { className: "flex items-start justify-between gap-3 pt-1", children: [_jsxs("div", { className: "flex-1 space-y-2", children: [_jsx("div", { className: "h-2.5 w-20 rounded bg-[var(--appkit-color-border)]" }), _jsx("div", { className: "h-7 w-24 rounded bg-[var(--appkit-color-border)]" })] }), _jsx("div", { className: "w-10 h-10 rounded-lg bg-[var(--appkit-color-border)]" })] })] }));
14
16
  }
15
17
  export function DashboardStatsGrid({ stats, isLoading, labels = {}, }) {
16
18
  if (isLoading) {
17
- return (_jsx(Grid, { cols: "statTiles", children: Array.from({ length: 4 }).map((_, i) => (_jsxs(Div, { className: "animate-pulse rounded-xl border border-neutral-200 dark:border-slate-700 p-5", children: [_jsx(Div, { className: "h-3 w-20 rounded bg-neutral-200 dark:bg-slate-700" }), _jsx(Div, { className: "mt-2 h-8 w-24 rounded bg-neutral-200 dark:bg-slate-700" })] }, i))) }));
19
+ return (_jsx(Grid, { cols: "statTiles", children: Array.from({ length: 4 }).map((_, i) => _jsx(SkeletonCard, {}, i)) }));
18
20
  }
19
21
  const currency = stats.currency ?? getDefaultCurrency();
20
- return (_jsxs(Grid, { cols: "statTiles", children: [_jsx(StatCard, { label: labels.totalOrders ?? "Total Orders", value: stats.totalOrders ?? 0 }), _jsx(StatCard, { label: labels.totalRevenue ?? "Total Revenue", value: formatCurrency(stats.totalRevenue ?? 0, currency), color: "green" }), _jsx(StatCard, { label: labels.totalUsers ?? "Total Users", value: stats.totalUsers ?? 0, color: "blue" }), _jsx(StatCard, { label: labels.totalProducts ?? "Total Products", value: stats.totalProducts ?? 0 }), stats.pendingOrders !== undefined && (_jsx(StatCard, { label: labels.pendingOrders ?? "Pending Orders", value: stats.pendingOrders, color: "amber" })), stats.pendingReviews !== undefined && (_jsx(StatCard, { label: labels.pendingReviews ?? "Pending Reviews", value: stats.pendingReviews, color: "amber" }))] }));
22
+ // Brand gradient (left→right, matches logo wordmark)
23
+ const brandGrad = `linear-gradient(135deg, ${BRAND_FROM} 0%, ${BRAND_MID} 55%, ${BRAND_TO} 100%)`;
24
+ const blueGrad = `linear-gradient(135deg, ${BRAND_FROM} 0%, ${BRAND_MID} 100%)`;
25
+ const greenGrad = `linear-gradient(135deg, ${BRAND_MID} 0%, ${BRAND_TO} 100%)`;
26
+ const amberGrad = "linear-gradient(135deg, #f59e0b 0%, #f97316 100%)";
27
+ const roseGrad = "linear-gradient(135deg, #f43f5e 0%, #e11d48 100%)";
28
+ return (_jsxs(Grid, { cols: "statTiles", children: [_jsx(StatCard, { label: labels.totalOrders ?? "Total Orders", value: stats.totalOrders ?? 0, icon: ShoppingBag, gradient: brandGrad, iconColor: "#fff" }), _jsx(StatCard, { label: labels.totalRevenue ?? "Total Revenue", value: formatCurrency(stats.totalRevenue ?? 0, currency), icon: TrendingUp, gradient: greenGrad, iconColor: "#fff" }), _jsx(StatCard, { label: labels.totalUsers ?? "Total Users", value: stats.totalUsers ?? 0, icon: Users, gradient: blueGrad, iconColor: "#fff" }), _jsx(StatCard, { label: labels.totalProducts ?? "Total Products", value: stats.totalProducts ?? 0, icon: Package, gradient: brandGrad, iconColor: "#fff" }), stats.pendingOrders !== undefined && (_jsx(StatCard, { label: labels.pendingOrders ?? "Pending Orders", value: stats.pendingOrders, icon: Clock, gradient: amberGrad, iconColor: "#fff" })), stats.pendingReviews !== undefined && (_jsx(StatCard, { label: labels.pendingReviews ?? "Pending Reviews", value: stats.pendingReviews, icon: Star, gradient: roseGrad, iconColor: "#fff" }))] }));
21
29
  }
@@ -13,9 +13,25 @@ const XAxis = dynamic(() => import("recharts").then((m) => m.XAxis), { ssr: fals
13
13
  const YAxis = dynamic(() => import("recharts").then((m) => m.YAxis), { ssr: false });
14
14
  const CartesianGrid = dynamic(() => import("recharts").then((m) => m.CartesianGrid), { ssr: false });
15
15
  const Tooltip = dynamic(() => import("recharts").then((m) => m.Tooltip), { ssr: false });
16
+ // Shared chart card wrapper
17
+ function ChartCard({ title, children, className = "", }) {
18
+ return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]", style: {
19
+ background: "linear-gradient(to right, var(--appkit-color-primary-700,#1343de) 0%, var(--appkit-color-cobalt,#3570fc) 55%, var(--appkit-color-secondary-400,#84e122) 100%)",
20
+ }, "aria-hidden": "true" }), _jsxs(Div, { className: "p-4 sm:p-6 pt-5 sm:pt-7", children: [title && (_jsx(Heading, { level: 3, className: "text-sm font-semibold text-[var(--appkit-color-text)] mb-4", children: title })), children] })] }));
21
+ }
16
22
  export function AdminRevenueChart({ data, labels = {}, className = "", }) {
17
- return (_jsxs(Div, { className: `bg-white dark:bg-neutral-900 rounded-xl border border-neutral-200 dark:border-neutral-800 p-4 sm:p-6 ${className}`, children: [labels.title && (_jsx(Heading, { level: 3, className: "text-base font-semibold mb-4", children: labels.title })), _jsx(ResponsiveContainer, { width: "100%", height: 300, children: _jsxs(AreaChart, { data: data, children: [_jsx("defs", { children: _jsxs("linearGradient", { id: "colorRevenue", x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "5%", stopColor: "var(--appkit-color-primary)", stopOpacity: 0.8 }), _jsx("stop", { offset: "95%", stopColor: "var(--appkit-color-primary)", stopOpacity: 0 })] }) }), _jsx(CartesianGrid, { strokeDasharray: "3 3" }), _jsx(XAxis, { dataKey: "month" }), _jsx(YAxis, {}), _jsx(Tooltip, {}), _jsx(Area, { type: "monotone", dataKey: "revenue", stroke: "var(--appkit-color-primary)", fillOpacity: 1, fill: "url(#colorRevenue)" })] }) })] }));
23
+ return (_jsx(ChartCard, { title: labels.title, className: className, children: _jsx(ResponsiveContainer, { width: "100%", height: 280, children: _jsxs(AreaChart, { data: data, margin: { top: 4, right: 4, left: 0, bottom: 0 }, children: [_jsxs("defs", { children: [_jsxs("linearGradient", { id: "revenueGrad", x1: "0", y1: "0", x2: "1", y2: "0", children: [_jsx("stop", { offset: "0%", stopColor: "#1343de" }), _jsx("stop", { offset: "55%", stopColor: "#3570fc" }), _jsx("stop", { offset: "100%", stopColor: "#84e122" })] }), _jsxs("linearGradient", { id: "revenueAreaGrad", x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "5%", stopColor: "#3570fc", stopOpacity: 0.25 }), _jsx("stop", { offset: "95%", stopColor: "#3570fc", stopOpacity: 0 })] })] }), _jsx(CartesianGrid, { strokeDasharray: "4 4", stroke: "var(--appkit-color-border)", vertical: false }), _jsx(XAxis, { dataKey: "month", tick: { fontSize: 11, fill: "var(--appkit-color-text-muted)" }, axisLine: false, tickLine: false }), _jsx(YAxis, { tick: { fontSize: 11, fill: "var(--appkit-color-text-muted)" }, axisLine: false, tickLine: false, width: 48 }), _jsx(Tooltip, { contentStyle: {
24
+ background: "var(--appkit-color-surface)",
25
+ border: "1px solid var(--appkit-color-border)",
26
+ borderRadius: "8px",
27
+ fontSize: 12,
28
+ }, cursor: { stroke: "var(--appkit-color-border)", strokeWidth: 1 } }), _jsx(Area, { type: "monotone", dataKey: "revenue", stroke: "url(#revenueGrad)", strokeWidth: 2.5, fillOpacity: 1, fill: "url(#revenueAreaGrad)", dot: false, activeDot: { r: 4, fill: "#3570fc" } })] }) }) }));
18
29
  }
19
30
  export function AdminOrdersChart({ data, labels = {}, className = "", }) {
20
- return (_jsxs(Div, { className: `bg-white dark:bg-neutral-900 rounded-xl border border-neutral-200 dark:border-neutral-800 p-4 sm:p-6 ${className}`, children: [labels.title && (_jsx(Heading, { level: 3, className: "text-base font-semibold mb-4", children: labels.title })), _jsx(ResponsiveContainer, { width: "100%", height: 300, children: _jsxs(BarChart, { data: data, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3" }), _jsx(XAxis, { dataKey: "month" }), _jsx(YAxis, {}), _jsx(Tooltip, {}), _jsx(Bar, { dataKey: "orders", fill: "var(--appkit-color-secondary)" })] }) })] }));
31
+ return (_jsx(ChartCard, { title: labels.title, className: className, children: _jsx(ResponsiveContainer, { width: "100%", height: 280, children: _jsxs(BarChart, { data: data, margin: { top: 4, right: 4, left: 0, bottom: 0 }, barSize: 20, children: [_jsx("defs", { children: _jsxs("linearGradient", { id: "ordersBarGrad", x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: "#3570fc", stopOpacity: 0.9 }), _jsx("stop", { offset: "100%", stopColor: "#84e122", stopOpacity: 0.7 })] }) }), _jsx(CartesianGrid, { strokeDasharray: "4 4", stroke: "var(--appkit-color-border)", vertical: false }), _jsx(XAxis, { dataKey: "month", tick: { fontSize: 11, fill: "var(--appkit-color-text-muted)" }, axisLine: false, tickLine: false }), _jsx(YAxis, { tick: { fontSize: 11, fill: "var(--appkit-color-text-muted)" }, axisLine: false, tickLine: false, width: 40 }), _jsx(Tooltip, { contentStyle: {
32
+ background: "var(--appkit-color-surface)",
33
+ border: "1px solid var(--appkit-color-border)",
34
+ borderRadius: "8px",
35
+ fontSize: 12,
36
+ }, cursor: { fill: "var(--appkit-color-border)", opacity: 0.3 } }), _jsx(Bar, { dataKey: "orders", fill: "url(#ordersBarGrad)", radius: [4, 4, 0, 0] })] }) }) }));
21
37
  }
@@ -1,8 +1,14 @@
1
1
  import React from "react";
2
+ declare const GRADIENTS: Record<string, string>;
2
3
  export interface AdminStatCardProps {
3
4
  label: string;
4
5
  value: string;
6
+ sub?: string;
7
+ /** Emoji or ReactNode icon */
5
8
  icon?: React.ReactNode | string;
9
+ /** Named preset or raw CSS gradient string */
10
+ gradient?: keyof typeof GRADIENTS | string;
6
11
  className?: string;
7
12
  }
8
- export declare function AdminStatCard({ label, value, icon, className, }: AdminStatCardProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function AdminStatCard({ label, value, sub, icon, gradient, className, }: AdminStatCardProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -1,5 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Div, Text, Heading } from "../../../../ui";
3
- export function AdminStatCard({ label, value, icon, className = "", }) {
4
- return (_jsx(Div, { className: `bg-white dark:bg-neutral-900 rounded-xl border border-neutral-200 dark:border-neutral-800 p-4 sm:p-6 ${className}`, children: _jsxs(Div, { className: "flex items-start justify-between", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-sm text-neutral-600 dark:text-neutral-400 mb-1", children: label }), _jsx(Heading, { level: 3, className: "text-2xl font-bold", children: value })] }), icon && _jsx(Div, { className: "text-2xl", children: icon })] }) }));
3
+ const BRAND_FROM = "var(--appkit-color-primary-700, #1343de)";
4
+ const BRAND_MID = "var(--appkit-color-cobalt, #3570fc)";
5
+ const BRAND_TO = "var(--appkit-color-secondary-400, #84e122)";
6
+ const GRADIENTS = {
7
+ brand: `linear-gradient(135deg, ${BRAND_FROM} 0%, ${BRAND_MID} 55%, ${BRAND_TO} 100%)`,
8
+ blue: `linear-gradient(135deg, ${BRAND_FROM} 0%, ${BRAND_MID} 100%)`,
9
+ green: `linear-gradient(135deg, ${BRAND_MID} 0%, ${BRAND_TO} 100%)`,
10
+ amber: "linear-gradient(135deg, #f59e0b 0%, #f97316 100%)",
11
+ rose: "linear-gradient(135deg, #f43f5e 0%, #e11d48 100%)",
12
+ };
13
+ export function AdminStatCard({ label, value, sub, icon, gradient = "brand", className = "", }) {
14
+ const resolvedGradient = GRADIENTS[gradient] ?? gradient;
15
+ return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]", style: { background: resolvedGradient }, "aria-hidden": "true" }), _jsxs(Div, { className: "px-5 pb-5 pt-6 flex items-start justify-between gap-3", children: [_jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)]", children: label }), _jsx(Heading, { level: 3, className: "mt-2 text-2xl font-bold tabular-nums leading-none text-[var(--appkit-color-text)]", children: value }), sub && (_jsx(Text, { className: "mt-1.5 text-xs text-[var(--appkit-color-text-muted)]", children: sub }))] }), icon && (_jsx(Div, { className: "flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center text-xl", style: { background: resolvedGradient }, children: icon }))] })] }));
5
16
  }
@@ -232,15 +232,27 @@ export interface SiteSettingsDocument {
232
232
  codEnabled: boolean;
233
233
  };
234
234
  commissions: {
235
- razorpayFeePercent: number;
235
+ /** Our platform cut charged to the buyer as a % of order value (e.g. 5 = 5%). */
236
+ platformFeePercent: number;
237
+ /** GST applied on top of our platform fee (18%). buyer pays platformFee × (1 + gstPercent/100). */
238
+ gstPercent: number;
239
+ /** Per-transaction gateway minimum fee in rupees. Ensures total charge is never below this floor. 0 = no minimum. */
240
+ minimumTransactionFee: number;
241
+ /** Razorpay gateway cost % (absorbed by platform, not passed through separately). */
242
+ gatewayFeePercent: number;
236
243
  codDepositPercent: number;
237
244
  sellerShippingFixed: number;
238
245
  platformShippingPercent: number;
239
246
  platformShippingFixedMin: number;
240
- processingFeePercent?: number;
241
- gstPercent?: number;
242
- gatewayFeePercent?: number;
243
247
  autoPayoutWindowDays?: number;
248
+ /** Days before a settled order's funds are released for payout. */
249
+ payoutHoldDays?: number;
250
+ /** Minimum payout amount in rupees. */
251
+ minPayoutAmount?: number;
252
+ auctionListingFee?: number;
253
+ preOrderListingFee?: number;
254
+ featuredSlotFee?: number;
255
+ promotedSlotFee?: number;
244
256
  };
245
257
  socialLinks: {
246
258
  facebook?: string;
@@ -280,6 +292,8 @@ export interface SiteSettingsDocument {
280
292
  sellerRegistration: boolean;
281
293
  preOrders: boolean;
282
294
  seedPanel: boolean;
295
+ /** When true, admin users see a bypass button in checkout that skips OTP and payment. Server-enforced. */
296
+ adminCheckoutBypass?: boolean;
283
297
  listingTypes?: {
284
298
  standard?: boolean;
285
299
  auction?: boolean;
@@ -136,11 +136,21 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
136
136
  codEnabled: true,
137
137
  },
138
138
  commissions: {
139
- razorpayFeePercent: 5,
139
+ platformFeePercent: 5,
140
+ gstPercent: 18,
141
+ minimumTransactionFee: 0,
142
+ gatewayFeePercent: 2,
140
143
  codDepositPercent: 10,
141
144
  sellerShippingFixed: 0,
142
145
  platformShippingPercent: 10,
143
146
  platformShippingFixedMin: 0,
147
+ autoPayoutWindowDays: 7,
148
+ payoutHoldDays: 2,
149
+ minPayoutAmount: 100,
150
+ auctionListingFee: 0,
151
+ preOrderListingFee: 0,
152
+ featuredSlotFee: 999,
153
+ promotedSlotFee: 499,
144
154
  },
145
155
  featureFlags: {
146
156
  chats: true,
@@ -4,5 +4,7 @@ export interface BundleBuyNowCtaProps {
4
4
  onBuyNow: (input: {
5
5
  bundleSlug: string;
6
6
  }) => Promise<unknown>;
7
+ /** Renders a smaller single-line button suited for list cards. */
8
+ compact?: boolean;
7
9
  }
8
- export declare function BundleBuyNowCta({ bundleSlug, outOfStock, onBuyNow, }: BundleBuyNowCtaProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare function BundleBuyNowCta({ bundleSlug, outOfStock, onBuyNow, compact, }: BundleBuyNowCtaProps): import("react/jsx-runtime").JSX.Element;
@@ -12,7 +12,7 @@ import { Button, LoginRequiredModal, Stack, Text } from "../../../ui";
12
12
  import { useToast } from "../../../ui";
13
13
  import { isAuthError } from "../../../utils/auth-error";
14
14
  import { BUNDLE_COPY } from "../../../_internal/shared/features/categories/bundle-copy";
15
- export function BundleBuyNowCta({ bundleSlug, outOfStock = false, onBuyNow, }) {
15
+ export function BundleBuyNowCta({ bundleSlug, outOfStock = false, onBuyNow, compact = false, }) {
16
16
  const { showToast } = useToast();
17
17
  const [submitting, setSubmitting] = useState(false);
18
18
  const [error, setError] = useState(null);
@@ -38,7 +38,7 @@ export function BundleBuyNowCta({ bundleSlug, outOfStock = false, onBuyNow, }) {
38
38
  }
39
39
  }, [bundleSlug, onBuyNow, showToast]);
40
40
  if (outOfStock) {
41
- return (_jsxs(Stack, { gap: "xs", "aria-live": "polite", children: [_jsx(Button, { variant: "primary", disabled: true, "aria-disabled": true, children: BUNDLE_COPY.detail.ctaOutOfStock }), _jsx(Text, { size: "xs", color: "muted", children: BUNDLE_COPY.detail.ctaHint })] }));
41
+ return (_jsxs(Stack, { gap: "xs", "aria-live": "polite", children: [_jsx(Button, { variant: "primary", size: compact ? "sm" : "md", disabled: true, "aria-disabled": true, className: compact ? "w-full" : undefined, children: BUNDLE_COPY.detail.ctaOutOfStock }), !compact && (_jsx(Text, { size: "xs", color: "muted", children: BUNDLE_COPY.detail.ctaHint }))] }));
42
42
  }
43
- return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to purchase this bundle. Please log in or create an account to continue." }), _jsxs(Stack, { gap: "sm", children: [_jsx(Button, { variant: "primary", onClick: handleClick, disabled: submitting, "aria-busy": submitting, children: submitting ? BUNDLE_COPY.detail.ctaAdding : BUNDLE_COPY.detail.ctaBuyNow }), error && (_jsx(Text, { size: "sm", color: "danger", role: "alert", children: error }))] })] }));
43
+ return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to purchase this bundle. Please log in or create an account to continue." }), _jsxs(Stack, { gap: "sm", children: [_jsx(Button, { variant: "primary", size: compact ? "sm" : "md", onClick: handleClick, disabled: submitting, "aria-busy": submitting, className: compact ? "w-full" : undefined, children: submitting ? BUNDLE_COPY.detail.ctaAdding : BUNDLE_COPY.detail.ctaBuyNow }), error && (_jsx(Text, { size: "sm", color: "danger", role: "alert", children: error }))] })] }));
44
44
  }
@@ -4,7 +4,7 @@ type DynamicRule = {
4
4
  categorySlug?: string;
5
5
  brandSlug?: string;
6
6
  tags?: string[];
7
- listingType?: "standard" | "pre-order";
7
+ listingType?: "standard" | "pre-order" | "prize-draw";
8
8
  };
9
9
  orderBy?: "price-asc" | "price-desc" | "createdAt-desc";
10
10
  limit: number;
@@ -29,9 +29,10 @@ const ORDER_BY_OPTIONS = [
29
29
  { label: "Price (high → low)", value: "price-desc" },
30
30
  ];
31
31
  const LISTING_TYPE_OPTIONS = [
32
- { label: "Any (standard + pre-order)", value: "" },
32
+ { label: "Any", value: "" },
33
33
  { label: "Standard only", value: "standard" },
34
34
  { label: "Pre-order only", value: "pre-order" },
35
+ { label: "Prize draw only", value: "prize-draw" },
35
36
  ];
36
37
  export function BundleDynamicRuleEditor({ value, onChange, disabled = false, }) {
37
38
  const rule = value ?? DEFAULT_DYNAMIC_RULE;
@@ -1,6 +1,9 @@
1
1
  type SearchParams = Record<string, string | string[]>;
2
2
  export interface BundlesListViewProps {
3
3
  searchParams?: SearchParams;
4
+ onBuyNow?: (input: {
5
+ bundleSlug: string;
6
+ }) => Promise<unknown>;
4
7
  }
5
8
  /**
6
9
  * Public `/bundles` index. Bundles live as `categoryType:"bundle"` rows on
@@ -9,5 +12,5 @@ export interface BundlesListViewProps {
9
12
  * Optional `?storeId=…` post-filters to a single seller's bundles.
10
13
  * Sort UI is owned by `CategoryBundlesListing` (client).
11
14
  */
12
- export declare function BundlesListView({ searchParams, }: BundlesListViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
15
+ export declare function BundlesListView({ searchParams, onBuyNow, }: BundlesListViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
13
16
  export {};
@@ -15,7 +15,7 @@ function sp(params, key) {
15
15
  * Optional `?storeId=…` post-filters to a single seller's bundles.
16
16
  * Sort UI is owned by `CategoryBundlesListing` (client).
17
17
  */
18
- export async function BundlesListView({ searchParams = {}, }) {
18
+ export async function BundlesListView({ searchParams = {}, onBuyNow, }) {
19
19
  const storeId = sp(searchParams, "storeId");
20
20
  const all = await categoriesRepository
21
21
  .listByType("bundle", { activeOnly: true, limit: DEFAULT_LIMIT })
@@ -23,5 +23,5 @@ export async function BundlesListView({ searchParams = {}, }) {
23
23
  const bundles = storeId
24
24
  ? all.filter((c) => c.createdByStoreId === storeId)
25
25
  : all;
26
- return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-2 text-3xl font-semibold text-zinc-900 dark:text-zinc-100", children: "Collectible Bundles" }), _jsx(Text, { className: "mb-6 text-sm text-[var(--appkit-color-text-muted)]", children: "Curated multi-product bundles \u2014 Pok\u00E9mon starter sets, Beyblade tournament packs, Hot Wheels collector boxes and more. One price, one checkout, one shipment." }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(CategoryBundlesListing, { initialBundles: bundles }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
26
+ return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-2 text-3xl font-semibold text-zinc-900 dark:text-zinc-100", children: "Collectible Bundles" }), _jsx(Text, { className: "mb-6 text-sm text-[var(--appkit-color-text-muted)]", children: "Curated multi-product bundles \u2014 Pok\u00E9mon starter sets, Beyblade tournament packs, Hot Wheels collector boxes and more. One price, one checkout, one shipment." }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(CategoryBundlesListing, { initialBundles: bundles, onBuyNow: onBuyNow }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
27
27
  }
@@ -2,5 +2,8 @@ import type { CategoryDocument } from "../schemas";
2
2
  export interface CategoryBundlesListingProps {
3
3
  initialBundles: CategoryDocument[];
4
4
  brandName?: string;
5
+ onBuyNow?: (input: {
6
+ bundleSlug: string;
7
+ }) => Promise<unknown>;
5
8
  }
6
- export declare function CategoryBundlesListing({ initialBundles, brandName, }: CategoryBundlesListingProps): import("react/jsx-runtime").JSX.Element;
9
+ export declare function CategoryBundlesListing({ initialBundles, brandName, onBuyNow, }: CategoryBundlesListingProps): import("react/jsx-runtime").JSX.Element;
@@ -12,6 +12,7 @@ import Link from "next/link";
12
12
  import { Badge, Div, Row, Select, Stack, Text } from "../../../ui";
13
13
  import { ROUTES } from "../../../next/routing/route-map";
14
14
  import { formatCurrency } from "../../../utils/number.formatter";
15
+ import { BundleBuyNowCta } from "./BundleBuyNowCta";
15
16
  const SORT_OPTIONS = [
16
17
  { label: "Newest", value: "newest" },
17
18
  { label: "Price ↑", value: "price-asc" },
@@ -47,7 +48,7 @@ function comparator(sort) {
47
48
  return bT - aT;
48
49
  };
49
50
  }
50
- export function CategoryBundlesListing({ initialBundles, brandName, }) {
51
+ export function CategoryBundlesListing({ initialBundles, brandName, onBuyNow, }) {
51
52
  const [sort, setSort] = useState("newest");
52
53
  const filtered = useMemo(() => initialBundles
53
54
  .filter((c) => c.categoryType === "bundle")
@@ -56,17 +57,17 @@ export function CategoryBundlesListing({ initialBundles, brandName, }) {
56
57
  if (filtered.length === 0) {
57
58
  return (_jsx(Div, { className: "rounded-2xl border border-dashed border-zinc-200 py-16 text-center dark:border-zinc-700", children: _jsxs(Text, { color: "muted", children: [COPY.empty, brandName ? COPY.forBrand(brandName) : "", COPY.yetSuffix] }) }));
58
59
  }
59
- return (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { gap: "sm", align: "center", justify: "end", children: [_jsx(Text, { size: "sm", color: "muted", children: COPY.sortLabel }), _jsx(Select, { options: SORT_OPTIONS, value: sort, onValueChange: setSort, "aria-label": COPY.sortLabel })] }), _jsx(Div, { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3", children: filtered.map((bundle) => (_jsx(BundleCard, { bundle: bundle }, bundle.id))) })] }));
60
+ return (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { gap: "sm", align: "center", justify: "end", children: [_jsx(Text, { size: "sm", color: "muted", children: COPY.sortLabel }), _jsx(Select, { options: SORT_OPTIONS, value: sort, onValueChange: setSort, "aria-label": COPY.sortLabel })] }), _jsx(Div, { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3", children: filtered.map((bundle) => (_jsx(BundleCard, { bundle: bundle, onBuyNow: onBuyNow }, bundle.id))) })] }));
60
61
  }
61
- function BundleCard({ bundle }) {
62
+ function BundleCard({ bundle, onBuyNow }) {
62
63
  const memberCount = bundle.bundleProductIds?.length ?? 0;
63
64
  const stock = bundle.bundleStockStatus ?? "in_stock";
64
65
  const badge = STOCK_BADGE_TEXT[stock];
65
66
  const cover = bundle.display?.coverImage;
66
67
  const href = String(ROUTES.PUBLIC.BUNDLE_DETAIL?.(bundle.slug) ?? "#");
67
- return (_jsxs(Link, { href: href, className: "group rounded-xl border border-zinc-200 bg-white p-3 transition-colors hover:border-zinc-300 dark:border-zinc-800 dark:bg-zinc-900 dark:hover:border-zinc-700", children: [_jsx(Div, { className: "mb-2 aspect-video overflow-hidden rounded-lg bg-zinc-100 dark:bg-zinc-800", children: cover ? (
68
- // eslint-disable-next-line @next/next/no-img-element
69
- _jsx("img", { src: cover, alt: bundle.name, className: "h-full w-full object-cover transition-transform group-hover:scale-105", loading: "lazy" })) : (_jsx(Div, { className: "flex h-full w-full items-center justify-center text-3xl", children: PLACEHOLDER_EMOJI })) }), _jsx(Text, { className: "line-clamp-2 text-sm font-semibold", children: bundle.name }), _jsxs(Row, { gap: "sm", align: "center", className: "mt-1", children: [_jsx(Text, { size: "sm", weight: "bold", children: bundle.bundlePriceInPaise
70
- ? formatCurrency(bundle.bundlePriceInPaise / 100, "INR")
71
- : COPY.priceFallback }), _jsx(Text, { size: "xs", color: "muted", children: COPY.items(memberCount) })] }), badge && (_jsx(Badge, { variant: STOCK_BADGE_VARIANT[stock], className: "mt-1", children: badge }))] }));
68
+ return (_jsxs(Stack, { gap: "none", className: "rounded-xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-900", children: [_jsxs(Link, { href: href, className: "group block p-3 hover:no-underline", children: [_jsx(Div, { className: "mb-2 aspect-video overflow-hidden rounded-lg bg-zinc-100 dark:bg-zinc-800", children: cover ? (
69
+ // eslint-disable-next-line @next/next/no-img-element
70
+ _jsx("img", { src: cover, alt: bundle.name, className: "h-full w-full object-cover transition-transform group-hover:scale-105", loading: "lazy" })) : (_jsx(Div, { className: "flex h-full w-full items-center justify-center text-3xl", children: PLACEHOLDER_EMOJI })) }), _jsx(Text, { className: "line-clamp-2 text-sm font-semibold", children: bundle.name }), _jsxs(Row, { gap: "sm", align: "center", className: "mt-1", children: [_jsx(Text, { size: "sm", weight: "bold", children: bundle.bundlePriceInPaise
71
+ ? formatCurrency(bundle.bundlePriceInPaise / 100, "INR")
72
+ : COPY.priceFallback }), _jsx(Text, { size: "xs", color: "muted", children: COPY.items(memberCount) })] }), badge && (_jsx(Badge, { variant: STOCK_BADGE_VARIANT[stock], className: "mt-1", children: badge }))] }), onBuyNow && (_jsx(Div, { className: "border-t border-zinc-100 p-3 pt-2 dark:border-zinc-800", children: _jsx(BundleBuyNowCta, { bundleSlug: bundle.slug, outOfStock: stock === "out_of_stock", onBuyNow: onBuyNow, compact: true }) }))] }));
72
73
  }
@@ -27,11 +27,24 @@ export type BundleQueryRule = {
27
27
  categorySlug?: string;
28
28
  brandSlug?: string;
29
29
  tags?: string[];
30
- listingType?: "standard" | "pre-order";
30
+ /** Eligible listing types that may appear in a dynamic bundle. */
31
+ listingType?: "standard" | "pre-order" | "prize-draw";
31
32
  };
32
33
  orderBy?: "price-asc" | "price-desc" | "createdAt-desc";
33
34
  limit: number;
34
35
  };
36
+ /**
37
+ * Per-member metadata stored alongside `bundleProductIds`.
38
+ * `drawCount` — only meaningful when the member product has
39
+ * `listingType === "prize-draw"`. Represents how many draw entries
40
+ * the buyer receives for that product when they purchase the bundle
41
+ * (e.g. drawCount=5 means 5 raffle entries, not 5 copies of the draw).
42
+ */
43
+ export interface BundleItemDetail {
44
+ productId: string;
45
+ /** Number of draw entries included when product is a prize-draw. */
46
+ drawCount?: number;
47
+ }
35
48
  /** Full Firestore category document (includes server-only fields) */
36
49
  export interface CategoryDocumentSEO {
37
50
  title: string;
@@ -100,6 +113,14 @@ export interface CategoryDocument {
100
113
  brandFounded?: number;
101
114
  /** Banner image for the brand storefront page — categoryType==="brand". */
102
115
  brandBannerImage?: string;
116
+ /**
117
+ * "special" — admin-curated bundle; writes reverse `partOfBundleIds`
118
+ * pointers on member products and shows the "Bundled" badge on cards.
119
+ *
120
+ * "brand" — auto-generated brand collection (dynamic brandSlug query).
121
+ * Does NOT update `partOfBundleIds`; used as a discovery surface only.
122
+ */
123
+ bundleKind?: "special" | "brand";
103
124
  /** Discounted bundle price in paise. */
104
125
  bundlePriceInPaise?: number;
105
126
  /** Rule resolving the bundle's member products — static list or live query. */
@@ -110,6 +131,13 @@ export interface CategoryDocument {
110
131
  bundleQueryResolvedAt?: Date;
111
132
  /** Hand-picked products list (mirror of bundleQueryRule for static rules); kept for index-friendly queries. */
112
133
  bundleProductIds?: string[];
134
+ /**
135
+ * Per-member metadata parallel to `bundleProductIds`.
136
+ * Carries `drawCount` for prize-draw members (how many raffle entries
137
+ * the buyer receives). Flat `bundleProductIds` is kept for Firestore
138
+ * array-contains queries; this array holds the richer shape.
139
+ */
140
+ bundleItemDetails?: BundleItemDetail[];
113
141
  seo: CategoryDocumentSEO;
114
142
  display: CategoryDocumentDisplay;
115
143
  isActive: boolean;
@@ -17,4 +17,15 @@ export declare function verifyCheckoutConsentOtp(userId: string, addressId: stri
17
17
  * registered phone number.
18
18
  */
19
19
  export declare function grantCheckoutConsentViaSms(userId: string, userPhone: string | undefined, addressId: string): Promise<void>;
20
+ /**
21
+ * Grants checkout consent for an admin test order, bypassing the normal OTP flow.
22
+ *
23
+ * The resulting Firestore doc is identical to a verified consent OTP so that
24
+ * `createCheckoutOrderAction` can proceed without modification. The
25
+ * `verifiedVia: "admin_bypass"` marker prevents bypass-credit grants.
26
+ *
27
+ * Security: callers MUST verify the requesting user is an admin AND that
28
+ * `siteSettings.featureFlags.adminCheckoutBypass === true` before calling this.
29
+ */
30
+ export declare function grantAdminCheckoutBypass(userId: string, addressId: string, bypassingAdminUid: string): Promise<void>;
20
31
  export { userRepository };