@mohasinac/appkit 3.2.19 → 3.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 (202) hide show
  1. package/dist/_internal/server/features/checkout/actions.d.ts +2 -0
  2. package/dist/_internal/server/features/checkout/actions.js +239 -190
  3. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  4. package/dist/_internal/server/functions/scheduled.js +9 -1
  5. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +8 -11
  6. package/dist/_internal/server/jobs/core/emiInstallmentReminder.d.ts +8 -0
  7. package/dist/_internal/server/jobs/core/emiInstallmentReminder.js +78 -0
  8. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.d.ts +2 -2
  9. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +15 -10
  10. package/dist/_internal/server/jobs/handlers/emiInstallmentReminder.d.ts +2 -0
  11. package/dist/_internal/server/jobs/handlers/emiInstallmentReminder.js +2 -0
  12. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  13. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  14. package/dist/_internal/server/providers/index.d.ts +4 -4
  15. package/dist/_internal/server/providers/index.js +4 -4
  16. package/dist/_internal/server/providers/payment/razorpay-mock.d.ts +3 -2
  17. package/dist/_internal/server/providers/payment/razorpay-mock.js +3 -1
  18. package/dist/_internal/server/providers/resolve.d.ts +10 -13
  19. package/dist/_internal/server/providers/resolve.js +9 -15
  20. package/dist/_internal/shared/actions/action-registry.js +28 -0
  21. package/dist/_internal/shared/checkout/rules/_defaults.js +1 -0
  22. package/dist/_internal/shared/checkout/rules/_registry.js +4 -0
  23. package/dist/_internal/shared/checkout/rules/art.rule.d.ts +9 -0
  24. package/dist/_internal/shared/checkout/rules/art.rule.js +5 -0
  25. package/dist/_internal/shared/checkout/rules/auction.rule.js +1 -0
  26. package/dist/_internal/shared/checkout/rules/classified.rule.js +1 -0
  27. package/dist/_internal/shared/checkout/rules/index.d.ts +2 -0
  28. package/dist/_internal/shared/checkout/rules/index.js +2 -0
  29. package/dist/_internal/shared/checkout/rules/stickers.rule.d.ts +9 -0
  30. package/dist/_internal/shared/checkout/rules/stickers.rule.js +5 -0
  31. package/dist/_internal/shared/checkout/rules/types.d.ts +6 -0
  32. package/dist/_internal/shared/features/checkout/config.d.ts +16 -1
  33. package/dist/_internal/shared/features/checkout/config.js +16 -1
  34. package/dist/_internal/shared/features/emi/schedule.d.ts +63 -0
  35. package/dist/_internal/shared/features/emi/schedule.js +62 -0
  36. package/dist/_internal/shared/fees/calculator.d.ts +53 -0
  37. package/dist/_internal/shared/fees/calculator.js +40 -0
  38. package/dist/_internal/shared/listing-types/_registry.d.ts +22 -66
  39. package/dist/_internal/shared/listing-types/_registry.js +19 -2
  40. package/dist/_internal/shared/listing-types/art/config.d.ts +15 -0
  41. package/dist/_internal/shared/listing-types/art/config.js +14 -0
  42. package/dist/_internal/shared/listing-types/art/ctas.d.ts +1 -0
  43. package/dist/_internal/shared/listing-types/art/ctas.js +3 -0
  44. package/dist/_internal/shared/listing-types/art/og.d.ts +1 -0
  45. package/dist/_internal/shared/listing-types/art/og.js +1 -0
  46. package/dist/_internal/shared/listing-types/art/schema.d.ts +2 -0
  47. package/dist/_internal/shared/listing-types/art/schema.js +3 -0
  48. package/dist/_internal/shared/listing-types/art/seed-factory.d.ts +1 -0
  49. package/dist/_internal/shared/listing-types/art/seed-factory.js +1 -0
  50. package/dist/_internal/shared/listing-types/auction/config.d.ts +8 -0
  51. package/dist/_internal/shared/listing-types/auction/config.js +6 -0
  52. package/dist/_internal/shared/listing-types/capabilities.js +17 -0
  53. package/dist/_internal/shared/listing-types/classified/config.d.ts +8 -0
  54. package/dist/_internal/shared/listing-types/classified/config.js +6 -0
  55. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +8 -0
  56. package/dist/_internal/shared/listing-types/digital-code/config.js +6 -0
  57. package/dist/_internal/shared/listing-types/live/config.d.ts +8 -0
  58. package/dist/_internal/shared/listing-types/live/config.js +6 -0
  59. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +8 -0
  60. package/dist/_internal/shared/listing-types/pre-order/config.js +6 -0
  61. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +8 -0
  62. package/dist/_internal/shared/listing-types/prize-draw/config.js +6 -0
  63. package/dist/_internal/shared/listing-types/standard/config.d.ts +8 -0
  64. package/dist/_internal/shared/listing-types/standard/config.js +6 -0
  65. package/dist/_internal/shared/listing-types/stickers/config.d.ts +15 -0
  66. package/dist/_internal/shared/listing-types/stickers/config.js +14 -0
  67. package/dist/_internal/shared/listing-types/stickers/ctas.d.ts +1 -0
  68. package/dist/_internal/shared/listing-types/stickers/ctas.js +3 -0
  69. package/dist/_internal/shared/listing-types/stickers/og.d.ts +1 -0
  70. package/dist/_internal/shared/listing-types/stickers/og.js +1 -0
  71. package/dist/_internal/shared/listing-types/stickers/schema.d.ts +2 -0
  72. package/dist/_internal/shared/listing-types/stickers/schema.js +3 -0
  73. package/dist/_internal/shared/listing-types/stickers/seed-factory.d.ts +1 -0
  74. package/dist/_internal/shared/listing-types/stickers/seed-factory.js +1 -0
  75. package/dist/client.d.ts +7 -2
  76. package/dist/client.js +4 -2
  77. package/dist/constants/field-names.d.ts +2 -6
  78. package/dist/constants/field-names.js +2 -6
  79. package/dist/contracts/index.d.ts +4 -2
  80. package/dist/contracts/index.js +6 -0
  81. package/dist/contracts/payment.d.ts +14 -9
  82. package/dist/contracts/payment.js +11 -1
  83. package/dist/contracts/shipping.d.ts +14 -9
  84. package/dist/contracts/shipping.js +11 -1
  85. package/dist/features/about/components/PublicProfileView.js +3 -6
  86. package/dist/features/account/components/UserOffersPanel.js +0 -1
  87. package/dist/features/admin/components/AdminAddressBookView.d.ts +11 -0
  88. package/dist/features/admin/components/AdminAddressBookView.js +30 -0
  89. package/dist/features/admin/components/AdminArtView.d.ts +3 -0
  90. package/dist/features/admin/components/AdminArtView.js +45 -0
  91. package/dist/features/admin/components/AdminCarouselEditorView.js +2 -3
  92. package/dist/features/admin/components/AdminFulfillmentView.js +0 -1
  93. package/dist/features/admin/components/AdminSiteConfigGuideView.js +1 -1
  94. package/dist/features/admin/components/AdminSiteSettingsView.js +14 -9
  95. package/dist/features/admin/components/AdminStickersView.d.ts +3 -0
  96. package/dist/features/admin/components/AdminStickersView.js +45 -0
  97. package/dist/features/admin/components/index.d.ts +6 -0
  98. package/dist/features/admin/components/index.js +4 -0
  99. package/dist/features/admin/hooks/useChat.js +0 -1
  100. package/dist/features/admin/schemas/firestore.d.ts +19 -11
  101. package/dist/features/admin/schemas/firestore.js +12 -2
  102. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +3 -2
  103. package/dist/features/auth/hooks/useAuth.js +35 -10
  104. package/dist/features/auth/schemas/firestore.d.ts +2 -6
  105. package/dist/features/cart/schemas/firestore.d.ts +3 -2
  106. package/dist/features/consultation/schemas/index.d.ts +2 -2
  107. package/dist/features/history/hooks/useHistory.js +0 -1
  108. package/dist/features/loyalty/schemas/index.d.ts +6 -6
  109. package/dist/features/messages/hooks/useConversation.js +0 -1
  110. package/dist/features/messages/hooks/useConversations.js +0 -1
  111. package/dist/features/orders/repository/orders.repository.d.ts +20 -2
  112. package/dist/features/orders/repository/orders.repository.js +34 -5
  113. package/dist/features/orders/schemas/firestore.d.ts +33 -7
  114. package/dist/features/orders/schemas/firestore.js +1 -1
  115. package/dist/features/orders/schemas/index.d.ts +176 -31
  116. package/dist/features/orders/schemas/index.js +20 -6
  117. package/dist/features/orders/types/index.d.ts +3 -3
  118. package/dist/features/payments/repository/payout.repository.js +2 -2
  119. package/dist/features/payments/schemas/firestore.d.ts +3 -3
  120. package/dist/features/payments/schemas/firestore.js +2 -2
  121. package/dist/features/payments/schemas/index.d.ts +5 -5
  122. package/dist/features/payments/schemas/index.js +2 -2
  123. package/dist/features/products/api/[id]/route.js +4 -1
  124. package/dist/features/products/api/route.js +4 -1
  125. package/dist/features/products/components/CompareOverlay.js +7 -9
  126. package/dist/features/products/components/ProductDetailPageView.d.ts +3 -1
  127. package/dist/features/products/components/ProductDetailPageView.js +2 -2
  128. package/dist/features/products/components/ProductForm.js +18 -7
  129. package/dist/features/products/components/ProductGrid.js +7 -12
  130. package/dist/features/products/components/ShowGroupSection.js +3 -5
  131. package/dist/features/products/components/SublistingCarouselSection.js +3 -6
  132. package/dist/features/products/constants/listing-tabs.d.ts +32 -0
  133. package/dist/features/products/constants/listing-tabs.js +8 -0
  134. package/dist/features/products/index.d.ts +1 -1
  135. package/dist/features/products/index.js +3 -1
  136. package/dist/features/products/schemas/firestore.d.ts +20 -1
  137. package/dist/features/products/schemas/firestore.js +2 -0
  138. package/dist/features/products/schemas/index.d.ts +48 -0
  139. package/dist/features/products/schemas/index.js +9 -0
  140. package/dist/features/products/schemas/product-features.d.ts +1 -1
  141. package/dist/features/products/types/index.d.ts +5 -3
  142. package/dist/features/products/utils/listing-type.d.ts +6 -0
  143. package/dist/features/products/utils/listing-type.js +3 -0
  144. package/dist/features/reviews/api/[id]/route.js +5 -4
  145. package/dist/features/seller/actions/seller-actions.d.ts +29 -14
  146. package/dist/features/seller/actions/seller-actions.js +101 -27
  147. package/dist/features/seller/components/FulfillmentView.js +0 -1
  148. package/dist/features/seller/components/SellerAddressesView.js +0 -1
  149. package/dist/features/seller/components/SellerArtView.d.ts +7 -0
  150. package/dist/features/seller/components/SellerArtView.js +114 -0
  151. package/dist/features/seller/components/SellerOffersPanel.js +0 -1
  152. package/dist/features/seller/components/SellerOrdersView.js +38 -1
  153. package/dist/features/seller/components/SellerProductShell.d.ts +5 -1
  154. package/dist/features/seller/components/SellerProductShell.js +69 -59
  155. package/dist/features/seller/components/SellerReviewsView.js +0 -1
  156. package/dist/features/seller/components/SellerShippingView.js +6 -25
  157. package/dist/features/seller/components/SellerStickersView.d.ts +7 -0
  158. package/dist/features/seller/components/SellerStickersView.js +114 -0
  159. package/dist/features/seller/components/index.d.ts +4 -0
  160. package/dist/features/seller/components/index.js +2 -0
  161. package/dist/features/seller/messages/en.json +1 -1
  162. package/dist/features/seller/schemas/index.d.ts +7 -7
  163. package/dist/features/seller/schemas/index.js +1 -1
  164. package/dist/features/seller/types/index.d.ts +1 -1
  165. package/dist/features/stores/components/StoreDetailLayoutView.js +18 -2
  166. package/dist/features/stores/schemas/firestore.d.ts +7 -1
  167. package/dist/features/stores/schemas/index.d.ts +19 -16
  168. package/dist/features/stores/schemas/index.js +1 -1
  169. package/dist/index.d.ts +21 -36
  170. package/dist/index.js +27 -39
  171. package/dist/next/routing/route-map.d.ts +22 -0
  172. package/dist/next/routing/route-map.js +10 -0
  173. package/dist/providers/payment-manual/index.d.ts +40 -0
  174. package/dist/providers/payment-manual/index.js +91 -0
  175. package/dist/providers/payment-razorpay/index.d.ts +4 -2
  176. package/dist/providers/payment-razorpay/index.js +4 -1
  177. package/dist/providers/shipping-manual/index.d.ts +22 -0
  178. package/dist/providers/shipping-manual/index.js +47 -0
  179. package/dist/providers.d.ts +2 -2
  180. package/dist/providers.js +5 -3
  181. package/dist/schemas/registry.d.ts +129 -70
  182. package/dist/schemas/registry.js +0 -4
  183. package/dist/security/pii-encrypt.js +0 -7
  184. package/dist/security/pii-redact.js +0 -1
  185. package/dist/seed/conversations-seed-data.js +2 -2
  186. package/dist/seed/faq-seed-data.js +7 -7
  187. package/dist/seed/grouped-listings-seed-data.d.ts +2 -0
  188. package/dist/seed/index.d.ts +2 -0
  189. package/dist/seed/index.js +2 -0
  190. package/dist/seed/manifest.js +20 -16
  191. package/dist/seed/payouts-seed-data.js +5 -5
  192. package/dist/seed/products-art-seed-data.d.ts +11 -0
  193. package/dist/seed/products-art-seed-data.js +125 -0
  194. package/dist/seed/products-preorders-seed-data.d.ts +2 -0
  195. package/dist/seed/products-stickers-seed-data.d.ts +11 -0
  196. package/dist/seed/products-stickers-seed-data.js +125 -0
  197. package/dist/seed/site-settings-seed-data.js +13 -4
  198. package/dist/seed/stores-seed-data.js +11 -11
  199. package/dist/server.d.ts +7 -15
  200. package/dist/server.js +17 -42
  201. package/dist/ui/rich-text/RichText.js +1 -1
  202. package/package.json +1 -1
