@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.
- package/dist/_internal/server/features/checkout/actions.d.ts +2 -0
- package/dist/_internal/server/features/checkout/actions.js +239 -190
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +9 -1
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +8 -11
- package/dist/_internal/server/jobs/core/emiInstallmentReminder.d.ts +8 -0
- package/dist/_internal/server/jobs/core/emiInstallmentReminder.js +78 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.d.ts +2 -2
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +15 -10
- package/dist/_internal/server/jobs/handlers/emiInstallmentReminder.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/emiInstallmentReminder.js +2 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +2 -0
- package/dist/_internal/server/providers/index.d.ts +4 -4
- package/dist/_internal/server/providers/index.js +4 -4
- package/dist/_internal/server/providers/payment/razorpay-mock.d.ts +3 -2
- package/dist/_internal/server/providers/payment/razorpay-mock.js +3 -1
- package/dist/_internal/server/providers/resolve.d.ts +10 -13
- package/dist/_internal/server/providers/resolve.js +9 -15
- package/dist/_internal/shared/actions/action-registry.js +28 -0
- package/dist/_internal/shared/checkout/rules/_defaults.js +1 -0
- package/dist/_internal/shared/checkout/rules/_registry.js +4 -0
- package/dist/_internal/shared/checkout/rules/art.rule.d.ts +9 -0
- package/dist/_internal/shared/checkout/rules/art.rule.js +5 -0
- package/dist/_internal/shared/checkout/rules/auction.rule.js +1 -0
- package/dist/_internal/shared/checkout/rules/classified.rule.js +1 -0
- package/dist/_internal/shared/checkout/rules/index.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/index.js +2 -0
- package/dist/_internal/shared/checkout/rules/stickers.rule.d.ts +9 -0
- package/dist/_internal/shared/checkout/rules/stickers.rule.js +5 -0
- package/dist/_internal/shared/checkout/rules/types.d.ts +6 -0
- package/dist/_internal/shared/features/checkout/config.d.ts +16 -1
- package/dist/_internal/shared/features/checkout/config.js +16 -1
- package/dist/_internal/shared/features/emi/schedule.d.ts +63 -0
- package/dist/_internal/shared/features/emi/schedule.js +62 -0
- package/dist/_internal/shared/fees/calculator.d.ts +53 -0
- package/dist/_internal/shared/fees/calculator.js +40 -0
- package/dist/_internal/shared/listing-types/_registry.d.ts +22 -66
- package/dist/_internal/shared/listing-types/_registry.js +19 -2
- package/dist/_internal/shared/listing-types/art/config.d.ts +15 -0
- package/dist/_internal/shared/listing-types/art/config.js +14 -0
- package/dist/_internal/shared/listing-types/art/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/art/ctas.js +3 -0
- package/dist/_internal/shared/listing-types/art/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/art/og.js +1 -0
- package/dist/_internal/shared/listing-types/art/schema.d.ts +2 -0
- package/dist/_internal/shared/listing-types/art/schema.js +3 -0
- package/dist/_internal/shared/listing-types/art/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/art/seed-factory.js +1 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/auction/config.js +6 -0
- package/dist/_internal/shared/listing-types/capabilities.js +17 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/classified/config.js +6 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +6 -0
- package/dist/_internal/shared/listing-types/live/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/live/config.js +6 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +6 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +6 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/standard/config.js +6 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +15 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +14 -0
- package/dist/_internal/shared/listing-types/stickers/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/stickers/ctas.js +3 -0
- package/dist/_internal/shared/listing-types/stickers/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/stickers/og.js +1 -0
- package/dist/_internal/shared/listing-types/stickers/schema.d.ts +2 -0
- package/dist/_internal/shared/listing-types/stickers/schema.js +3 -0
- package/dist/_internal/shared/listing-types/stickers/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/stickers/seed-factory.js +1 -0
- package/dist/client.d.ts +7 -2
- package/dist/client.js +4 -2
- package/dist/constants/field-names.d.ts +2 -6
- package/dist/constants/field-names.js +2 -6
- package/dist/contracts/index.d.ts +4 -2
- package/dist/contracts/index.js +6 -0
- package/dist/contracts/payment.d.ts +14 -9
- package/dist/contracts/payment.js +11 -1
- package/dist/contracts/shipping.d.ts +14 -9
- package/dist/contracts/shipping.js +11 -1
- package/dist/features/about/components/PublicProfileView.js +3 -6
- package/dist/features/account/components/UserOffersPanel.js +0 -1
- package/dist/features/admin/components/AdminAddressBookView.d.ts +11 -0
- package/dist/features/admin/components/AdminAddressBookView.js +30 -0
- package/dist/features/admin/components/AdminArtView.d.ts +3 -0
- package/dist/features/admin/components/AdminArtView.js +45 -0
- package/dist/features/admin/components/AdminCarouselEditorView.js +2 -3
- package/dist/features/admin/components/AdminFulfillmentView.js +0 -1
- package/dist/features/admin/components/AdminSiteConfigGuideView.js +1 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +14 -9
- package/dist/features/admin/components/AdminStickersView.d.ts +3 -0
- package/dist/features/admin/components/AdminStickersView.js +45 -0
- package/dist/features/admin/components/index.d.ts +6 -0
- package/dist/features/admin/components/index.js +4 -0
- package/dist/features/admin/hooks/useChat.js +0 -1
- package/dist/features/admin/schemas/firestore.d.ts +19 -11
- package/dist/features/admin/schemas/firestore.js +12 -2
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +3 -2
- package/dist/features/auth/hooks/useAuth.js +35 -10
- package/dist/features/auth/schemas/firestore.d.ts +2 -6
- package/dist/features/cart/schemas/firestore.d.ts +3 -2
- package/dist/features/consultation/schemas/index.d.ts +2 -2
- package/dist/features/history/hooks/useHistory.js +0 -1
- package/dist/features/loyalty/schemas/index.d.ts +6 -6
- package/dist/features/messages/hooks/useConversation.js +0 -1
- package/dist/features/messages/hooks/useConversations.js +0 -1
- package/dist/features/orders/repository/orders.repository.d.ts +20 -2
- package/dist/features/orders/repository/orders.repository.js +34 -5
- package/dist/features/orders/schemas/firestore.d.ts +33 -7
- package/dist/features/orders/schemas/firestore.js +1 -1
- package/dist/features/orders/schemas/index.d.ts +176 -31
- package/dist/features/orders/schemas/index.js +20 -6
- package/dist/features/orders/types/index.d.ts +3 -3
- package/dist/features/payments/repository/payout.repository.js +2 -2
- package/dist/features/payments/schemas/firestore.d.ts +3 -3
- package/dist/features/payments/schemas/firestore.js +2 -2
- package/dist/features/payments/schemas/index.d.ts +5 -5
- package/dist/features/payments/schemas/index.js +2 -2
- package/dist/features/products/api/[id]/route.js +4 -1
- package/dist/features/products/api/route.js +4 -1
- package/dist/features/products/components/CompareOverlay.js +7 -9
- package/dist/features/products/components/ProductDetailPageView.d.ts +3 -1
- package/dist/features/products/components/ProductDetailPageView.js +2 -2
- package/dist/features/products/components/ProductForm.js +18 -7
- package/dist/features/products/components/ProductGrid.js +7 -12
- package/dist/features/products/components/ShowGroupSection.js +3 -5
- package/dist/features/products/components/SublistingCarouselSection.js +3 -6
- package/dist/features/products/constants/listing-tabs.d.ts +32 -0
- package/dist/features/products/constants/listing-tabs.js +8 -0
- package/dist/features/products/index.d.ts +1 -1
- package/dist/features/products/index.js +3 -1
- package/dist/features/products/schemas/firestore.d.ts +20 -1
- package/dist/features/products/schemas/firestore.js +2 -0
- package/dist/features/products/schemas/index.d.ts +48 -0
- package/dist/features/products/schemas/index.js +9 -0
- package/dist/features/products/schemas/product-features.d.ts +1 -1
- package/dist/features/products/types/index.d.ts +5 -3
- package/dist/features/products/utils/listing-type.d.ts +6 -0
- package/dist/features/products/utils/listing-type.js +3 -0
- package/dist/features/reviews/api/[id]/route.js +5 -4
- package/dist/features/seller/actions/seller-actions.d.ts +29 -14
- package/dist/features/seller/actions/seller-actions.js +101 -27
- package/dist/features/seller/components/FulfillmentView.js +0 -1
- package/dist/features/seller/components/SellerAddressesView.js +0 -1
- package/dist/features/seller/components/SellerArtView.d.ts +7 -0
- package/dist/features/seller/components/SellerArtView.js +114 -0
- package/dist/features/seller/components/SellerOffersPanel.js +0 -1
- package/dist/features/seller/components/SellerOrdersView.js +38 -1
- package/dist/features/seller/components/SellerProductShell.d.ts +5 -1
- package/dist/features/seller/components/SellerProductShell.js +69 -59
- package/dist/features/seller/components/SellerReviewsView.js +0 -1
- package/dist/features/seller/components/SellerShippingView.js +6 -25
- package/dist/features/seller/components/SellerStickersView.d.ts +7 -0
- package/dist/features/seller/components/SellerStickersView.js +114 -0
- package/dist/features/seller/components/index.d.ts +4 -0
- package/dist/features/seller/components/index.js +2 -0
- package/dist/features/seller/messages/en.json +1 -1
- package/dist/features/seller/schemas/index.d.ts +7 -7
- package/dist/features/seller/schemas/index.js +1 -1
- package/dist/features/seller/types/index.d.ts +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +18 -2
- package/dist/features/stores/schemas/firestore.d.ts +7 -1
- package/dist/features/stores/schemas/index.d.ts +19 -16
- package/dist/features/stores/schemas/index.js +1 -1
- package/dist/index.d.ts +21 -36
- package/dist/index.js +27 -39
- package/dist/next/routing/route-map.d.ts +22 -0
- package/dist/next/routing/route-map.js +10 -0
- package/dist/providers/payment-manual/index.d.ts +40 -0
- package/dist/providers/payment-manual/index.js +91 -0
- package/dist/providers/payment-razorpay/index.d.ts +4 -2
- package/dist/providers/payment-razorpay/index.js +4 -1
- package/dist/providers/shipping-manual/index.d.ts +22 -0
- package/dist/providers/shipping-manual/index.js +47 -0
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +5 -3
- package/dist/schemas/registry.d.ts +129 -70
- package/dist/schemas/registry.js +0 -4
- package/dist/security/pii-encrypt.js +0 -7
- package/dist/security/pii-redact.js +0 -1
- package/dist/seed/conversations-seed-data.js +2 -2
- package/dist/seed/faq-seed-data.js +7 -7
- package/dist/seed/grouped-listings-seed-data.d.ts +2 -0
- package/dist/seed/index.d.ts +2 -0
- package/dist/seed/index.js +2 -0
- package/dist/seed/manifest.js +20 -16
- package/dist/seed/payouts-seed-data.js +5 -5
- package/dist/seed/products-art-seed-data.d.ts +11 -0
- package/dist/seed/products-art-seed-data.js +125 -0
- package/dist/seed/products-preorders-seed-data.d.ts +2 -0
- package/dist/seed/products-stickers-seed-data.d.ts +11 -0
- package/dist/seed/products-stickers-seed-data.js +125 -0
- package/dist/seed/site-settings-seed-data.js +13 -4
- package/dist/seed/stores-seed-data.js +11 -11
- package/dist/server.d.ts +7 -15
- package/dist/server.js +17 -42
- package/dist/ui/rich-text/RichText.js +1 -1
- 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 [
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,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:
|
|
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
|
|
15
|
-
listingType?:
|
|
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
|
-
}, []); //
|
|
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
|
-
//
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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" | "
|
|
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" | "
|
|
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());
|