@mohasinac/appkit 3.2.20 → 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/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/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/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 -9
- 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/loyalty/schemas/index.d.ts +6 -6
- 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/SellerArtView.d.ts +7 -0
- package/dist/features/seller/components/SellerArtView.js +114 -0
- 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/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/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/client.d.ts
CHANGED
|
@@ -269,7 +269,7 @@ export { useHistory, useHistoryMergeOnLogin, getGuestHistory, trackGuestHistory,
|
|
|
269
269
|
export type { GuestHistoryItem, GuestHistoryType, UserHistoryItem, HistoryProductType, HistoryItemSnapshot, } from "./features/history/index";
|
|
270
270
|
export type { TrackArgs as TrackHistoryArgs } from "./features/history/hooks/useHistory";
|
|
271
271
|
export { WISHLIST_MAX, HISTORY_MAX, CART_MAX_ITEMS, } from "./constants/limits";
|
|
272
|
-
export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing, isClassifiedListing, isDigitalCodeListing, isLiveListing, } from "./features/products/utils/listing-type";
|
|
272
|
+
export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing, isClassifiedListing, isDigitalCodeListing, isLiveListing, isArtListing, isStickersListing, } from "./features/products/utils/listing-type";
|
|
273
273
|
export { isListingTypeEnabled, isCategoryTypeEnabled, enabledListingTypes, enabledCategoryTypes, } from "./_internal/shared/listing-types/feature-flags";
|
|
274
274
|
export { actionTracker, setActionTrackerSink, resetActionTrackerSink, type ActionEvent, type ActionTrackerSink, } from "./_internal/shared/listing-types/action-tracker";
|
|
275
275
|
export { cartRequiresShipping, cartIsDigitalOnly, cartIsChatOnly, } from "./_internal/shared/listing-types/cart-shipping";
|
|
@@ -281,8 +281,9 @@ export type { DashboardLayoutClientProps, RoleGuardProps, } from "./_internal/cl
|
|
|
281
281
|
export type { LayoutBreakpoint, DashboardVariant, LayoutRole, SidebarNavItem, SidebarNavGroup, MainNavItem as LayoutMainNavItem, BrandingConfig, FooterConfig, SectionResponsive, SectionTheming, LayoutConfig, DashboardLayoutConfig, } from "./_internal/shared/features/layout/index";
|
|
282
282
|
export { LISTING_TYPE_CAPABILITIES, capabilityFor, canAddToCart, canBid, supportsShipping, requiresVendorVerified, requiresJurisdictionCheck, hasInstantFulfillment, assertNever, } from "./_internal/shared/listing-types/capabilities";
|
|
283
283
|
export type { ListingTypeCapability } from "./_internal/shared/listing-types/capabilities";
|
|
284
|
-
export { LISTING_TYPE_REGISTRY, pluginFor } from "./_internal/shared/listing-types/_registry";
|
|
284
|
+
export { LISTING_TYPE_REGISTRY, pluginFor, detectListingTypeFromSlug } from "./_internal/shared/listing-types/_registry";
|
|
285
285
|
export type { ListingTypePlugin } from "./_internal/shared/listing-types/_registry";
|
|
286
|
+
export type { ListingType } from "./features/products/types/index";
|
|
286
287
|
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, } from "./_internal/shared/media/limits";
|
|
287
288
|
export type { MediaKind, AllowedImageMime, AllowedVideoMime, AllowedDocMime, AllowedMime, } from "./_internal/shared/media/limits";
|
|
288
289
|
export { ScamAwarenessModal } from "./features/scams/components/ScamAwarenessModal";
|
|
@@ -317,6 +318,10 @@ export { SellerBundlesView } from "./features/seller/components/SellerBundlesVie
|
|
|
317
318
|
export type { SellerBundlesViewProps } from "./features/seller/components/SellerBundlesView";
|
|
318
319
|
export { SellerClassifiedView } from "./features/seller/components/SellerClassifiedView";
|
|
319
320
|
export type { SellerClassifiedViewProps } from "./features/seller/components/SellerClassifiedView";
|
|
321
|
+
export { SellerArtView } from "./features/seller/components/SellerArtView";
|
|
322
|
+
export type { SellerArtViewProps } from "./features/seller/components/SellerArtView";
|
|
323
|
+
export { SellerStickersView } from "./features/seller/components/SellerStickersView";
|
|
324
|
+
export type { SellerStickersViewProps } from "./features/seller/components/SellerStickersView";
|
|
320
325
|
export { SellerDigitalCodesView } from "./features/seller/components/SellerDigitalCodesView";
|
|
321
326
|
export type { SellerDigitalCodesViewProps } from "./features/seller/components/SellerDigitalCodesView";
|
|
322
327
|
export { SellerLiveView } from "./features/seller/components/SellerLiveView";
|
package/dist/client.js
CHANGED
|
@@ -274,7 +274,7 @@ export { useHistory, useHistoryMergeOnLogin, getGuestHistory, trackGuestHistory,
|
|
|
274
274
|
export { WISHLIST_MAX, HISTORY_MAX, CART_MAX_ITEMS, } from "./constants/limits";
|
|
275
275
|
// SB1-G canonical listing-type accessors (pure functions, client-safe).
|
|
276
276
|
// SB-UNI-F 2026-05-13 — Phase 2 predicates surfaced through client barrel.
|
|
277
|
-
export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing, isClassifiedListing, isDigitalCodeListing, isLiveListing, } from "./features/products/utils/listing-type";
|
|
277
|
+
export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing, isClassifiedListing, isDigitalCodeListing, isLiveListing, isArtListing, isStickersListing, } from "./features/products/utils/listing-type";
|
|
278
278
|
// SB-UNI-X4 2026-05-13 — per-type feature-flag helpers (client-safe).
|
|
279
279
|
export { isListingTypeEnabled, isCategoryTypeEnabled, enabledListingTypes, enabledCategoryTypes, } from "./_internal/shared/listing-types/feature-flags";
|
|
280
280
|
// SB-UNI-X5 2026-05-13 — action telemetry sink (client-safe; defaults to
|
|
@@ -292,7 +292,7 @@ export { isAdminUser, isSellerUser, isModeratorUser, isEmployeeUser, isBuyerUser
|
|
|
292
292
|
export { DashboardLayoutClient, RoleGuard } from "./_internal/client/features/layout/index";
|
|
293
293
|
// Listing-type capability registry — SB-UNI X1.
|
|
294
294
|
export { LISTING_TYPE_CAPABILITIES, capabilityFor, canAddToCart, canBid, supportsShipping, requiresVendorVerified, requiresJurisdictionCheck, hasInstantFulfillment, assertNever, } from "./_internal/shared/listing-types/capabilities";
|
|
295
|
-
export { LISTING_TYPE_REGISTRY, pluginFor } from "./_internal/shared/listing-types/_registry";
|
|
295
|
+
export { LISTING_TYPE_REGISTRY, pluginFor, detectListingTypeFromSlug } from "./_internal/shared/listing-types/_registry";
|
|
296
296
|
// Media upload limits — shared by client uploaders + server sign/finalize routes.
|
|
297
297
|
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, } from "./_internal/shared/media/limits";
|
|
298
298
|
export { ScamAwarenessModal } from "./features/scams/components/ScamAwarenessModal";
|
|
@@ -315,6 +315,8 @@ export { SellerShippingConfigsView } from "./features/seller/components/SellerSh
|
|
|
315
315
|
export { SellerGoogleReviewsView } from "./features/seller/components/SellerGoogleReviewsView";
|
|
316
316
|
export { SellerBundlesView } from "./features/seller/components/SellerBundlesView";
|
|
317
317
|
export { SellerClassifiedView } from "./features/seller/components/SellerClassifiedView";
|
|
318
|
+
export { SellerArtView } from "./features/seller/components/SellerArtView";
|
|
319
|
+
export { SellerStickersView } from "./features/seller/components/SellerStickersView";
|
|
318
320
|
export { SellerDigitalCodesView } from "./features/seller/components/SellerDigitalCodesView";
|
|
319
321
|
export { SellerLiveView } from "./features/seller/components/SellerLiveView";
|
|
320
322
|
// ── Central schema registry (W1) ──────────────────────────────────────────────
|
|
@@ -121,6 +121,8 @@ export declare const PRODUCT_FIELDS: {
|
|
|
121
121
|
readonly CLASSIFIED: "classified";
|
|
122
122
|
readonly DIGITAL_CODE: "digital-code";
|
|
123
123
|
readonly LIVE: "live";
|
|
124
|
+
readonly ART: "art";
|
|
125
|
+
readonly STICKERS: "stickers";
|
|
124
126
|
};
|
|
125
127
|
};
|
|
126
128
|
export declare const PRODUCT_STATUS_TRANSITIONS: Record<string, readonly string[]>;
|
|
@@ -160,11 +162,6 @@ export declare const ORDER_FIELDS: {
|
|
|
160
162
|
readonly SHIPPING_METHOD: "shippingMethod";
|
|
161
163
|
readonly SHIPPING_CARRIER: "shippingCarrier";
|
|
162
164
|
readonly TRACKING_URL: "trackingUrl";
|
|
163
|
-
readonly SHIPROCKET_ORDER_ID: "shiprocketOrderId";
|
|
164
|
-
readonly SHIPROCKET_SHIPMENT_ID: "shiprocketShipmentId";
|
|
165
|
-
readonly SHIPROCKET_AWB: "shiprocketAWB";
|
|
166
|
-
readonly SHIPROCKET_STATUS: "shiprocketStatus";
|
|
167
|
-
readonly SHIPROCKET_UPDATED_AT: "shiprocketUpdatedAt";
|
|
168
165
|
readonly ITEMS: "items";
|
|
169
166
|
readonly TOTAL_AMOUNT: "totalAmount";
|
|
170
167
|
readonly BUYER_ID: "buyerId";
|
|
@@ -195,7 +192,6 @@ export declare const ORDER_FIELDS: {
|
|
|
195
192
|
};
|
|
196
193
|
readonly SHIPPING_METHOD_VALUES: {
|
|
197
194
|
readonly CUSTOM: "custom";
|
|
198
|
-
readonly SHIPROCKET: "shiprocket";
|
|
199
195
|
};
|
|
200
196
|
};
|
|
201
197
|
export declare const REVIEW_FIELDS: {
|
|
@@ -126,6 +126,8 @@ export const PRODUCT_FIELDS = {
|
|
|
126
126
|
CLASSIFIED: "classified",
|
|
127
127
|
DIGITAL_CODE: "digital-code",
|
|
128
128
|
LIVE: "live",
|
|
129
|
+
ART: "art",
|
|
130
|
+
STICKERS: "stickers",
|
|
129
131
|
},
|
|
130
132
|
};
|
|
131
133
|
export const PRODUCT_STATUS_TRANSITIONS = {
|
|
@@ -173,11 +175,6 @@ export const ORDER_FIELDS = {
|
|
|
173
175
|
SHIPPING_METHOD: "shippingMethod",
|
|
174
176
|
SHIPPING_CARRIER: "shippingCarrier",
|
|
175
177
|
TRACKING_URL: "trackingUrl",
|
|
176
|
-
SHIPROCKET_ORDER_ID: "shiprocketOrderId",
|
|
177
|
-
SHIPROCKET_SHIPMENT_ID: "shiprocketShipmentId",
|
|
178
|
-
SHIPROCKET_AWB: "shiprocketAWB",
|
|
179
|
-
SHIPROCKET_STATUS: "shiprocketStatus",
|
|
180
|
-
SHIPROCKET_UPDATED_AT: "shiprocketUpdatedAt",
|
|
181
178
|
ITEMS: "items",
|
|
182
179
|
TOTAL_AMOUNT: "totalAmount",
|
|
183
180
|
BUYER_ID: "buyerId",
|
|
@@ -208,7 +205,6 @@ export const ORDER_FIELDS = {
|
|
|
208
205
|
},
|
|
209
206
|
SHIPPING_METHOD_VALUES: {
|
|
210
207
|
CUSTOM: "custom",
|
|
211
|
-
SHIPROCKET: "shiprocket",
|
|
212
208
|
},
|
|
213
209
|
};
|
|
214
210
|
// ============================================================================
|
|
@@ -2,8 +2,10 @@ export type { WhereOp, SieveQuery, PagedResult, IReadRepository, IWriteRepositor
|
|
|
2
2
|
export type { AuthPayload, AuthUser, CreateUserInput, IAuthProvider, ISessionProvider, } from "./auth";
|
|
3
3
|
export type { EmailAttachment, EmailOptions, EmailResult, IEmailProvider, } from "./email";
|
|
4
4
|
export type { UploadOptions, StorageFile, IStorageProvider } from "./storage";
|
|
5
|
-
export type { PaymentOrder, PaymentCapture, Refund
|
|
6
|
-
export
|
|
5
|
+
export type { PaymentOrder, PaymentCapture, Refund } from "./payment";
|
|
6
|
+
export { IPaymentProvider } from "./payment";
|
|
7
|
+
export type { ShippingAddress, CreateShipmentInput, Shipment, TrackingEvent, TrackingInfo, ServiceabilityResult, } from "./shipping";
|
|
8
|
+
export { IShippingProvider } from "./shipping";
|
|
7
9
|
export type { SearchOptions, SearchHit, SearchResult, SuggestOptions, ISearchProvider, } from "./search";
|
|
8
10
|
export type { ICacheProvider, QueueJob, IQueueProvider, EventHandler, IEventBus, } from "./infra";
|
|
9
11
|
export type { IStyleAdapter } from "./style";
|
package/dist/contracts/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
// @mohasinac/contracts
|
|
2
2
|
// Pure TypeScript interfaces and shared types — no concrete code.
|
|
3
3
|
// Every other @mohasinac/* package depends on this package.
|
|
4
|
+
// IPaymentProvider is an abstract class (not a pure type) — subclasses need
|
|
5
|
+
// the runtime constructor to `extends` it, so it's a value export.
|
|
6
|
+
export { IPaymentProvider } from "./payment";
|
|
7
|
+
// IShippingProvider is an abstract class (not a pure type) — subclasses need
|
|
8
|
+
// the runtime constructor to `extends` it, so it's a value export.
|
|
9
|
+
export { IShippingProvider } from "./shipping";
|
|
4
10
|
export { registerProviders, getProviders, _resetProviders } from "./registry";
|
|
5
11
|
export { setCollectionHooks, getCollectionHooks, removeCollectionHooks, _resetCollectionHooks, } from "./repository";
|
|
6
12
|
export { registerFormFields, resolveFormFields, removeFormFields, _resetFormFields, } from "./form";
|
|
@@ -26,15 +26,20 @@ export interface Refund {
|
|
|
26
26
|
createdAt: string;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
* Payment gateway adapter contract.
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* Payment gateway adapter contract. An abstract base class rather than a
|
|
30
|
+
* plain interface so every provider (manual, Razorpay, and any future
|
|
31
|
+
* gateway) extends one shared type — adding a new provider is a subclass,
|
|
32
|
+
* not a fresh reimplementation of the shape.
|
|
33
|
+
*
|
|
34
|
+
* Implemented by the manual provider (default), Razorpay, and their mocks.
|
|
32
35
|
*/
|
|
33
|
-
export
|
|
34
|
-
|
|
36
|
+
export declare abstract class IPaymentProvider {
|
|
37
|
+
/** Short discriminator used by admin dev tooling to confirm which provider is registered. */
|
|
38
|
+
abstract readonly name: string;
|
|
39
|
+
abstract createOrder(amount: number, currency: string, metadata?: Record<string, JsonValue>): Promise<PaymentOrder>;
|
|
35
40
|
/** Returns true if the webhook signature is valid. */
|
|
36
|
-
verifyWebhook(payload: string, signature: string): boolean;
|
|
37
|
-
capturePayment(orderId: string): Promise<PaymentCapture>;
|
|
38
|
-
refund(paymentId: string, amount?: number): Promise<Refund>;
|
|
39
|
-
getOrder(orderId: string): Promise<PaymentOrder>;
|
|
41
|
+
abstract verifyWebhook(payload: string, signature: string): boolean;
|
|
42
|
+
abstract capturePayment(orderId: string): Promise<PaymentCapture>;
|
|
43
|
+
abstract refund(paymentId: string, amount?: number): Promise<Refund>;
|
|
44
|
+
abstract getOrder(orderId: string): Promise<PaymentOrder>;
|
|
40
45
|
}
|
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
// --- Payment Shared Types -----------------------------------------------------
|
|
2
|
-
|
|
2
|
+
// --- Payment Contract -----------------------------------------------------
|
|
3
|
+
/**
|
|
4
|
+
* Payment gateway adapter contract. An abstract base class rather than a
|
|
5
|
+
* plain interface so every provider (manual, Razorpay, and any future
|
|
6
|
+
* gateway) extends one shared type — adding a new provider is a subclass,
|
|
7
|
+
* not a fresh reimplementation of the shape.
|
|
8
|
+
*
|
|
9
|
+
* Implemented by the manual provider (default), Razorpay, and their mocks.
|
|
10
|
+
*/
|
|
11
|
+
export class IPaymentProvider {
|
|
12
|
+
}
|
|
@@ -51,14 +51,19 @@ export interface ServiceabilityResult {
|
|
|
51
51
|
}>;
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
-
* Shipping carrier adapter contract.
|
|
55
|
-
*
|
|
56
|
-
*
|
|
54
|
+
* Shipping carrier adapter contract. An abstract base class rather than a
|
|
55
|
+
* plain interface so every provider (manual, and any future carrier
|
|
56
|
+
* integration) extends one shared type — adding a new carrier is a
|
|
57
|
+
* subclass, not a fresh reimplementation of the shape.
|
|
58
|
+
*
|
|
59
|
+
* Implemented by the manual provider (default) and its mock.
|
|
57
60
|
*/
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
export declare abstract class IShippingProvider {
|
|
62
|
+
/** Short discriminator used by admin dev tooling to confirm which provider is registered. */
|
|
63
|
+
abstract readonly name: string;
|
|
64
|
+
abstract createShipment(data: CreateShipmentInput): Promise<Shipment>;
|
|
65
|
+
abstract trackShipment(trackingId: string): Promise<TrackingInfo>;
|
|
66
|
+
abstract cancelShipment(shipmentId: string): Promise<void>;
|
|
67
|
+
abstract checkServiceability(pincode: string, weight: number): Promise<ServiceabilityResult>;
|
|
68
|
+
abstract generateLabel(shipmentId: string): Promise<ArrayBuffer>;
|
|
64
69
|
}
|
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
// --- Shipping Shared Types ----------------------------------------------------
|
|
2
|
-
|
|
2
|
+
// --- Shipping Contract ------------------------------------------------------
|
|
3
|
+
/**
|
|
4
|
+
* Shipping carrier adapter contract. An abstract base class rather than a
|
|
5
|
+
* plain interface so every provider (manual, and any future carrier
|
|
6
|
+
* integration) extends one shared type — adding a new carrier is a
|
|
7
|
+
* subclass, not a fresh reimplementation of the shape.
|
|
8
|
+
*
|
|
9
|
+
* Implemented by the manual provider (default) and its mock.
|
|
10
|
+
*/
|
|
11
|
+
export class IShippingProvider {
|
|
12
|
+
}
|
|
@@ -9,7 +9,8 @@ import { Anchor, Div, Grid, Heading, Row, Section, Span, Stack, Text } from "../
|
|
|
9
9
|
import { MediaImage } from "../../media/MediaImage";
|
|
10
10
|
import { ProductCard } from "../../products/components/ProductGrid";
|
|
11
11
|
import { ReviewCard } from "../../reviews/components/ReviewsList";
|
|
12
|
-
import {
|
|
12
|
+
import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
|
|
13
|
+
import { normalizeListingType } from "../../products/utils/listing-type";
|
|
13
14
|
import { isAdminUser, isSellerUser } from "../../auth/role-predicates";
|
|
14
15
|
import { User, Star, ShoppingBag, Package, Trophy, Globe, MapPin, ExternalLink } from "lucide-react";
|
|
15
16
|
const __P = {
|
|
@@ -50,11 +51,7 @@ function toProductItem(p) {
|
|
|
50
51
|
};
|
|
51
52
|
}
|
|
52
53
|
function getProductHref(p) {
|
|
53
|
-
|
|
54
|
-
return String(ROUTES.PUBLIC.AUCTION_DETAIL(p.id));
|
|
55
|
-
if (isPreOrderListing(p))
|
|
56
|
-
return String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id));
|
|
57
|
-
return String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.id));
|
|
54
|
+
return pluginFor(normalizeListingType(p)).detailRoute(p.id);
|
|
58
55
|
}
|
|
59
56
|
export async function PublicProfileView({ userId, }) {
|
|
60
57
|
const page = { container: PAGE_CONTAINER };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdminAddressBookView — admin lookup of the addresses collection by owner.
|
|
3
|
+
* The list endpoint (GET /api/admin/addresses) only supports per-owner or
|
|
4
|
+
* per-banStatus queries, not a global browse, so this is a search-by-owner
|
|
5
|
+
* tool rather than a paginated DataListingView.
|
|
6
|
+
*/
|
|
7
|
+
import React from "react";
|
|
8
|
+
export interface AdminAddressBookViewProps {
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
export declare function AdminAddressBookView(_props: AdminAddressBookViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* AdminAddressBookView — admin lookup of the addresses collection by owner.
|
|
5
|
+
* The list endpoint (GET /api/admin/addresses) only supports per-owner or
|
|
6
|
+
* per-banStatus queries, not a global browse, so this is a search-by-owner
|
|
7
|
+
* tool rather than a paginated DataListingView.
|
|
8
|
+
*/
|
|
9
|
+
import React from "react";
|
|
10
|
+
import { useQuery } from "@tanstack/react-query";
|
|
11
|
+
import { Badge, Button, Card, FieldSelect, Heading, Input, Row, Stack, Text, TextLink, } from "../../../ui";
|
|
12
|
+
import { apiClient } from "../../../http";
|
|
13
|
+
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
14
|
+
import { ROUTES } from "../../../next/routing/route-map";
|
|
15
|
+
const OWNER_TYPE_OPTIONS = [
|
|
16
|
+
{ value: "user", label: "User" },
|
|
17
|
+
{ value: "store", label: "Store" },
|
|
18
|
+
];
|
|
19
|
+
export function AdminAddressBookView(_props) {
|
|
20
|
+
const [ownerType, setOwnerType] = React.useState("user");
|
|
21
|
+
const [ownerIdInput, setOwnerIdInput] = React.useState("");
|
|
22
|
+
const [searchedOwnerId, setSearchedOwnerId] = React.useState(null);
|
|
23
|
+
const { data, isLoading, error } = useQuery({
|
|
24
|
+
queryKey: ["admin", "addresses", "by-owner", ownerType, searchedOwnerId],
|
|
25
|
+
queryFn: () => apiClient.get(`${ADMIN_ENDPOINTS.ADDRESSES}?ownerType=${ownerType}&ownerId=${encodeURIComponent(searchedOwnerId)}`),
|
|
26
|
+
enabled: !!searchedOwnerId,
|
|
27
|
+
});
|
|
28
|
+
const addresses = data?.items ?? [];
|
|
29
|
+
return (_jsxs(Stack, { gap: "lg", padding: "page", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Stack, { gap: "none", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", color: "primary", children: "Addresses" }), _jsx(Text, { size: "sm", color: "muted", className: "mt-1", children: "Look up a user's or store's saved addresses by owner ID." })] }), _jsx(TextLink, { href: String(ROUTES.ADMIN.ADDRESSES_NEW), children: _jsx(Button, { type: "button", children: "+ New address" }) })] }), _jsx(Card, { variant: "outlined", padding: "md", children: _jsxs(Row, { gap: "md", align: "end", wrap: true, children: [_jsx(FieldSelect, { name: "ownerType", label: "Owner type", value: ownerType, onChange: (v) => setOwnerType(v), options: OWNER_TYPE_OPTIONS }), _jsx(Input, { name: "ownerId", label: "Owner ID", placeholder: ownerType === "user" ? "user-..." : "store-...", value: ownerIdInput, onChange: (e) => setOwnerIdInput(e.target.value) }), _jsx(Button, { type: "button", isLoading: isLoading, disabled: !ownerIdInput.trim(), onClick: () => setSearchedOwnerId(ownerIdInput.trim()), children: "Search" })] }) }), error && (_jsx(Text, { size: "sm", color: "danger", children: error?.message ?? "Failed to load addresses." })), searchedOwnerId && !isLoading && addresses.length === 0 && !error && (_jsx(Text, { size: "sm", color: "muted", children: "No addresses found for this owner." })), addresses.length > 0 && (_jsx(Stack, { gap: "sm", children: addresses.map((addr) => (_jsx(Card, { variant: "outlined", padding: "md", children: _jsxs(Row, { justify: "between", align: "center", wrap: true, children: [_jsxs(Stack, { gap: "none", children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Text, { weight: "medium", children: addr.label }), addr.isDefault && _jsx(Badge, { variant: "secondary", size: "sm", children: "Default" })] }), _jsx(Text, { size: "sm", color: "muted", children: addr.fullName }), _jsx(Text, { size: "sm", color: "muted", children: [addr.addressLine1, addr.city, addr.state, addr.postalCode].filter(Boolean).join(", ") })] }), _jsx(TextLink, { href: String(ROUTES.ADMIN.ADDRESSES_EDIT(addr.id)), children: _jsx(Button, { type: "button", variant: "secondary", children: "Edit" }) })] }) }, addr.id))) }))] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* AdminArtView — admin browse of art print 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_ART_CONFIG = {
|
|
14
|
+
portal: "admin",
|
|
15
|
+
title: "Art",
|
|
16
|
+
searchPlaceholder: "Search art prints by name or seller",
|
|
17
|
+
emptyLabel: "No art listings",
|
|
18
|
+
filterKeys: [],
|
|
19
|
+
defaultSort: sortBy("createdAt", "DESC"),
|
|
20
|
+
queryKey: ["admin", "art", "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, `art-${index}`),
|
|
29
|
+
primary: toStringValue(item.title ?? item.productTitle, "Untitled print"),
|
|
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==art",
|
|
39
|
+
};
|
|
40
|
+
export function AdminArtView({ 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_ART_CONFIG });
|
|
45
|
+
}
|
|
@@ -12,7 +12,7 @@ export function AdminSiteConfigGuideView() {
|
|
|
12
12
|
{ name: "contactSocial", desc: "Email, phone, social media URLs, WhatsApp business number." },
|
|
13
13
|
{ name: "watermark", desc: "Watermark text and opacity applied to all product images via the /api/media proxy." },
|
|
14
14
|
{ name: "fees", desc: "platformFee (%), Razorpay gateway fee pass-through toggle, GST rate on platform fees." },
|
|
15
|
-
{ name: "integrations", desc: "API keys: Razorpay (live/test
|
|
15
|
+
{ name: "integrations", desc: "API keys: Razorpay (live/test, disabled by default — manual payment is the default), Resend, WhatsApp Cloud API. Keys are encrypted at rest — never log or export." },
|
|
16
16
|
{ name: "shipping", desc: "Default shipping carriers, free shipping threshold (paise), COD availability toggle." },
|
|
17
17
|
{ name: "auctionConfig", desc: "Global bid increment default, auto-extend window (minutes), minimum auction duration." },
|
|
18
18
|
{ name: "platformLimits", desc: "Cart item cap (50), wishlist cap (20), history cap (50), support ticket limits (2 general + 1 per order)." },
|
|
@@ -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],
|