@@ -112,7 +112,6 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
112
112
  // ⑧ Integrations
113
113
  const [razorpayKeyId, setRazorpayKeyId] = React.useState("");
114
114
  const [razorpaySecret, setRazorpaySecret] = React.useState("");
115
- const [shiprocketToken, setShiprocketToken] = React.useState("");
116
115
  const [smtpHost, setSmtpHost] = React.useState("");
117
116
  const [smtpPort, setSmtpPort] = React.useState("587");
118
117
  const [smtpUser, setSmtpUser] = React.useState("");
@@ -131,9 +130,12 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
131
130
  const [deviantartClientSecret, setDeviantartClientSecret] = React.useState("");
132
131
  // ⑨ Shipping
133
132
  const [freeShippingThreshold, setFreeShippingThreshold] = React.useState(999);
134
- const [codEnabled, setCodEnabled] = React.useState(true);
135
- const [defaultCarrier, setDefaultCarrier] = React.useState("shiprocket");
133
+ const [defaultCarrier, setDefaultCarrier] = React.useState("custom");
136
134
  const [maxDeliveryRadius, setMaxDeliveryRadius] = React.useState(0);
135
+ // ⑨ Payment methods (siteSettings.payment — read by checkout for method eligibility)
136
+ const [razorpayEnabled, setRazorpayEnabled] = React.useState(false);
137
+ const [upiManualEnabled, setUpiManualEnabled] = React.useState(true);
138
+ const [codEnabled, setCodEnabled] = React.useState(true);
137
139
  // ⑩ Auction
138
140
  const [minBidIncrement, setMinBidIncrement] = React.useState(50);
139
141
  const [autoExtendWindow, setAutoExtendWindow] = React.useState(5);
@@ -231,7 +233,6 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
231
233
  setPromotedSlotFee(s.commissions?.promotedSlotFee ?? 499);
232
234
  setRazorpayKeyId(s.credentialsMasked?.razorpayKeyId ?? "");
233
235
  setRazorpaySecret(s.credentialsMasked?.razorpaySecret ?? "");
234
- setShiprocketToken(s.credentialsMasked?.shiprocketToken ?? "");
235
236
  setSmtpHost(s.emailSettings?.host ?? "");
236
237
  setSmtpPort(String(s.emailSettings?.port ?? 587));
237
238
  setSmtpUser(s.emailSettings?.user ?? "");
@@ -248,9 +249,11 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
248
249
  setDeviantartClientId(s.credentialsMasked?.deviantartClientId ?? "");
249
250
  setDeviantartClientSecret(s.credentialsMasked?.deviantartClientSecret ?? "");
250
251
  setFreeShippingThreshold(Math.round((s.shipping?.freeShippingThreshold ?? 99900) / 100));
251
- setCodEnabled(s.shipping?.codEnabled ?? true);
252
- setDefaultCarrier(s.shipping?.defaultCarrier ?? "shiprocket");
252
+ setDefaultCarrier(s.shipping?.defaultCarrier ?? "custom");
253
253
  setMaxDeliveryRadius(s.shipping?.maxDeliveryRadius ?? 0);
254
+ setRazorpayEnabled(s.payment?.razorpayEnabled ?? false);
255
+ setUpiManualEnabled(s.payment?.upiManualEnabled ?? true);
256
+ setCodEnabled(s.payment?.codEnabled ?? true);
254
257
  setMinBidIncrement(Math.round((s.auctionConfig?.minBidIncrement ?? 5000) / 100));
255
258
  setAutoExtendWindow(s.auctionConfig?.autoExtendWindowMinutes ?? 5);
256
259
  setSettlementGrace(s.auctionConfig?.settlementGracePeriodHours ?? 24);
@@ -328,7 +331,7 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
328
331
  }));
329
332
  const integrationsMutation = useSave("Integrations", () => ({
330
333
  credentials: {
331
- razorpayKeyId, razorpaySecret, shiprocketToken, smtpPassword,
334
+ razorpayKeyId, razorpaySecret, smtpPassword,
332
335
  metaPageAccessToken, metaPageId,
333
336
  tiktokClientKey, tiktokClientSecret, tiktokAccessToken,
334
337
  deviantartClientId, deviantartClientSecret,
@@ -337,7 +340,8 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
337
340
  integrations: { googleAnalyticsId: gaMeasurementId, facebookPixelId: fbPixelId, gtmContainerId },
338
341
  }));
339
342
  const shippingMutation = useSave("Shipping", () => ({
340
- shipping: { freeShippingThreshold: freeShippingThreshold * 100, codEnabled, defaultCarrier, maxDeliveryRadius },
343
+ shipping: { freeShippingThreshold: freeShippingThreshold * 100, defaultCarrier, maxDeliveryRadius },
344
+ payment: { razorpayEnabled, upiManualEnabled, codEnabled },
341
345
  }));
342
346
  const auctionMutation = useSave("Auction Config", () => ({
343
347
  auctionConfig: { minBidIncrement: minBidIncrement * 100, autoExtendWindowMinutes: autoExtendWindow, settlementGracePeriodHours: settlementGrace },
@@ -380,6 +384,7 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
380
384
  { label: "System", value: "system" },
381
385
  ];
382
386
  const CARRIER_OPTIONS = [
387
+ { label: "Custom / Other", value: "custom" },
383
388
  { label: "Shiprocket", value: "shiprocket" },
384
389
  { label: "Delhivery", value: "delhivery" },
385
390
  { label: "Bluedart", value: "bluedart" },
@@ -408,7 +413,7 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
408
413
  ].map(([value, label]) => (_jsx(TabsTrigger, { value: value, children: label }, value))) }), _jsx(TabsContent, { value: "about", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); aboutMutation.mutate(); }, className: "pt-4", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", 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(_Fragment, { children: [_jsx(Text, { className: "mb-1", color: "muted", size: "sm", weight: "medium", children: "Mission text" }), _jsx("textarea", { value: aboutMissionText, onChange: (e) => setAboutMissionText(e.target.value), placeholder: "LetItRip was built to democratise commerce\u2026", rows: 4, className: CLS_TEXTAREA })] }), _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: "pt-4", spacing: "md", 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(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "md", 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: "pt-4", spacing: "md", children: [_jsxs(Grid, { gap: "md", className: "grid-cols-3", children: [_jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Primary color" }), _jsx(Input, { type: "color", value: primaryColor || "#000000", onChange: (e) => setPrimaryColor(e.target.value), className: "h-10 w-full cursor-pointer", bare: true }), " "] }), _jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Secondary color" }), _jsx(Input, { type: "color", value: secondaryColor || "#000000", onChange: (e) => setSecondaryColor(e.target.value), className: "h-10 w-full cursor-pointer", bare: true }), " "] }), _jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Accent color" }), _jsx(Input, { type: "color", value: accentColor || "#000000", onChange: (e) => setAccentColor(e.target.value), className: "h-10 w-full cursor-pointer", bare: true }), " "] })] }), _jsxs(Grid, { cols: 2, gap: "md", 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: "themes", children: _jsxs(Form, { onSubmit: (e) => {
409
414
  e.preventDefault();
410
415
  themeMutation.mutate();
411
- }, className: "pt-4", spacing: "md", children: [_jsx(ThemeManagerView, { value: themeRegistry, onChange: setThemeRegistry, previewOrigin: "/" }), _jsx(GroupSaveButton, { isPending: themeMutation.isPending })] }) }), _jsx(TabsContent, { value: "announcement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); announcementMutation.mutate(); }, className: "pt-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: "/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(GroupSaveButton, { isPending: announcementMutation.isPending })] }) }), _jsx(TabsContent, { value: "seo", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); seoMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: seoMutation.isPending })] }) }), _jsx(TabsContent, { value: "contact", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); contactMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: contactMutation.isPending })] }) }), _jsx(TabsContent, { value: "watermark", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); watermarkMutation.mutate(); }, className: "pt-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, className: "select-none font-medium p-1 text-[var(--appkit-color-text-faint)]", children: watermarkText }) })] }), _jsx(GroupSaveButton, { isPending: watermarkMutation.isPending })] }) }), _jsx(TabsContent, { value: "fees", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); feesMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: feesMutation.isPending })] }) }), _jsx(TabsContent, { value: "integrations", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); integrationsMutation.mutate(); }, className: "pt-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: "Shiprocket" }), _jsx(MaskedInput, { label: "Shiprocket API token", value: shiprocketToken, onChange: setShiprocketToken, 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(GroupSaveButton, { isPending: integrationsMutation.isPending })] }) }), _jsx(TabsContent, { value: "shipping", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); shippingMutation.mutate(); }, className: "pt-4", spacing: "md", 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: "pt-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(GroupSaveButton, { isPending: auctionMutation.isPending })] }) }), _jsx(TabsContent, { value: "limits", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); limitsMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: limitsMutation.isPending })] }) }), _jsx(TabsContent, { value: "whatsapp", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); whatsappMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: whatsappMutation.isPending })] }) }), _jsx(TabsContent, { value: "notifications", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); notifChannelsMutation.mutate(); }, className: "pt-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" })] })] }))] }), _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." })] }))] }), _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(GroupSaveButton, { isPending: notifChannelsMutation.isPending })] }) }), _jsx(TabsContent, { value: "legal", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); legalMutation.mutate(); }, className: "pt-4", spacing: "md", children: [[
416
+ }, className: "pt-4", spacing: "md", children: [_jsx(ThemeManagerView, { value: themeRegistry, onChange: setThemeRegistry, previewOrigin: "/" }), _jsx(GroupSaveButton, { isPending: themeMutation.isPending })] }) }), _jsx(TabsContent, { value: "announcement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); announcementMutation.mutate(); }, className: "pt-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: "/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(GroupSaveButton, { isPending: announcementMutation.isPending })] }) }), _jsx(TabsContent, { value: "seo", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); seoMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: seoMutation.isPending })] }) }), _jsx(TabsContent, { value: "contact", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); contactMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: contactMutation.isPending })] }) }), _jsx(TabsContent, { value: "watermark", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); watermarkMutation.mutate(); }, className: "pt-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, className: "select-none font-medium p-1 text-[var(--appkit-color-text-faint)]", children: watermarkText }) })] }), _jsx(GroupSaveButton, { isPending: watermarkMutation.isPending })] }) }), _jsx(TabsContent, { value: "fees", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); feesMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: feesMutation.isPending })] }) }), _jsx(TabsContent, { value: "integrations", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); integrationsMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: integrationsMutation.isPending })] }) }), _jsx(TabsContent, { value: "shipping", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); shippingMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: shippingMutation.isPending })] }) }), _jsx(TabsContent, { value: "auction", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); auctionMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: auctionMutation.isPending })] }) }), _jsx(TabsContent, { value: "limits", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); limitsMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: limitsMutation.isPending })] }) }), _jsx(TabsContent, { value: "whatsapp", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); whatsappMutation.mutate(); }, className: "pt-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(GroupSaveButton, { isPending: whatsappMutation.isPending })] }) }), _jsx(TabsContent, { value: "notifications", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); notifChannelsMutation.mutate(); }, className: "pt-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" })] })] }))] }), _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." })] }))] }), _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(GroupSaveButton, { isPending: notifChannelsMutation.isPending })] }) }), _jsx(TabsContent, { value: "legal", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); legalMutation.mutate(); }, className: "pt-4", spacing: "md", children: [[
412
417
  ["Terms of Service", termsHtml, setTermsHtml],
413
418
  ["Privacy Policy", privacyHtml, setPrivacyHtml],
414
419
  ["Refund Policy", refundHtml, setRefundHtml],
@@ -0,0 +1,3 @@
1
+ import type { ListingLayoutProps } from "../../../ui";
2
+ export type AdminStickersViewProps = ListingLayoutProps;
3
+ export declare function AdminStickersView({ children, ...props }: AdminStickersViewProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * AdminStickersView — admin browse of sticker listings (EMI/art-stickers session).
5
+ * Mirrors AdminClassifiedView's thin config-driven pattern.
6
+ */
7
+ import { sortBy } from "@mohasinac/appkit";
8
+ import React from "react";
9
+ import { ListingLayout } from "../../../ui";
10
+ import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
11
+ import { toRecordArray, toRelativeDate, toRupees, toStringValue, } from "../hooks/useAdminListingData";
12
+ import { DataListingView } from "./DataListingView";
13
+ const ADMIN_STICKERS_CONFIG = {
14
+ portal: "admin",
15
+ title: "Stickers",
16
+ searchPlaceholder: "Search stickers by name or seller",
17
+ emptyLabel: "No sticker listings",
18
+ filterKeys: [],
19
+ defaultSort: sortBy("createdAt", "DESC"),
20
+ queryKey: ["admin", "stickers", "listing"],
21
+ endpoint: ADMIN_ENDPOINTS.PRODUCTS,
22
+ sortOptions: [
23
+ { value: sortBy("createdAt", "DESC"), label: "Newest" },
24
+ { value: sortBy("createdAt", "ASC"), label: "Oldest" },
25
+ { value: "title", label: "Title A–Z" },
26
+ ],
27
+ mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
28
+ id: toStringValue(item.id, `stickers-${index}`),
29
+ primary: toStringValue(item.title ?? item.productTitle, "Untitled sticker listing"),
30
+ secondary: [
31
+ toStringValue(item.sellerName, "Unknown seller"),
32
+ toRupees(item.price),
33
+ ].join(" · "),
34
+ status: toStringValue(item.status, "draft"),
35
+ updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
36
+ })),
37
+ getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
38
+ buildFilters: () => "listingType==stickers",
39
+ };
40
+ export function AdminStickersView({ children, ...props }) {
41
+ if (React.Children.count(children) > 0) {
42
+ return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
43
+ }
44
+ return _jsx(DataListingView, { config: ADMIN_STICKERS_CONFIG });
45
+ }
@@ -40,6 +40,10 @@ export { AdminDigitalCodesView } from "./AdminDigitalCodesView";
40
40
  export type { AdminDigitalCodesViewProps } from "./AdminDigitalCodesView";
41
41
  export { AdminLiveView } from "./AdminLiveView";
42
42
  export type { AdminLiveViewProps } from "./AdminLiveView";
43
+ export { AdminArtView } from "./AdminArtView";
44
+ export type { AdminArtViewProps } from "./AdminArtView";
45
+ export { AdminStickersView } from "./AdminStickersView";
46
+ export type { AdminStickersViewProps } from "./AdminStickersView";
43
47
  export { AdminGroupedListingsView } from "./AdminGroupedListingsView";
44
48
  export type { AdminGroupedListingsViewProps } from "./AdminGroupedListingsView";
45
49
  export { AdminBundlesView } from "./AdminBundlesView";
@@ -157,6 +161,8 @@ export { AdminFulfillmentView } from "./AdminFulfillmentView";
157
161
  export type { AdminFulfillmentViewProps } from "./AdminFulfillmentView";
158
162
  export { AdminAddressesView } from "./AdminAddressesView";
159
163
  export type { AdminAddressesViewProps } from "./AdminAddressesView";
164
+ export { AdminAddressBookView } from "./AdminAddressBookView";
165
+ export type { AdminAddressBookViewProps } from "./AdminAddressBookView";
160
166
  export { AdminAddressClustersView } from "./AdminAddressClustersView";
161
167
  export type { AdminAddressClustersViewProps } from "./AdminAddressClustersView";
162
168
  export { AdminPaymentMethodsView } from "./AdminPaymentMethodsView";
@@ -22,6 +22,9 @@ export { AdminPrizeDrawsView } from "./AdminPrizeDrawsView";
22
22
  export { AdminClassifiedView } from "./AdminClassifiedView";
23
23
  export { AdminDigitalCodesView } from "./AdminDigitalCodesView";
24
24
  export { AdminLiveView } from "./AdminLiveView";
25
+ // EMI/art-stickers session — admin listing views for art / stickers.
26
+ export { AdminArtView } from "./AdminArtView";
27
+ export { AdminStickersView } from "./AdminStickersView";
25
28
  export { AdminGroupedListingsView } from "./AdminGroupedListingsView";
26
29
  // S-SBUNI-4 2026-05-13 — admin bundle CRUD views.
27
30
  export { AdminBundlesView } from "./AdminBundlesView";
@@ -82,6 +85,7 @@ export { AdminScammersView } from "./AdminScammersView";
82
85
  export { AdminScammerEditorView } from "./AdminScammerEditorView";
83
86
  export { AdminFulfillmentView } from "./AdminFulfillmentView";
84
87
  export { AdminAddressesView } from "./AdminAddressesView";
88
+ export { AdminAddressBookView } from "./AdminAddressBookView";
85
89
  export { AdminAddressClustersView } from "./AdminAddressClustersView";
86
90
  export { AdminPaymentMethodsView } from "./AdminPaymentMethodsView";
87
91
  export { AdminPaymentClustersView } from "./AdminPaymentClustersView";
@@ -16,7 +16,6 @@ export function useChat(chatId, options) {
16
16
  const tokenExpiresAtRef = useRef(0);
17
17
  const unsubscribeRef = useRef(null);
18
18
  const connectAndSubscribe = useCallback(async () => {
19
- // toast-intentionally-silent: RTDB connection management — error set in state, not surfaced via toast
20
19
  if (!chatId)
21
20
  return;
22
21
  setIsLoading(true);
@@ -3,7 +3,7 @@
3
3
  * Covers: notifications, chat rooms, site settings
4
4
  */
5
5
  export declare const ADMIN_CHECKOUT_BYPASS_FLAG_KEY: "adminCheckoutBypass";
6
- 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";
6
+ 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";
7
7
  import type { BaseDocument } from "../../../_internal/shared/types/base-document";
8
8
  export type NotificationPriority = "low" | "normal" | "high";
9
9
  export interface NotificationDocument extends BaseDocument {
@@ -143,8 +143,6 @@ export interface SiteSettingsCredentials {
143
143
  razorpayWebhookSecret?: string;
144
144
  resendApiKey?: string;
145
145
  whatsappApiKey?: string;
146
- shiprocketEmail?: string;
147
- shiprocketPassword?: string;
148
146
  metaAppId?: string;
149
147
  metaAppSecret?: string;
150
148
  metaPageAccessToken?: string;
@@ -171,8 +169,6 @@ export interface SiteSettingsCredentialsMasked {
171
169
  razorpayWebhookSecret?: string;
172
170
  resendApiKey?: string;
173
171
  whatsappApiKey?: string;
174
- shiprocketEmail?: string;
175
- shiprocketPassword?: string;
176
172
  metaAppId?: string;
177
173
  metaAppSecret?: string;
178
174
  metaPageAccessToken?: string;
@@ -293,6 +289,21 @@ export interface SiteSettingsDocument extends BaseDocument {
293
289
  upiManualEnabled: boolean;
294
290
  codEnabled: boolean;
295
291
  };
292
+ /** Site-wide EMI (installment) settings. A seller must ALSO have `StoreDocument.emiEnabled` on for EMI to appear at checkout for their items. */
293
+ emi: {
294
+ enabled: boolean;
295
+ /** Paise — a seller's cart subtotal must exceed this for EMI to appear as an option. */
296
+ minOrderValueInPaise: number;
297
+ tenureOptions: number[];
298
+ /** Down payment % collected at checkout. */
299
+ tokenPercent: number;
300
+ /** Day of month (1–10) each installment is due. */
301
+ billingDay: number;
302
+ /** Extra % of principal per month of tenure — the "excess EMI fee" the buyer pays for spreading payment. */
303
+ surchargePercentPerMonth: number;
304
+ /** Share of the surcharge that goes to the seller (0–100); the rest is the platform's cut. */
305
+ surchargeSellerSharePercent: number;
306
+ };
296
307
  commissions: {
297
308
  /** Our platform cut charged to the buyer as a % of order value (e.g. 5 = 5%). */
298
309
  platformFeePercent: number;
@@ -303,6 +314,9 @@ export interface SiteSettingsDocument extends BaseDocument {
303
314
  /** Razorpay gateway cost % (absorbed by platform, not passed through separately). */
304
315
  gatewayFeePercent: number;
305
316
  codDepositPercent: number;
317
+ /** COD handling fee charged to the buyer: max(codHandlingFeeMinInPaise, subtotal × codHandlingFeePercent / 100). */
318
+ codHandlingFeeMinInPaise: number;
319
+ codHandlingFeePercent: number;
306
320
  sellerShippingFixed: number;
307
321
  platformShippingPercent: number;
308
322
  platformShippingFixedMin: number;
@@ -362,12 +376,6 @@ export interface SiteSettingsDocument extends BaseDocument {
362
376
  * if this is ever true. Default false.
363
377
  */
364
378
  useMockPayment?: boolean;
365
- /**
366
- * Track H — when true and NODE_ENV !== "production", the shipping provider
367
- * resolver returns MockShiprocketProvider. In production the resolver throws
368
- * if this is ever true. Default false.
369
- */
370
- useMockShipping?: boolean;
371
379
  listingTypes?: {
372
380
  standard?: boolean;
373
381
  auction?: boolean;
@@ -167,16 +167,27 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
167
167
  motto: "Your Marketplace, Your Rules",
168
168
  theme: DEFAULT_SITE_SETTINGS_THEME,
169
169
  payment: {
170
- razorpayEnabled: true,
170
+ razorpayEnabled: false,
171
171
  upiManualEnabled: true,
172
172
  codEnabled: true,
173
173
  },
174
+ emi: {
175
+ enabled: false,
176
+ minOrderValueInPaise: 1000000,
177
+ tenureOptions: [2, 3, 4, 5, 6],
178
+ tokenPercent: 10,
179
+ billingDay: 5,
180
+ surchargePercentPerMonth: 1,
181
+ surchargeSellerSharePercent: 50,
182
+ },
174
183
  commissions: {
175
184
  platformFeePercent: 5,
176
185
  gstPercent: 18,
177
186
  minimumTransactionFee: 0,
178
187
  gatewayFeePercent: 2,
179
188
  codDepositPercent: 10,
189
+ codHandlingFeeMinInPaise: 20000,
190
+ codHandlingFeePercent: 10,
180
191
  sellerShippingFixed: 0,
181
192
  platformShippingPercent: 10,
182
193
  platformShippingFixedMin: 0,
@@ -205,7 +216,6 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
205
216
  // Track H — both mock-provider flags default to false. Production
206
217
  // deployments must never flip these on (the resolver throws if they are).
207
218
  useMockPayment: false,
208
- useMockShipping: false,
209
219
  // W1-37 2026-05-23 — Phase 2 listing types enabled by default; all per-type
210
220
  // surfaces (seller + admin via W1-29 + public) are now shipped.
211
221
  listingTypes: {
@@ -1,4 +1,5 @@
1
1
  import { type WishlistToggleActions } from "../../wishlist";
2
+ import type { ListingType } from "../../products/types/index";
2
3
  export interface MarketplaceAuctionCardData {
3
4
  id: string;
4
5
  title: string;
@@ -11,8 +12,8 @@ export interface MarketplaceAuctionCardData {
11
12
  url?: string;
12
13
  thumbnailUrl?: string;
13
14
  };
14
- /** Canonical discriminator (SB1-G Phase 4 · SB-UNI-F 2026-05-13 union extension). */
15
- listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live";
15
+ /** Canonical discriminator (SB1-G Phase 4). */
16
+ listingType?: ListingType;
16
17
  auctionEndDate?: string | Date;
17
18
  startingBid?: number;
18
19
  currentBid?: number;
@@ -57,6 +57,17 @@ export function useGoogleLogin(options) {
57
57
  const [popupPending, setPopupPending] = useState(false);
58
58
  // calledRef prevents both RTDB and postMessage from firing the callbacks
59
59
  const calledRef = useRef(false);
60
+ // Polls for the popup being closed manually (without completing sign-in).
61
+ // Without this, a user who closes the popup before RTDB/postMessage
62
+ // resolves leaves popupPending stuck true forever — the button never
63
+ // recovers from its loading state.
64
+ const closePollRef = useRef(null);
65
+ const clearClosePoll = useCallback(() => {
66
+ if (closePollRef.current !== null) {
67
+ clearInterval(closePollRef.current);
68
+ closePollRef.current = null;
69
+ }
70
+ }, []);
60
71
  const onSuccessRef = useRef(options?.onSuccess);
61
72
  const onErrorRef = useRef(options?.onError);
62
73
  const onSessionSyncedRef = useRef(options?.onSessionSynced);
@@ -65,6 +76,8 @@ export function useGoogleLogin(options) {
65
76
  useEffect(() => { onErrorRef.current = options?.onError; }, [options?.onError]);
66
77
  useEffect(() => { onSessionSyncedRef.current = options?.onSessionSynced; }, [options?.onSessionSynced]);
67
78
  useEffect(() => { authEventResetRef.current = authEvent.reset; }, [authEvent.reset]);
79
+ // Stop polling for popup-closed if the component unmounts mid-flow.
80
+ useEffect(() => clearClosePoll, [clearClosePoll]);
68
81
  // RTDB status watcher — fast path when RTDB is available.
69
82
  // FAILED is intentionally not forwarded to onError here: RTDB connection
70
83
  // failures (e.g. missing database URL) should fall through to the postMessage
@@ -75,6 +88,7 @@ export function useGoogleLogin(options) {
75
88
  if (calledRef.current)
76
89
  return;
77
90
  calledRef.current = true;
91
+ clearClosePoll();
78
92
  Promise.resolve(onSessionSyncedRef.current?.()).then(() => {
79
93
  onSuccessRef.current?.(authEvent.data);
80
94
  });
@@ -85,10 +99,11 @@ export function useGoogleLogin(options) {
85
99
  if (calledRef.current)
86
100
  return;
87
101
  calledRef.current = true;
102
+ clearClosePoll();
88
103
  onErrorRef.current?.(new Error(authEvent.error ?? "Sign-in timed out. Please try again."));
89
104
  }
90
105
  // FAILED: do not call onError — wait for the postMessage from /auth/close
91
- }, [authEvent.status, authEvent.error, authEvent.data]);
106
+ }, [authEvent.status, authEvent.error, authEvent.data, clearClosePoll]);
92
107
  // postMessage fallback — fires when /auth/close sends window.opener.postMessage.
93
108
  // This covers two cases:
94
109
  // 1. RTDB is unavailable (the primary channel failed)
@@ -104,6 +119,7 @@ export function useGoogleLogin(options) {
104
119
  return;
105
120
  calledRef.current = true;
106
121
  setPopupPending(false);
122
+ clearClosePoll();
107
123
  authEventResetRef.current();
108
124
  if (event.data.status === "success") {
109
125
  const data = event.data.uid
@@ -119,9 +135,8 @@ export function useGoogleLogin(options) {
119
135
  };
120
136
  window.addEventListener("message", handleMessage);
121
137
  return () => window.removeEventListener("message", handleMessage);
122
- }, []); // mount once uses refs, no stale closure risk
138
+ }, [clearClosePoll]); // clearClosePoll has a stable identity effectively mount-once
123
139
  const mutate = useCallback(async () => {
124
- // toast-intentionally-silent: OAuth popup flow — errors forwarded to onErrorRef callback, not toast
125
140
  calledRef.current = false; // reset for each new auth flow
126
141
  const popup = window.open(`${window.location.origin}/auth.html`, "oauth_google", "width=500,height=660,left=400,top=100");
127
142
  if (!popup) {
@@ -129,6 +144,16 @@ export function useGoogleLogin(options) {
129
144
  return;
130
145
  }
131
146
  setPopupPending(true);
147
+ clearClosePoll();
148
+ closePollRef.current = setInterval(() => {
149
+ if (popup.closed && !calledRef.current) {
150
+ calledRef.current = true;
151
+ clearClosePoll();
152
+ setPopupPending(false);
153
+ authEventResetRef.current();
154
+ onErrorRef.current?.(new Error("Sign-in was cancelled."));
155
+ }
156
+ }, 500);
132
157
  try {
133
158
  setInitiating(true);
134
159
  authEvent.reset();
@@ -143,6 +168,7 @@ export function useGoogleLogin(options) {
143
168
  }
144
169
  catch (err) {
145
170
  void normalizeError(err);
171
+ clearClosePoll();
146
172
  popup.close();
147
173
  setPopupPending(false);
148
174
  onErrorRef.current?.(err instanceof Error ? err : new Error("Failed to start sign-in."));
@@ -150,7 +176,7 @@ export function useGoogleLogin(options) {
150
176
  finally {
151
177
  setInitiating(false);
152
178
  }
153
- }, [authEvent]);
179
+ }, [authEvent, clearClosePoll]);
154
180
  const isLoading = initiating ||
155
181
  popupPending ||
156
182
  authEvent.status === RealtimeEventStatus.SUBSCRIBING ||
@@ -167,13 +193,12 @@ export function useRegister(options) {
167
193
  displayName: data.displayName?.trim() || "",
168
194
  acceptTerms: data.acceptTerms,
169
195
  });
170
- // Sign in via client SDK and exchange ID token for a server session cookie
196
+ // /api/auth/register already created the server-side session cookie +
197
+ // Firestore session doc. Sign in via the client SDK only so
198
+ // `auth.currentUser` is populated for client-side Firebase usage
199
+ // (RTDB presence, etc) — do NOT call /api/auth/session again here,
200
+ // that would create a second, orphaned session document.
171
201
  await getClientAuthProvider().signInWithEmailAndPassword(data.email.trim(), data.password);
172
- const currentUser = getClientSessionAdapter().getCurrentUser();
173
- if (currentUser) {
174
- const idToken = await currentUser.getIdToken(true);
175
- await apiClient.post(AUTH_ENDPOINTS.SESSION, { idToken });
176
- }
177
202
  return { success: true, ...response };
178
203
  },
179
204
  onSuccess: options?.onSuccess,
@@ -109,7 +109,8 @@ export interface UserSoftBan {
109
109
  /** Null or absent = permanent. Past date = expired (treated as lifted without deletion). */
110
110
  expiresAt?: Date | null;
111
111
  }
112
- export type SellerShippingMethod = "custom" | "shiprocket";
112
+ /** Manual shipping is the only supported method — sellers enter carrier + tracking directly, no carrier API integration. */
113
+ export type SellerShippingMethod = "custom";
113
114
  export interface SellerPickupAddress {
114
115
  locationName: string;
115
116
  name: string;
@@ -122,16 +123,11 @@ export interface SellerPickupAddress {
122
123
  pincode: string;
123
124
  country: string;
124
125
  isVerified: boolean;
125
- shiprocketAddressId?: number;
126
126
  }
127
127
  export interface SellerShippingConfig {
128
128
  method: SellerShippingMethod;
129
129
  customShippingPrice?: number;
130
130
  customCarrierName?: string;
131
- shiprocketEmail?: string;
132
- /** Shiprocket JWT — server-only, never returned to client. */
133
- shiprocketToken?: string;
134
- shiprocketTokenExpiry?: Date;
135
131
  pickupAddress?: SellerPickupAddress;
136
132
  isConfigured: boolean;
137
133
  }
@@ -5,6 +5,7 @@
5
5
  * for the cart feature.
6
6
  */
7
7
  import type { BaseDocument } from "../../../_internal/shared/types/base-document";
8
+ import type { ListingType } from "../../products/types/index";
8
9
  export interface CartItemDocument {
9
10
  itemId: string;
10
11
  productId: string;
@@ -22,7 +23,7 @@ export interface CartItemDocument {
22
23
  * carts) and cart-side UI badges. Replaces the legacy `isAuction`/`isPreOrder`
23
24
  * pair on the cart item.
24
25
  */
25
- listingType: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live";
26
+ listingType: ListingType;
26
27
  /** True when item was added from an accepted Make-an-Offer */
27
28
  isOffer?: boolean;
28
29
  offerId?: string;
@@ -110,7 +111,7 @@ export type AddToCartInput = {
110
111
  /** Store slug (= storeId = store.id) */
111
112
  storeId: string;
112
113
  storeName: string;
113
- listingType: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live";
114
+ listingType: ListingType;
114
115
  isOffer?: boolean;
115
116
  offerId?: string;
116
117
  lockedPrice?: number;
@@ -26,7 +26,7 @@ export declare const consultationBookingSchema: z.ZodObject<{
26
26
  adminNote: z.ZodOptional<z.ZodString>;
27
27
  createdAt: z.ZodOptional<z.ZodString>;
28
28
  }, "strip", z.ZodTypeAny, {
29
- status: "cancelled" | "pending" | "completed" | "confirmed" | "no_show";
29
+ status: "cancelled" | "pending" | "confirmed" | "completed" | "no_show";
30
30
  id: string;
31
31
  name: string;
32
32
  email: string;
@@ -43,7 +43,7 @@ export declare const consultationBookingSchema: z.ZodObject<{
43
43
  id: string;
44
44
  name: string;
45
45
  email: string;
46
- status?: "cancelled" | "pending" | "completed" | "confirmed" | "no_show" | undefined;
46
+ status?: "cancelled" | "pending" | "confirmed" | "completed" | "no_show" | undefined;
47
47
  message?: string | undefined;
48
48
  phone?: string | undefined;
49
49
  createdAt?: string | undefined;
@@ -74,7 +74,6 @@ export function useHistory(userId) {
74
74
  const [items, setItems] = useState([]);
75
75
  const trackTimers = useRef(new Map());
76
76
  const loadItems = useCallback(async () => {
77
- // toast-intentionally-silent: background history load — normalizeError logs, silent fail is acceptable
78
77
  try {
79
78
  if (isAuth) {
80
79
  setItems(await fetchAuthHistory());