@mohasinac/appkit 2.7.17 → 2.7.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/_internal/server/features/bundles/actions.d.ts +15 -0
  2. package/dist/_internal/server/features/bundles/actions.js +48 -0
  3. package/dist/_internal/server/features/bundles/index.d.ts +1 -0
  4. package/dist/_internal/server/features/bundles/index.js +1 -0
  5. package/dist/_internal/server/features/checkout/actions.d.ts +4 -0
  6. package/dist/_internal/server/features/checkout/actions.js +23 -12
  7. package/dist/_internal/server/features/payments/data.d.ts +5 -3
  8. package/dist/_internal/server/features/payments/data.js +13 -8
  9. package/dist/_internal/server/features/payments/index.d.ts +1 -1
  10. package/dist/_internal/server/features/payments/index.js +1 -1
  11. package/dist/_internal/shared/features/categories/bundle-config.d.ts +24 -0
  12. package/dist/_internal/shared/features/categories/bundle-config.js +28 -0
  13. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +7 -0
  14. package/dist/_internal/shared/features/categories/bundle-copy.js +9 -0
  15. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +69 -19
  16. package/dist/_internal/shared/features/categories/bundle-schemas.js +20 -2
  17. package/dist/_internal/shared/features/checkout/config.d.ts +1 -1
  18. package/dist/_internal/shared/features/checkout/config.js +1 -1
  19. package/dist/_internal/shared/features/payments/config.d.ts +2 -1
  20. package/dist/_internal/shared/features/payments/config.js +2 -1
  21. package/dist/_internal/shared/features/products/schema.d.ts +8 -8
  22. package/dist/constants/field-names.d.ts +8 -1
  23. package/dist/constants/field-names.js +9 -2
  24. package/dist/constants/table-keys.d.ts +3 -0
  25. package/dist/constants/table-keys.js +3 -0
  26. package/dist/features/admin/components/AdminSiteSettingsView.js +17 -15
  27. package/dist/features/admin/components/DashboardStats.js +20 -12
  28. package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +18 -2
  29. package/dist/features/admin/components/analytics/AdminStatCard.d.ts +7 -1
  30. package/dist/features/admin/components/analytics/AdminStatCard.js +13 -2
  31. package/dist/features/admin/schemas/firestore.d.ts +18 -4
  32. package/dist/features/admin/schemas/firestore.js +11 -1
  33. package/dist/features/categories/components/BundleBuyNowCta.d.ts +3 -1
  34. package/dist/features/categories/components/BundleBuyNowCta.js +3 -3
  35. package/dist/features/categories/components/BundleDynamicRuleEditor.d.ts +1 -1
  36. package/dist/features/categories/components/BundleDynamicRuleEditor.js +2 -1
  37. package/dist/features/categories/components/BundlesListView.d.ts +4 -1
  38. package/dist/features/categories/components/BundlesListView.js +2 -2
  39. package/dist/features/categories/components/CategoryBundlesListing.d.ts +4 -1
  40. package/dist/features/categories/components/CategoryBundlesListing.js +9 -8
  41. package/dist/features/categories/schemas/firestore.d.ts +29 -1
  42. package/dist/features/checkout/actions/checkout-actions.d.ts +11 -0
  43. package/dist/features/checkout/actions/checkout-actions.js +26 -0
  44. package/dist/features/filters/FilterFacetSection.d.ts +6 -1
  45. package/dist/features/filters/FilterFacetSection.js +25 -2
  46. package/dist/features/homepage/components/FeaturedBundlesSection.d.ts +4 -1
  47. package/dist/features/homepage/components/FeaturedBundlesSection.js +9 -8
  48. package/dist/features/orders/actions/refund-actions.js +10 -7
  49. package/dist/features/orders/schemas/firestore.d.ts +16 -13
  50. package/dist/features/orders/schemas/firestore.js +6 -5
  51. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
  52. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -1
  53. package/dist/features/products/components/AuctionsIndexListing.js +2 -1
  54. package/dist/features/products/components/ProductDetailPageView.js +3 -1
  55. package/dist/features/products/components/ProductDetailView.d.ts +3 -1
  56. package/dist/features/products/components/ProductDetailView.js +2 -2
  57. package/dist/features/products/components/ProductFilters.d.ts +10 -4
  58. package/dist/features/products/components/ProductFilters.js +20 -16
  59. package/dist/features/products/components/ProductGrid.js +2 -2
  60. package/dist/features/products/components/ProductsIndexListing.js +2 -1
  61. package/dist/features/products/constants/product-features.constants.js +5 -1
  62. package/dist/features/products/schemas/index.d.ts +2 -2
  63. package/dist/features/products/schemas/product-features.d.ts +1 -1
  64. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  65. package/dist/features/products/schemas/product-features.validators.js +4 -0
  66. package/dist/features/seller/components/SellerCouponEditorView.js +2 -2
  67. package/dist/features/seller/components/analytics/SellerAnalyticsStats.js +10 -4
  68. package/dist/features/stores/components/StoreBundlesPageView.d.ts +4 -1
  69. package/dist/features/stores/components/StoreBundlesPageView.js +2 -2
  70. package/dist/features/stores/components/StoresIndexListing.js +2 -1
  71. package/dist/providers/db-firebase/admin-app-lite.js +8 -2
  72. package/dist/providers/db-firebase/admin.js +8 -2
  73. package/dist/seed/addresses-seed-data.js +24 -7
  74. package/dist/seed/cart-seed-data.d.ts +10 -10
  75. package/dist/seed/cart-seed-data.js +39 -2
  76. package/dist/seed/grouped-listings-seed-data.js +47 -0
  77. package/dist/seed/orders-seed-data.js +150 -0
  78. package/dist/seed/products-standard-seed-data.js +42 -0
  79. package/dist/seed/site-settings-seed-data.js +11 -4
  80. package/dist/seed/users-seed-data.js +1 -1
  81. package/dist/seed/wishlists-seed-data.js +7 -0
  82. package/dist/server-entry.d.ts +2 -1
  83. package/dist/server-entry.js +2 -1
  84. package/dist/server.d.ts +2 -1
  85. package/dist/server.js +3 -1
  86. package/dist/tailwind-utilities.css +1 -1
  87. package/dist/ui/components/SideDrawer.style.css +49 -11
  88. package/package.json +1 -1
@@ -8,8 +8,16 @@
8
8
  * client too if the admin form ever pre-validates.
9
9
  */
10
10
  import { z } from "zod";
11
- import { BUNDLE_MAX_ITEMS } from "./bundle-config";
11
+ import { BUNDLE_MAX_ITEMS, BUNDLE_DRAW_COUNT_MAX, BUNDLE_KIND_SPECIAL, BUNDLE_KIND_BRAND, } from "./bundle-config";
12
12
  const productIdsSchema = z.array(z.string().min(1)).min(0).max(BUNDLE_MAX_ITEMS);
13
+ /**
14
+ * Per-member detail — mirrors BundleItemDetail in the Firestore schema.
15
+ * `drawCount` is required when the member is a prize-draw listing (1–BUNDLE_DRAW_COUNT_MAX).
16
+ */
17
+ const bundleItemDetailSchema = z.object({
18
+ productId: z.string().min(1),
19
+ drawCount: z.number().int().min(1).max(BUNDLE_DRAW_COUNT_MAX).optional(),
20
+ });
13
21
  export const bundleQueryRuleSchema = z.discriminatedUnion("type", [
14
22
  z.object({
15
23
  type: z.literal("static"),
@@ -21,7 +29,8 @@ export const bundleQueryRuleSchema = z.discriminatedUnion("type", [
21
29
  categorySlug: z.string().optional(),
22
30
  brandSlug: z.string().optional(),
23
31
  tags: z.array(z.string()).optional(),
24
- listingType: z.enum(["standard", "pre-order"]).optional(),
32
+ /** Eligible listing types for a dynamic bundle rule. */
33
+ listingType: z.enum(["standard", "pre-order", "prize-draw"]).optional(),
25
34
  }),
26
35
  orderBy: z
27
36
  .enum(["price-asc", "price-desc", "createdAt-desc"])
@@ -38,18 +47,27 @@ export const bundleCreateSchema = z.object({
38
47
  name: z.string().min(2).max(120),
39
48
  slug: z.string().min(2).max(120).optional(),
40
49
  description: z.string().max(2000).optional(),
50
+ /**
51
+ * "special" — curated bundle; updates partOfBundleIds on member products.
52
+ * "brand" — brand discovery collection; does not touch product reverse pointers.
53
+ */
54
+ bundleKind: z.enum([BUNDLE_KIND_SPECIAL, BUNDLE_KIND_BRAND]),
41
55
  bundlePriceInPaise: z.number().int().min(100),
42
56
  bundleQueryRule: bundleQueryRuleSchema,
43
57
  bundleProductIds: productIdsSchema,
58
+ /** Richer per-member metadata (draw counts for prize-draws). */
59
+ bundleItemDetails: z.array(bundleItemDetailSchema).optional(),
44
60
  display: bundleDisplaySchema.optional(),
45
61
  isActive: z.boolean().optional(),
46
62
  });
47
63
  export const bundleUpdateSchema = z.object({
48
64
  name: z.string().min(2).max(120).optional(),
49
65
  description: z.string().max(2000).optional(),
66
+ bundleKind: z.enum([BUNDLE_KIND_SPECIAL, BUNDLE_KIND_BRAND]).optional(),
50
67
  bundlePriceInPaise: z.number().int().min(100).optional(),
51
68
  bundleQueryRule: bundleQueryRuleSchema.optional(),
52
69
  bundleProductIds: productIdsSchema.optional(),
70
+ bundleItemDetails: z.array(bundleItemDetailSchema).optional(),
53
71
  display: bundleDisplaySchema.optional(),
54
72
  isActive: z.boolean().optional(),
55
73
  });
@@ -4,5 +4,5 @@ export declare const CHECKOUT_DEFAULT_COMMISSIONS: {
4
4
  readonly platformShippingPercent: 10;
5
5
  readonly platformShippingFixedMin: 50;
6
6
  };
7
- export declare const CHECKOUT_PAYMENT_METHODS: readonly ["cod", "online", "upi_manual"];
7
+ export declare const CHECKOUT_PAYMENT_METHODS: readonly ["cod", "online", "upi_manual", "admin_bypass"];
8
8
  export type CheckoutPaymentMethod = (typeof CHECKOUT_PAYMENT_METHODS)[number];
@@ -4,4 +4,4 @@ export const CHECKOUT_DEFAULT_COMMISSIONS = {
4
4
  platformShippingPercent: 10,
5
5
  platformShippingFixedMin: 50,
6
6
  };
7
- export const CHECKOUT_PAYMENT_METHODS = ["cod", "online", "upi_manual"];
7
+ export const CHECKOUT_PAYMENT_METHODS = ["cod", "online", "upi_manual", "admin_bypass"];
@@ -1,2 +1,3 @@
1
- export declare const PAYMENTS_DEFAULT_RAZORPAY_FEE_PERCENT = 5;
1
+ export declare const PAYMENTS_DEFAULT_PLATFORM_FEE_PERCENT = 5;
2
+ export declare const PAYMENTS_DEFAULT_GST_PERCENT = 18;
2
3
  export declare const PAYMENTS_RECEIPT_PREFIX = "rcpt_";
@@ -1,2 +1,3 @@
1
- export const PAYMENTS_DEFAULT_RAZORPAY_FEE_PERCENT = 5;
1
+ export const PAYMENTS_DEFAULT_PLATFORM_FEE_PERCENT = 5;
2
+ export const PAYMENTS_DEFAULT_GST_PERCENT = 18;
2
3
  export const PAYMENTS_RECEIPT_PREFIX = "rcpt_";
@@ -34,12 +34,12 @@ export declare const productInputSchema: z.ZodObject<{
34
34
  availableQuantity: number;
35
35
  mainImage: string;
36
36
  condition?: "new" | "used" | "refurbished" | "broken" | "like_new" | "good" | "fair" | "poor" | undefined;
37
+ features?: string[] | undefined;
37
38
  description?: string | undefined;
38
39
  seoTitle?: string | undefined;
39
40
  seoDescription?: string | undefined;
40
41
  seoKeywords?: string[] | undefined;
41
42
  brandSlug?: string | undefined;
42
- features?: string[] | undefined;
43
43
  shippingInfo?: string | undefined;
44
44
  returnPolicy?: string | undefined;
45
45
  insurance?: boolean | undefined;
@@ -55,6 +55,7 @@ export declare const productInputSchema: z.ZodObject<{
55
55
  currency?: string | undefined;
56
56
  condition?: "new" | "used" | "refurbished" | "broken" | "like_new" | "good" | "fair" | "poor" | undefined;
57
57
  tags?: string[] | undefined;
58
+ features?: string[] | undefined;
58
59
  images?: string[] | undefined;
59
60
  description?: string | undefined;
60
61
  seoTitle?: string | undefined;
@@ -63,7 +64,6 @@ export declare const productInputSchema: z.ZodObject<{
63
64
  brandSlug?: string | undefined;
64
65
  stockQuantity?: number | undefined;
65
66
  availableQuantity?: number | undefined;
66
- features?: string[] | undefined;
67
67
  shippingInfo?: string | undefined;
68
68
  returnPolicy?: string | undefined;
69
69
  insurance?: boolean | undefined;
@@ -103,6 +103,7 @@ export declare const productUpdateSchema: z.ZodObject<{
103
103
  price?: number | undefined;
104
104
  condition?: "new" | "used" | "refurbished" | "broken" | "like_new" | "good" | "fair" | "poor" | undefined;
105
105
  tags?: string[] | undefined;
106
+ features?: string[] | undefined;
106
107
  images?: string[] | undefined;
107
108
  description?: string | undefined;
108
109
  seoTitle?: string | undefined;
@@ -112,7 +113,6 @@ export declare const productUpdateSchema: z.ZodObject<{
112
113
  stockQuantity?: number | undefined;
113
114
  availableQuantity?: number | undefined;
114
115
  mainImage?: string | undefined;
115
- features?: string[] | undefined;
116
116
  shippingInfo?: string | undefined;
117
117
  returnPolicy?: string | undefined;
118
118
  insurance?: boolean | undefined;
@@ -127,6 +127,7 @@ export declare const productUpdateSchema: z.ZodObject<{
127
127
  price?: number | undefined;
128
128
  condition?: "new" | "used" | "refurbished" | "broken" | "like_new" | "good" | "fair" | "poor" | undefined;
129
129
  tags?: string[] | undefined;
130
+ features?: string[] | undefined;
130
131
  images?: string[] | undefined;
131
132
  description?: string | undefined;
132
133
  seoTitle?: string | undefined;
@@ -136,7 +137,6 @@ export declare const productUpdateSchema: z.ZodObject<{
136
137
  stockQuantity?: number | undefined;
137
138
  availableQuantity?: number | undefined;
138
139
  mainImage?: string | undefined;
139
- features?: string[] | undefined;
140
140
  shippingInfo?: string | undefined;
141
141
  returnPolicy?: string | undefined;
142
142
  insurance?: boolean | undefined;
@@ -194,12 +194,12 @@ export declare const auctionInputSchema: z.ZodObject<{
194
194
  startingBid: number;
195
195
  autoExtendable: boolean;
196
196
  condition?: "new" | "used" | "refurbished" | "broken" | "like_new" | "good" | "fair" | "poor" | undefined;
197
+ features?: string[] | undefined;
197
198
  description?: string | undefined;
198
199
  seoTitle?: string | undefined;
199
200
  seoDescription?: string | undefined;
200
201
  seoKeywords?: string[] | undefined;
201
202
  brandSlug?: string | undefined;
202
- features?: string[] | undefined;
203
203
  shippingInfo?: string | undefined;
204
204
  returnPolicy?: string | undefined;
205
205
  insurance?: boolean | undefined;
@@ -223,6 +223,7 @@ export declare const auctionInputSchema: z.ZodObject<{
223
223
  currency?: string | undefined;
224
224
  condition?: "new" | "used" | "refurbished" | "broken" | "like_new" | "good" | "fair" | "poor" | undefined;
225
225
  tags?: string[] | undefined;
226
+ features?: string[] | undefined;
226
227
  images?: string[] | undefined;
227
228
  description?: string | undefined;
228
229
  seoTitle?: string | undefined;
@@ -231,7 +232,6 @@ export declare const auctionInputSchema: z.ZodObject<{
231
232
  brandSlug?: string | undefined;
232
233
  stockQuantity?: number | undefined;
233
234
  availableQuantity?: number | undefined;
234
- features?: string[] | undefined;
235
235
  shippingInfo?: string | undefined;
236
236
  returnPolicy?: string | undefined;
237
237
  insurance?: boolean | undefined;
@@ -292,12 +292,12 @@ export declare const preOrderInputSchema: z.ZodObject<{
292
292
  preOrderDeliveryDate: string;
293
293
  preOrderCancellable: boolean;
294
294
  condition?: "new" | "used" | "refurbished" | "broken" | "like_new" | "good" | "fair" | "poor" | undefined;
295
+ features?: string[] | undefined;
295
296
  description?: string | undefined;
296
297
  seoTitle?: string | undefined;
297
298
  seoDescription?: string | undefined;
298
299
  seoKeywords?: string[] | undefined;
299
300
  brandSlug?: string | undefined;
300
- features?: string[] | undefined;
301
301
  shippingInfo?: string | undefined;
302
302
  returnPolicy?: string | undefined;
303
303
  insurance?: boolean | undefined;
@@ -318,6 +318,7 @@ export declare const preOrderInputSchema: z.ZodObject<{
318
318
  condition?: "new" | "used" | "refurbished" | "broken" | "like_new" | "good" | "fair" | "poor" | undefined;
319
319
  tags?: string[] | undefined;
320
320
  preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship" | undefined;
321
+ features?: string[] | undefined;
321
322
  images?: string[] | undefined;
322
323
  description?: string | undefined;
323
324
  seoTitle?: string | undefined;
@@ -326,7 +327,6 @@ export declare const preOrderInputSchema: z.ZodObject<{
326
327
  brandSlug?: string | undefined;
327
328
  stockQuantity?: number | undefined;
328
329
  availableQuantity?: number | undefined;
329
- features?: string[] | undefined;
330
330
  shippingInfo?: string | undefined;
331
331
  returnPolicy?: string | undefined;
332
332
  insurance?: boolean | undefined;
@@ -407,7 +407,9 @@ export declare const CATEGORY_FIELDS: {
407
407
  readonly DISPLAY_ORDER: "displayOrder";
408
408
  readonly IS_ACTIVE: "isActive";
409
409
  readonly CATEGORY_TYPE: "categoryType";
410
+ readonly BUNDLE_KIND: "bundleKind";
410
411
  readonly BUNDLE_PRODUCT_IDS: "bundleProductIds";
412
+ readonly BUNDLE_ITEM_DETAILS: "bundleItemDetails";
411
413
  readonly BUNDLE_STOCK_STATUS: "bundleStockStatus";
412
414
  readonly BUNDLE_QUERY_RESOLVED_AT: "bundleQueryResolvedAt";
413
415
  readonly DISPLAY: "display";
@@ -766,11 +768,16 @@ export declare const SITE_SETTINGS_FIELDS: {
766
768
  };
767
769
  readonly COMMISSIONS: "commissions";
768
770
  readonly COMMISSION_FIELDS: {
769
- readonly RAZORPAY_FEE_PERCENT: "commissions.razorpayFeePercent";
771
+ readonly PLATFORM_FEE_PERCENT: "commissions.platformFeePercent";
772
+ readonly GST_PERCENT: "commissions.gstPercent";
773
+ readonly MINIMUM_TRANSACTION_FEE: "commissions.minimumTransactionFee";
774
+ readonly GATEWAY_FEE_PERCENT: "commissions.gatewayFeePercent";
770
775
  readonly COD_DEPOSIT_PERCENT: "commissions.codDepositPercent";
771
776
  readonly SELLER_SHIPPING_FIXED: "commissions.sellerShippingFixed";
772
777
  readonly PLATFORM_SHIPPING_PERCENT: "commissions.platformShippingPercent";
773
778
  readonly PLATFORM_SHIPPING_FIXED_MIN: "commissions.platformShippingFixedMin";
779
+ readonly PAYOUT_HOLD_DAYS: "commissions.payoutHoldDays";
780
+ readonly MIN_PAYOUT_AMOUNT: "commissions.minPayoutAmount";
774
781
  };
775
782
  readonly SOCIAL_LINKS: "socialLinks";
776
783
  readonly EMAIL_SETTINGS: "emailSettings";
@@ -448,8 +448,10 @@ export const CATEGORY_FIELDS = {
448
448
  IS_ACTIVE: "isActive",
449
449
  // SB-UNI — categoryType discriminator (category/brand/sublisting/bundle)
450
450
  CATEGORY_TYPE: "categoryType",
451
- // Bundle-specific fields (SB3)
451
+ // Bundle-specific fields (SB3 + bundle-eligibility rules)
452
+ BUNDLE_KIND: "bundleKind",
452
453
  BUNDLE_PRODUCT_IDS: "bundleProductIds",
454
+ BUNDLE_ITEM_DETAILS: "bundleItemDetails",
453
455
  BUNDLE_STOCK_STATUS: "bundleStockStatus",
454
456
  BUNDLE_QUERY_RESOLVED_AT: "bundleQueryResolvedAt",
455
457
  DISPLAY: "display",
@@ -862,11 +864,16 @@ export const SITE_SETTINGS_FIELDS = {
862
864
  },
863
865
  COMMISSIONS: "commissions",
864
866
  COMMISSION_FIELDS: {
865
- RAZORPAY_FEE_PERCENT: "commissions.razorpayFeePercent",
867
+ PLATFORM_FEE_PERCENT: "commissions.platformFeePercent",
868
+ GST_PERCENT: "commissions.gstPercent",
869
+ MINIMUM_TRANSACTION_FEE: "commissions.minimumTransactionFee",
870
+ GATEWAY_FEE_PERCENT: "commissions.gatewayFeePercent",
866
871
  COD_DEPOSIT_PERCENT: "commissions.codDepositPercent",
867
872
  SELLER_SHIPPING_FIXED: "commissions.sellerShippingFixed",
868
873
  PLATFORM_SHIPPING_PERCENT: "commissions.platformShippingPercent",
869
874
  PLATFORM_SHIPPING_FIXED_MIN: "commissions.platformShippingFixedMin",
875
+ PAYOUT_HOLD_DAYS: "commissions.payoutHoldDays",
876
+ MIN_PAYOUT_AMOUNT: "commissions.minPayoutAmount",
870
877
  },
871
878
  SOCIAL_LINKS: "socialLinks",
872
879
  EMAIL_SETTINGS: "emailSettings",
@@ -38,6 +38,9 @@ export declare const TABLE_KEYS: {
38
38
  readonly CATEGORY_SLUG: "categorySlug";
39
39
  readonly SHOW_UNREAD: "showUnread";
40
40
  readonly TYPE: "type";
41
+ readonly IS_PART_OF_BUNDLE: "isPartOfBundle";
42
+ readonly SUBLISTING_CATEGORY: "sublistingCategory";
43
+ readonly FEATURES: "features";
41
44
  };
42
45
  export type TableKey = (typeof TABLE_KEYS)[keyof typeof TABLE_KEYS];
43
46
  export declare const VIEW_MODE: {
@@ -38,6 +38,9 @@ export const TABLE_KEYS = {
38
38
  CATEGORY_SLUG: "categorySlug",
39
39
  SHOW_UNREAD: "showUnread",
40
40
  TYPE: "type",
41
+ IS_PART_OF_BUNDLE: "isPartOfBundle",
42
+ SUBLISTING_CATEGORY: "sublistingCategory",
43
+ FEATURES: "features",
41
44
  };
42
45
  export const VIEW_MODE = {
43
46
  GRID: "grid",
@@ -70,10 +70,11 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
70
70
  const [watermarkImageUrl, setWatermarkImageUrl] = React.useState("");
71
71
  const [watermarkSize, setWatermarkSize] = React.useState(30);
72
72
  const [watermarkOpacity, setWatermarkOpacity] = React.useState(20);
73
- // ⑦ Fees
74
- const [platformCommission, setPlatformCommission] = React.useState(5);
75
- const [buyerFee, setBuyerFee] = React.useState(2);
76
- const [razorpayFee, setRazorpayFee] = React.useState(2.36);
73
+ // ⑦ Fees — all read/written under commissions key
74
+ const [platformFeePercent, setPlatformFeePercent] = React.useState(5);
75
+ const [gstPercent, setGstPercent] = React.useState(18);
76
+ const [minimumTransactionFee, setMinimumTransactionFee] = React.useState(0);
77
+ const [gatewayFeePercent, setGatewayFeePercent] = React.useState(2);
77
78
  const [payoutHoldDays, setPayoutHoldDays] = React.useState(7);
78
79
  const [minPayoutAmount, setMinPayoutAmount] = React.useState(100);
79
80
  const [auctionListingFee, setAuctionListingFee] = React.useState(0);
@@ -172,15 +173,16 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
172
173
  setWatermarkImageUrl(s.watermark?.imageUrl ?? "");
173
174
  setWatermarkSize(s.watermark?.size ?? 30);
174
175
  setWatermarkOpacity(s.watermark?.opacity ?? 20);
175
- setPlatformCommission(s.fees?.platformCommission ?? 5);
176
- setBuyerFee(s.fees?.buyerFee ?? 2);
177
- setRazorpayFee(s.fees?.razorpayFeePercent ?? 2.36);
178
- setPayoutHoldDays(s.fees?.payoutHoldDays ?? 7);
179
- setMinPayoutAmount(s.fees?.minPayoutAmount ?? 100);
180
- setAuctionListingFee(s.fees?.auctionListingFee ?? 0);
181
- setPreOrderListingFee(s.fees?.preOrderListingFee ?? 0);
182
- setFeaturedSlotFee(s.fees?.featuredSlotFee ?? 999);
183
- setPromotedSlotFee(s.fees?.promotedSlotFee ?? 499);
176
+ setPlatformFeePercent(s.commissions?.platformFeePercent ?? 5);
177
+ setGstPercent(s.commissions?.gstPercent ?? 18);
178
+ setMinimumTransactionFee(s.commissions?.minimumTransactionFee ?? 0);
179
+ setGatewayFeePercent(s.commissions?.gatewayFeePercent ?? 2);
180
+ setPayoutHoldDays(s.commissions?.payoutHoldDays ?? 7);
181
+ setMinPayoutAmount(s.commissions?.minPayoutAmount ?? 100);
182
+ setAuctionListingFee(s.commissions?.auctionListingFee ?? 0);
183
+ setPreOrderListingFee(s.commissions?.preOrderListingFee ?? 0);
184
+ setFeaturedSlotFee(s.commissions?.featuredSlotFee ?? 999);
185
+ setPromotedSlotFee(s.commissions?.promotedSlotFee ?? 499);
184
186
  setRazorpayKeyId(s.credentialsMasked?.razorpayKeyId ?? "");
185
187
  setRazorpaySecret(s.credentialsMasked?.razorpaySecret ?? "");
186
188
  setShiprocketToken(s.credentialsMasked?.shiprocketToken ?? "");
@@ -259,7 +261,7 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
259
261
  watermark: { type: watermarkType, text: watermarkText, imageUrl: watermarkImageUrl, size: watermarkSize, opacity: watermarkOpacity },
260
262
  }));
261
263
  const feesMutation = useSave("Fees", () => ({
262
- fees: { platformCommission, buyerFee, razorpayFeePercent: razorpayFee, payoutHoldDays, minPayoutAmount, auctionListingFee, preOrderListingFee, featuredSlotFee, promotedSlotFee },
264
+ commissions: { platformFeePercent, gstPercent, minimumTransactionFee, gatewayFeePercent, payoutHoldDays, minPayoutAmount, auctionListingFee, preOrderListingFee, featuredSlotFee, promotedSlotFee },
263
265
  }));
264
266
  const integrationsMutation = useSave("Integrations", () => ({
265
267
  credentials: {
@@ -328,7 +330,7 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
328
330
  ["limits", "⑪ Limits"],
329
331
  ["legal", "⑫ Legal"],
330
332
  ["whatsapp", "⑬ WhatsApp"],
331
- ].map(([value, label]) => (_jsx(TabsTrigger, { value: value, children: label }, value))) }), _jsx(TabsContent, { value: "about", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); aboutMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Override the About page hero and mission text. Leave blank to use the platform defaults." }), _jsx(Input, { label: "Hero title", value: aboutTitle, onChange: (e) => setAboutTitle(e.target.value), placeholder: "About LetItRip" }), _jsx(Input, { label: "Hero subtitle", value: aboutSubtitle, onChange: (e) => setAboutSubtitle(e.target.value), placeholder: "Connecting buyers, sellers, and bidders in one vibrant marketplace" }), _jsx(Input, { label: "Mission section title", value: aboutMissionTitle, onChange: (e) => setAboutMissionTitle(e.target.value), placeholder: "Our Mission" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Mission text" }), _jsx("textarea", { value: aboutMissionText, onChange: (e) => setAboutMissionText(e.target.value), placeholder: "LetItRip was built to democratise commerce\u2026", rows: 4, className: "w-full rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 p-3 text-sm text-zinc-800 dark:text-zinc-200 resize-y focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsx(Input, { label: "CTA banner title", value: aboutCtaTitle, onChange: (e) => setAboutCtaTitle(e.target.value), placeholder: "Ready to get started?" }), _jsx(GroupSaveButton, { isPending: aboutMutation.isPending })] }) }), _jsx(TabsContent, { value: "branding", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); brandingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Site name", value: siteName, onChange: (e) => setSiteName(e.target.value), placeholder: "LetItRip" }), _jsx(Input, { label: "Tagline", value: tagline, onChange: (e) => setTagline(e.target.value), placeholder: "India's Largest Collectibles Marketplace" }), _jsx(ImageUpload, { label: "Logo", currentImage: logoUrl, onUpload: (file) => upload(file, "store"), onChange: setLogoUrl }), _jsx(ImageUpload, { label: "Favicon", currentImage: faviconUrl, onUpload: (file) => upload(file, "store"), onChange: setFaviconUrl }), _jsxs("div", { className: "space-y-3 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4", children: [_jsx(Toggle, { label: "Maintenance mode", checked: maintenanceMode, onChange: setMaintenanceMode }), _jsx(Input, { label: "Maintenance message", value: maintenanceMessage, onChange: (e) => setMaintenanceMessage(e.target.value), placeholder: "We're back soon.", disabled: !maintenanceMode })] }), _jsx(GroupSaveButton, { isPending: brandingMutation.isPending })] }) }), _jsx(TabsContent, { value: "appearance", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); appearanceMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Primary color" }), _jsx("input", { type: "color", value: primaryColor || "#000000", onChange: (e) => setPrimaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Secondary color" }), _jsx("input", { type: "color", value: secondaryColor || "#000000", onChange: (e) => setSecondaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Accent color" }), _jsx("input", { type: "color", value: accentColor || "#000000", onChange: (e) => setAccentColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Select, { label: "Default theme", options: THEME_OPTIONS, value: defaultTheme, onValueChange: setDefaultTheme }), _jsx(Select, { label: "Font family", options: FONT_OPTIONS, value: fontFamily, onValueChange: setFontFamily })] }), _jsx(GroupSaveButton, { isPending: appearanceMutation.isPending })] }) }), _jsx(TabsContent, { value: "announcement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); announcementMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Toggle, { label: "Show announcement bar", checked: announcementEnabled, onChange: setAnnouncementEnabled }), _jsx(Input, { label: "Announcement text", value: announcementText, onChange: (e) => setAnnouncementText(e.target.value), placeholder: "\uD83C\uDF89 Free shipping on orders \u20B9999+", disabled: !announcementEnabled }), _jsx(Input, { label: "Link URL (optional)", value: announcementLink, onChange: (e) => setAnnouncementLink(e.target.value), placeholder: "/products", disabled: !announcementEnabled }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Background color" }), _jsx("input", { type: "color", value: announcementBg || "#1d4ed8", onChange: (e) => setAnnouncementBg(e.target.value), className: "h-10 w-32 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer", disabled: !announcementEnabled })] }), _jsx(GroupSaveButton, { isPending: announcementMutation.isPending })] }) }), _jsx(TabsContent, { value: "seo", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); seoMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Default meta title", value: seoTitle, onChange: (e) => setSeoTitle(e.target.value), placeholder: "LetItRip \u2014 Buy, Sell & Auction Collectibles in India", maxLength: 60, helperText: "Max 60 chars. Use {page} token for dynamic insertion." }), _jsx(Input, { label: "Default meta description", value: seoDescription, onChange: (e) => setSeoDescription(e.target.value), placeholder: "India's largest collectibles marketplace\u2026", maxLength: 160, helperText: "Max 160 chars." }), _jsx(ImageUpload, { label: "Default OG image", currentImage: seoOgImage, onUpload: (file) => upload(file, "store"), onChange: setSeoOgImage }), _jsx(Input, { label: "Canonical base URL", value: canonicalUrl, onChange: (e) => setCanonicalUrl(e.target.value), placeholder: "https://letitrip.in" }), _jsx(Toggle, { label: "Robots noindex (disables search indexing \u2014 use carefully)", checked: seoNoIndex, onChange: setSeoNoIndex }), _jsx(GroupSaveButton, { isPending: seoMutation.isPending })] }) }), _jsx(TabsContent, { value: "contact", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); contactMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Support email", value: supportEmail, onChange: (e) => setSupportEmail(e.target.value), type: "email", placeholder: "support@letitrip.in" }), _jsx(Input, { label: "Support phone", value: supportPhone, onChange: (e) => setSupportPhone(e.target.value), placeholder: "+91 XXXXX XXXXX" })] }), _jsx(Input, { label: "Physical address", value: supportAddress, onChange: (e) => setSupportAddress(e.target.value), placeholder: "Mumbai, Maharashtra, India" }), _jsx(Input, { label: "Support hours", value: supportHours, onChange: (e) => setSupportHours(e.target.value), placeholder: "Mon\u2013Fri, 10 AM \u2013 6 PM IST" }), _jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400 pt-2", children: "Social links" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Instagram URL", value: instagram, onChange: (e) => setInstagram(e.target.value), placeholder: "https://instagram.com/letitrip" }), _jsx(Input, { label: "Twitter / X URL", value: twitter, onChange: (e) => setTwitter(e.target.value), placeholder: "https://twitter.com/letitrip" }), _jsx(Input, { label: "Facebook URL", value: facebook, onChange: (e) => setFacebook(e.target.value), placeholder: "https://facebook.com/letitrip" }), _jsx(Input, { label: "YouTube URL", value: youtube, onChange: (e) => setYoutube(e.target.value), placeholder: "https://youtube.com/@letitrip" }), _jsx(Input, { label: "WhatsApp number", value: whatsapp, onChange: (e) => setWhatsapp(e.target.value), placeholder: "+91XXXXXXXXXX" }), _jsx(Input, { label: "LinkedIn URL", value: linkedin, onChange: (e) => setLinkedin(e.target.value), placeholder: "https://linkedin.com/company/letitrip" }), _jsx(Input, { label: "Pinterest URL", value: pinterest, onChange: (e) => setPinterest(e.target.value), placeholder: "https://pinterest.com/letitrip" })] }), _jsx(GroupSaveButton, { isPending: contactMutation.isPending })] }) }), _jsx(TabsContent, { value: "watermark", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); watermarkMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Select, { label: "Watermark type", options: [{ label: "Text", value: "text" }, { label: "Image", value: "image" }], value: watermarkType, onValueChange: (v) => setWatermarkType(v) }), watermarkType === "text" ? (_jsx(Input, { label: "Watermark text", value: watermarkText, onChange: (e) => setWatermarkText(e.target.value), placeholder: "letitrip.in" })) : (_jsx(ImageUpload, { label: "Watermark image", currentImage: watermarkImageUrl, onUpload: (file) => upload(file, "store"), onChange: setWatermarkImageUrl })), _jsx(Slider, { label: `Size — ${watermarkSize}% of image width`, value: watermarkSize, onChange: setWatermarkSize, min: 5, max: 100, step: 5 }), _jsx(Slider, { label: `Opacity — ${watermarkOpacity}%`, value: watermarkOpacity, onChange: setWatermarkOpacity, min: 5, max: 100, step: 5 }), _jsxs("div", { className: "rounded-lg border border-zinc-200 dark:border-zinc-700 p-4 bg-zinc-50 dark:bg-zinc-800", children: [_jsx("p", { className: "text-xs text-zinc-500 mb-2", children: "Preview (text watermark only)" }), _jsx("div", { className: "relative bg-white dark:bg-zinc-900 rounded h-32 flex items-end justify-end overflow-hidden", children: _jsx("span", { className: "text-zinc-400 select-none p-2 font-medium", style: { fontSize: `${Math.max(10, watermarkSize / 5)}px`, opacity: watermarkOpacity / 100 }, children: watermarkText }) })] }), _jsx(GroupSaveButton, { isPending: watermarkMutation.isPending })] }) }), _jsx(TabsContent, { value: "fees", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); feesMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Platform commission (%)", value: String(platformCommission), onChange: (e) => setPlatformCommission(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "Buyer convenience fee (%)", value: String(buyerFee), onChange: (e) => setBuyerFee(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "Razorpay gateway fee (%)", value: String(razorpayFee), onChange: (e) => setRazorpayFee(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 10, step: 0.01 }), _jsx(Input, { label: "Seller payout hold (days)", value: String(payoutHoldDays), onChange: (e) => setPayoutHoldDays(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Minimum payout amount (\u20B9)", value: String(minPayoutAmount), onChange: (e) => setMinPayoutAmount(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Auction listing fee (\u20B9)", value: String(auctionListingFee), onChange: (e) => setAuctionListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Pre-order listing fee (\u20B9)", value: String(preOrderListingFee), onChange: (e) => setPreOrderListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Featured slot fee (\u20B9)", value: String(featuredSlotFee), onChange: (e) => setFeaturedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Promoted slot fee (\u20B9)", value: String(promotedSlotFee), onChange: (e) => setPromotedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 })] }), _jsx(GroupSaveButton, { isPending: feesMutation.isPending })] }) }), _jsx(TabsContent, { value: "integrations", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); integrationsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Keys are masked in transit and stored encrypted. Click Reveal to view." }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Razorpay" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(MaskedInput, { label: "Razorpay Key ID", value: razorpayKeyId, onChange: setRazorpayKeyId, placeholder: "rzp_live_\u2026" }), _jsx(MaskedInput, { label: "Razorpay Secret", value: razorpaySecret, onChange: setRazorpaySecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Shiprocket" }), _jsx(MaskedInput, { label: "Shiprocket API token", value: shiprocketToken, onChange: setShiprocketToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "SMTP / Email" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "SMTP host", value: smtpHost, onChange: (e) => setSmtpHost(e.target.value), placeholder: "smtp.sendgrid.net" }), _jsx(Input, { label: "SMTP port", value: smtpPort, onChange: (e) => setSmtpPort(e.target.value), type: "number", placeholder: "587" }), _jsx(Input, { label: "SMTP user", value: smtpUser, onChange: (e) => setSmtpUser(e.target.value), placeholder: "apikey" }), _jsx(MaskedInput, { label: "SMTP password", value: smtpPassword, onChange: setSmtpPassword, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsx(Input, { label: "From address", value: smtpFrom, onChange: (e) => setSmtpFrom(e.target.value), placeholder: "noreply@letitrip.in", type: "email" })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Analytics & Tracking" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Google Analytics ID", value: gaMeasurementId, onChange: (e) => setGaMeasurementId(e.target.value), placeholder: "G-XXXXXXXXXX" }), _jsx(Input, { label: "Facebook Pixel ID", value: fbPixelId, onChange: (e) => setFbPixelId(e.target.value), placeholder: "XXXXXXXXXXXXXXXX" }), _jsx(Input, { label: "GTM Container ID", value: gtmContainerId, onChange: (e) => setGtmContainerId(e.target.value), placeholder: "GTM-XXXXXXX" })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Meta (Instagram & Facebook Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Used by the Social Feed section to fetch Instagram and Facebook posts via Meta Graph API v19." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(MaskedInput, { label: "Page Access Token", value: metaPageAccessToken, onChange: setMetaPageAccessToken, placeholder: "EAAxxxxxxx\u2026" }), _jsx(Input, { label: "Page ID (or handle)", value: metaPageId, onChange: (e) => setMetaPageId(e.target.value), placeholder: "letitrip" })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "TikTok for Developers (Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Client credentials + long-lived access token from TikTok for Developers. Used to list your account's public videos." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(MaskedInput, { label: "Client Key", value: tiktokClientKey, onChange: setTiktokClientKey, placeholder: "aw\u2026" }), _jsx(MaskedInput, { label: "Client Secret", value: tiktokClientSecret, onChange: setTiktokClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" }), _jsx(MaskedInput, { label: "Access Token (long-lived)", value: tiktokAccessToken, onChange: setTiktokAccessToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "DeviantArt OAuth2 (Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Client credentials for DeviantArt gallery fetching (client-credentials OAuth2 flow \u2014 no user login required)." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(MaskedInput, { label: "Client ID", value: deviantartClientId, onChange: setDeviantartClientId, placeholder: "1234" }), _jsx(MaskedInput, { label: "Client Secret", value: deviantartClientSecret, onChange: setDeviantartClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsx(GroupSaveButton, { isPending: integrationsMutation.isPending })] }) }), _jsx(TabsContent, { value: "shipping", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); shippingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Free shipping threshold (\u20B9)", value: String(freeShippingThreshold), onChange: (e) => setFreeShippingThreshold(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Orders above this amount get free shipping." }), _jsx(Toggle, { label: "Cash on delivery (COD) enabled", checked: codEnabled, onChange: setCodEnabled }), _jsx(Select, { label: "Default carrier", options: CARRIER_OPTIONS, value: defaultCarrier, onValueChange: setDefaultCarrier }), _jsx(Input, { label: "Max delivery radius (km, 0 = no limit)", value: String(maxDeliveryRadius), onChange: (e) => setMaxDeliveryRadius(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(GroupSaveButton, { isPending: shippingMutation.isPending })] }) }), _jsx(TabsContent, { value: "auction", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); auctionMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Minimum bid increment (\u20B9)", value: String(minBidIncrement), onChange: (e) => setMinBidIncrement(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Global default \u2014 individual auctions may override." }), _jsx(Input, { label: "Auto-extend window (minutes before end)", value: String(autoExtendWindow), onChange: (e) => setAutoExtendWindow(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Extend auction end time if a bid arrives within this window." }), _jsx(Input, { label: "Settlement grace period (hours)", value: String(settlementGrace), onChange: (e) => setSettlementGrace(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Time winner has to pay before the auction is re-listed." }), _jsx(GroupSaveButton, { isPending: auctionMutation.isPending })] }) }), _jsx(TabsContent, { value: "limits", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); limitsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Max products per store", value: String(maxProductsPerStore), onChange: (e) => setMaxProductsPerStore(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max images per product", value: String(maxImagesPerProduct), onChange: (e) => setMaxImagesPerProduct(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max video size (MB)", value: String(maxVideoSizeMb), onChange: (e) => setMaxVideoSizeMb(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max custom fields per product", value: String(maxCustomFields), onChange: (e) => setMaxCustomFields(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Max custom sections per product", value: String(maxCustomSections), onChange: (e) => setMaxCustomSections(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Order cancellation window (hours)", value: String(orderCancelWindow), onChange: (e) => setOrderCancelWindow(parseInt(e.target.value) || 0), type: "number", min: 0 })] }), _jsx(GroupSaveButton, { isPending: limitsMutation.isPending })] }) }), _jsx(TabsContent, { value: "whatsapp", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); whatsappMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Platform-level WhatsApp Business Cloud API credentials. Used for automated purchase announcements to admin numbers when orders are placed. Store owners configure their own credentials in Store \u2192 WhatsApp." }), _jsx(Input, { label: "Phone Number ID", value: waPhoneNumberId, onChange: (e) => setWaPhoneNumberId(e.target.value), placeholder: "987654321098765", helperText: "From Meta for Developers \u2192 App \u2192 WhatsApp \u2192 API Setup" }), _jsx(MaskedInput, { label: "Cloud API System User Token", value: waCloudApiToken, onChange: setWaCloudApiToken, placeholder: "EAAxxxxxxxx\u2026", helperText: "Long-lived system user access token with WhatsApp Business permissions" }), _jsx(Input, { label: "Admin Notify Numbers", value: waAdminNotifyNumbers, onChange: (e) => setWaAdminNotifyNumbers(e.target.value), placeholder: "919876543210,918765432109", helperText: "Comma-separated, digits-only, include country code. These receive a WhatsApp message when any order is placed." }), _jsx(GroupSaveButton, { isPending: whatsappMutation.isPending })] }) }), _jsx(TabsContent, { value: "legal", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); legalMutation.mutate(); }, className: "space-y-5 pt-4", children: [[
333
+ ].map(([value, label]) => (_jsx(TabsTrigger, { value: value, children: label }, value))) }), _jsx(TabsContent, { value: "about", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); aboutMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Override the About page hero and mission text. Leave blank to use the platform defaults." }), _jsx(Input, { label: "Hero title", value: aboutTitle, onChange: (e) => setAboutTitle(e.target.value), placeholder: "About LetItRip" }), _jsx(Input, { label: "Hero subtitle", value: aboutSubtitle, onChange: (e) => setAboutSubtitle(e.target.value), placeholder: "Connecting buyers, sellers, and bidders in one vibrant marketplace" }), _jsx(Input, { label: "Mission section title", value: aboutMissionTitle, onChange: (e) => setAboutMissionTitle(e.target.value), placeholder: "Our Mission" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Mission text" }), _jsx("textarea", { value: aboutMissionText, onChange: (e) => setAboutMissionText(e.target.value), placeholder: "LetItRip was built to democratise commerce\u2026", rows: 4, className: "w-full rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 p-3 text-sm text-zinc-800 dark:text-zinc-200 resize-y focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsx(Input, { label: "CTA banner title", value: aboutCtaTitle, onChange: (e) => setAboutCtaTitle(e.target.value), placeholder: "Ready to get started?" }), _jsx(GroupSaveButton, { isPending: aboutMutation.isPending })] }) }), _jsx(TabsContent, { value: "branding", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); brandingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Site name", value: siteName, onChange: (e) => setSiteName(e.target.value), placeholder: "LetItRip" }), _jsx(Input, { label: "Tagline", value: tagline, onChange: (e) => setTagline(e.target.value), placeholder: "India's Largest Collectibles Marketplace" }), _jsx(ImageUpload, { label: "Logo", currentImage: logoUrl, onUpload: (file) => upload(file, "store"), onChange: setLogoUrl }), _jsx(ImageUpload, { label: "Favicon", currentImage: faviconUrl, onUpload: (file) => upload(file, "store"), onChange: setFaviconUrl }), _jsxs("div", { className: "space-y-3 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4", children: [_jsx(Toggle, { label: "Maintenance mode", checked: maintenanceMode, onChange: setMaintenanceMode }), _jsx(Input, { label: "Maintenance message", value: maintenanceMessage, onChange: (e) => setMaintenanceMessage(e.target.value), placeholder: "We're back soon.", disabled: !maintenanceMode })] }), _jsx(GroupSaveButton, { isPending: brandingMutation.isPending })] }) }), _jsx(TabsContent, { value: "appearance", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); appearanceMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Primary color" }), _jsx("input", { type: "color", value: primaryColor || "#000000", onChange: (e) => setPrimaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Secondary color" }), _jsx("input", { type: "color", value: secondaryColor || "#000000", onChange: (e) => setSecondaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Accent color" }), _jsx("input", { type: "color", value: accentColor || "#000000", onChange: (e) => setAccentColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Select, { label: "Default theme", options: THEME_OPTIONS, value: defaultTheme, onValueChange: setDefaultTheme }), _jsx(Select, { label: "Font family", options: FONT_OPTIONS, value: fontFamily, onValueChange: setFontFamily })] }), _jsx(GroupSaveButton, { isPending: appearanceMutation.isPending })] }) }), _jsx(TabsContent, { value: "announcement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); announcementMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Toggle, { label: "Show announcement bar", checked: announcementEnabled, onChange: setAnnouncementEnabled }), _jsx(Input, { label: "Announcement text", value: announcementText, onChange: (e) => setAnnouncementText(e.target.value), placeholder: "\uD83C\uDF89 Free shipping on orders \u20B9999+", disabled: !announcementEnabled }), _jsx(Input, { label: "Link URL (optional)", value: announcementLink, onChange: (e) => setAnnouncementLink(e.target.value), placeholder: "/products", disabled: !announcementEnabled }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Background color" }), _jsx("input", { type: "color", value: announcementBg || "#1d4ed8", onChange: (e) => setAnnouncementBg(e.target.value), className: "h-10 w-32 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer", disabled: !announcementEnabled })] }), _jsx(GroupSaveButton, { isPending: announcementMutation.isPending })] }) }), _jsx(TabsContent, { value: "seo", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); seoMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Default meta title", value: seoTitle, onChange: (e) => setSeoTitle(e.target.value), placeholder: "LetItRip \u2014 Buy, Sell & Auction Collectibles in India", maxLength: 60, helperText: "Max 60 chars. Use {page} token for dynamic insertion." }), _jsx(Input, { label: "Default meta description", value: seoDescription, onChange: (e) => setSeoDescription(e.target.value), placeholder: "India's largest collectibles marketplace\u2026", maxLength: 160, helperText: "Max 160 chars." }), _jsx(ImageUpload, { label: "Default OG image", currentImage: seoOgImage, onUpload: (file) => upload(file, "store"), onChange: setSeoOgImage }), _jsx(Input, { label: "Canonical base URL", value: canonicalUrl, onChange: (e) => setCanonicalUrl(e.target.value), placeholder: "https://letitrip.in" }), _jsx(Toggle, { label: "Robots noindex (disables search indexing \u2014 use carefully)", checked: seoNoIndex, onChange: setSeoNoIndex }), _jsx(GroupSaveButton, { isPending: seoMutation.isPending })] }) }), _jsx(TabsContent, { value: "contact", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); contactMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Support email", value: supportEmail, onChange: (e) => setSupportEmail(e.target.value), type: "email", placeholder: "support@letitrip.in" }), _jsx(Input, { label: "Support phone", value: supportPhone, onChange: (e) => setSupportPhone(e.target.value), placeholder: "+91 XXXXX XXXXX" })] }), _jsx(Input, { label: "Physical address", value: supportAddress, onChange: (e) => setSupportAddress(e.target.value), placeholder: "Mumbai, Maharashtra, India" }), _jsx(Input, { label: "Support hours", value: supportHours, onChange: (e) => setSupportHours(e.target.value), placeholder: "Mon\u2013Fri, 10 AM \u2013 6 PM IST" }), _jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400 pt-2", children: "Social links" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Instagram URL", value: instagram, onChange: (e) => setInstagram(e.target.value), placeholder: "https://instagram.com/letitrip" }), _jsx(Input, { label: "Twitter / X URL", value: twitter, onChange: (e) => setTwitter(e.target.value), placeholder: "https://twitter.com/letitrip" }), _jsx(Input, { label: "Facebook URL", value: facebook, onChange: (e) => setFacebook(e.target.value), placeholder: "https://facebook.com/letitrip" }), _jsx(Input, { label: "YouTube URL", value: youtube, onChange: (e) => setYoutube(e.target.value), placeholder: "https://youtube.com/@letitrip" }), _jsx(Input, { label: "WhatsApp number", value: whatsapp, onChange: (e) => setWhatsapp(e.target.value), placeholder: "+91XXXXXXXXXX" }), _jsx(Input, { label: "LinkedIn URL", value: linkedin, onChange: (e) => setLinkedin(e.target.value), placeholder: "https://linkedin.com/company/letitrip" }), _jsx(Input, { label: "Pinterest URL", value: pinterest, onChange: (e) => setPinterest(e.target.value), placeholder: "https://pinterest.com/letitrip" })] }), _jsx(GroupSaveButton, { isPending: contactMutation.isPending })] }) }), _jsx(TabsContent, { value: "watermark", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); watermarkMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Select, { label: "Watermark type", options: [{ label: "Text", value: "text" }, { label: "Image", value: "image" }], value: watermarkType, onValueChange: (v) => setWatermarkType(v) }), watermarkType === "text" ? (_jsx(Input, { label: "Watermark text", value: watermarkText, onChange: (e) => setWatermarkText(e.target.value), placeholder: "letitrip.in" })) : (_jsx(ImageUpload, { label: "Watermark image", currentImage: watermarkImageUrl, onUpload: (file) => upload(file, "store"), onChange: setWatermarkImageUrl })), _jsx(Slider, { label: `Size — ${watermarkSize}% of image width`, value: watermarkSize, onChange: setWatermarkSize, min: 5, max: 100, step: 5 }), _jsx(Slider, { label: `Opacity — ${watermarkOpacity}%`, value: watermarkOpacity, onChange: setWatermarkOpacity, min: 5, max: 100, step: 5 }), _jsxs("div", { className: "rounded-lg border border-zinc-200 dark:border-zinc-700 p-4 bg-zinc-50 dark:bg-zinc-800", children: [_jsx("p", { className: "text-xs text-zinc-500 mb-2", children: "Preview (text watermark only)" }), _jsx("div", { className: "relative bg-white dark:bg-zinc-900 rounded h-32 flex items-end justify-end overflow-hidden", children: _jsx("span", { className: "text-zinc-400 select-none p-2 font-medium", style: { fontSize: `${Math.max(10, watermarkSize / 5)}px`, opacity: watermarkOpacity / 100 }, children: watermarkText }) })] }), _jsx(GroupSaveButton, { isPending: watermarkMutation.isPending })] }) }), _jsx(TabsContent, { value: "fees", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); feesMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Platform fee \u2014 our cut (%)", helperText: "% charged on order value. Buyer pays this.", value: String(platformFeePercent), onChange: (e) => setPlatformFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "GST on platform fee (%)", helperText: "Applied to our fee only (not full order). Usually 18%.", value: String(gstPercent), onChange: (e) => setGstPercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "Razorpay gateway cost (%)", helperText: "Gateway's own fee \u2014 absorbed by platform, not passed through.", value: String(gatewayFeePercent), onChange: (e) => setGatewayFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 10, step: 0.01 }), _jsx(Input, { label: "Minimum transaction fee (\u20B9)", helperText: "Per-transaction floor. Total charge will never be below base + this.", value: String(minimumTransactionFee), onChange: (e) => setMinimumTransactionFee(parseFloat(e.target.value) || 0), type: "number", min: 0, step: 0.01 }), _jsx(Input, { label: "Seller payout hold (days)", value: String(payoutHoldDays), onChange: (e) => setPayoutHoldDays(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Minimum payout amount (\u20B9)", value: String(minPayoutAmount), onChange: (e) => setMinPayoutAmount(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Auction listing fee (\u20B9)", value: String(auctionListingFee), onChange: (e) => setAuctionListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Pre-order listing fee (\u20B9)", value: String(preOrderListingFee), onChange: (e) => setPreOrderListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Featured slot fee (\u20B9)", value: String(featuredSlotFee), onChange: (e) => setFeaturedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Promoted slot fee (\u20B9)", value: String(promotedSlotFee), onChange: (e) => setPromotedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 })] }), _jsx(GroupSaveButton, { isPending: feesMutation.isPending })] }) }), _jsx(TabsContent, { value: "integrations", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); integrationsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Keys are masked in transit and stored encrypted. Click Reveal to view." }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Razorpay" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(MaskedInput, { label: "Razorpay Key ID", value: razorpayKeyId, onChange: setRazorpayKeyId, placeholder: "rzp_live_\u2026" }), _jsx(MaskedInput, { label: "Razorpay Secret", value: razorpaySecret, onChange: setRazorpaySecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Shiprocket" }), _jsx(MaskedInput, { label: "Shiprocket API token", value: shiprocketToken, onChange: setShiprocketToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "SMTP / Email" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "SMTP host", value: smtpHost, onChange: (e) => setSmtpHost(e.target.value), placeholder: "smtp.sendgrid.net" }), _jsx(Input, { label: "SMTP port", value: smtpPort, onChange: (e) => setSmtpPort(e.target.value), type: "number", placeholder: "587" }), _jsx(Input, { label: "SMTP user", value: smtpUser, onChange: (e) => setSmtpUser(e.target.value), placeholder: "apikey" }), _jsx(MaskedInput, { label: "SMTP password", value: smtpPassword, onChange: setSmtpPassword, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsx(Input, { label: "From address", value: smtpFrom, onChange: (e) => setSmtpFrom(e.target.value), placeholder: "noreply@letitrip.in", type: "email" })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Analytics & Tracking" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Google Analytics ID", value: gaMeasurementId, onChange: (e) => setGaMeasurementId(e.target.value), placeholder: "G-XXXXXXXXXX" }), _jsx(Input, { label: "Facebook Pixel ID", value: fbPixelId, onChange: (e) => setFbPixelId(e.target.value), placeholder: "XXXXXXXXXXXXXXXX" }), _jsx(Input, { label: "GTM Container ID", value: gtmContainerId, onChange: (e) => setGtmContainerId(e.target.value), placeholder: "GTM-XXXXXXX" })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "Meta (Instagram & Facebook Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Used by the Social Feed section to fetch Instagram and Facebook posts via Meta Graph API v19." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(MaskedInput, { label: "Page Access Token", value: metaPageAccessToken, onChange: setMetaPageAccessToken, placeholder: "EAAxxxxxxx\u2026" }), _jsx(Input, { label: "Page ID (or handle)", value: metaPageId, onChange: (e) => setMetaPageId(e.target.value), placeholder: "letitrip" })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "TikTok for Developers (Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Client credentials + long-lived access token from TikTok for Developers. Used to list your account's public videos." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(MaskedInput, { label: "Client Key", value: tiktokClientKey, onChange: setTiktokClientKey, placeholder: "aw\u2026" }), _jsx(MaskedInput, { label: "Client Secret", value: tiktokClientSecret, onChange: setTiktokClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" }), _jsx(MaskedInput, { label: "Access Token (long-lived)", value: tiktokAccessToken, onChange: setTiktokAccessToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400", children: "DeviantArt OAuth2 (Social Feed)" }), _jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Client credentials for DeviantArt gallery fetching (client-credentials OAuth2 flow \u2014 no user login required)." }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(MaskedInput, { label: "Client ID", value: deviantartClientId, onChange: setDeviantartClientId, placeholder: "1234" }), _jsx(MaskedInput, { label: "Client Secret", value: deviantartClientSecret, onChange: setDeviantartClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsx(GroupSaveButton, { isPending: integrationsMutation.isPending })] }) }), _jsx(TabsContent, { value: "shipping", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); shippingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Free shipping threshold (\u20B9)", value: String(freeShippingThreshold), onChange: (e) => setFreeShippingThreshold(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Orders above this amount get free shipping." }), _jsx(Toggle, { label: "Cash on delivery (COD) enabled", checked: codEnabled, onChange: setCodEnabled }), _jsx(Select, { label: "Default carrier", options: CARRIER_OPTIONS, value: defaultCarrier, onValueChange: setDefaultCarrier }), _jsx(Input, { label: "Max delivery radius (km, 0 = no limit)", value: String(maxDeliveryRadius), onChange: (e) => setMaxDeliveryRadius(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(GroupSaveButton, { isPending: shippingMutation.isPending })] }) }), _jsx(TabsContent, { value: "auction", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); auctionMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Minimum bid increment (\u20B9)", value: String(minBidIncrement), onChange: (e) => setMinBidIncrement(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Global default \u2014 individual auctions may override." }), _jsx(Input, { label: "Auto-extend window (minutes before end)", value: String(autoExtendWindow), onChange: (e) => setAutoExtendWindow(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Extend auction end time if a bid arrives within this window." }), _jsx(Input, { label: "Settlement grace period (hours)", value: String(settlementGrace), onChange: (e) => setSettlementGrace(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Time winner has to pay before the auction is re-listed." }), _jsx(GroupSaveButton, { isPending: auctionMutation.isPending })] }) }), _jsx(TabsContent, { value: "limits", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); limitsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Max products per store", value: String(maxProductsPerStore), onChange: (e) => setMaxProductsPerStore(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max images per product", value: String(maxImagesPerProduct), onChange: (e) => setMaxImagesPerProduct(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max video size (MB)", value: String(maxVideoSizeMb), onChange: (e) => setMaxVideoSizeMb(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max custom fields per product", value: String(maxCustomFields), onChange: (e) => setMaxCustomFields(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Max custom sections per product", value: String(maxCustomSections), onChange: (e) => setMaxCustomSections(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Order cancellation window (hours)", value: String(orderCancelWindow), onChange: (e) => setOrderCancelWindow(parseInt(e.target.value) || 0), type: "number", min: 0 })] }), _jsx(GroupSaveButton, { isPending: limitsMutation.isPending })] }) }), _jsx(TabsContent, { value: "whatsapp", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); whatsappMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Platform-level WhatsApp Business Cloud API credentials. Used for automated purchase announcements to admin numbers when orders are placed. Store owners configure their own credentials in Store \u2192 WhatsApp." }), _jsx(Input, { label: "Phone Number ID", value: waPhoneNumberId, onChange: (e) => setWaPhoneNumberId(e.target.value), placeholder: "987654321098765", helperText: "From Meta for Developers \u2192 App \u2192 WhatsApp \u2192 API Setup" }), _jsx(MaskedInput, { label: "Cloud API System User Token", value: waCloudApiToken, onChange: setWaCloudApiToken, placeholder: "EAAxxxxxxxx\u2026", helperText: "Long-lived system user access token with WhatsApp Business permissions" }), _jsx(Input, { label: "Admin Notify Numbers", value: waAdminNotifyNumbers, onChange: (e) => setWaAdminNotifyNumbers(e.target.value), placeholder: "919876543210,918765432109", helperText: "Comma-separated, digits-only, include country code. These receive a WhatsApp message when any order is placed." }), _jsx(GroupSaveButton, { isPending: whatsappMutation.isPending })] }) }), _jsx(TabsContent, { value: "legal", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); legalMutation.mutate(); }, className: "space-y-5 pt-4", children: [[
332
334
  ["Terms of Service", termsHtml, setTermsHtml],
333
335
  ["Privacy Policy", privacyHtml, setPrivacyHtml],
334
336
  ["Refund Policy", refundHtml, setRefundHtml],
@@ -1,21 +1,29 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Div, Text, Grid } from "../../../ui";
3
+ import { TrendingUp, ShoppingBag, Users, Package, Clock, Star } from "lucide-react";
4
+ import { Grid } from "../../../ui";
3
5
  import { formatCurrency } from "../../../utils/number.formatter";
4
6
  import { getDefaultCurrency } from "../../../core/baseline-resolver";
5
- function StatCard({ label, value, sub, color = "default" }) {
6
- const colorClass = {
7
- default: "bg-white dark:bg-slate-900",
8
- blue: "bg-blue-50 dark:bg-blue-900/20",
9
- green: "bg-green-50 dark:bg-green-900/20",
10
- amber: "bg-amber-50 dark:bg-amber-900/20",
11
- red: "bg-red-50 dark:bg-red-900/20",
12
- }[color];
13
- return (_jsxs(Div, { className: `rounded-xl border border-neutral-200 dark:border-slate-700 p-5 ${colorClass}`, children: [_jsx(Text, { className: "text-xs font-medium uppercase tracking-wide text-neutral-500 dark:text-zinc-400", children: label }), _jsx(Text, { className: "mt-2 text-2xl font-bold text-neutral-900 dark:text-zinc-100", children: value }), sub && _jsx(Text, { className: "mt-1 text-xs text-neutral-500 dark:text-zinc-400", children: sub })] }));
7
+ // Brand gradient stop colors (mirrors SiteLogo wordmark gradient)
8
+ const BRAND_FROM = "var(--appkit-color-primary-700, #1343de)";
9
+ const BRAND_MID = "var(--appkit-color-cobalt, #3570fc)";
10
+ const BRAND_TO = "var(--appkit-color-secondary-400, #84e122)";
11
+ function StatCard({ label, value, sub, icon: Icon, gradient, iconColor }) {
12
+ return (_jsxs("div", { className: "relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm hover:shadow-md transition-shadow", children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]", style: { background: gradient }, "aria-hidden": "true" }), _jsxs("div", { className: "px-5 pb-5 pt-6 flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)]", children: label }), _jsx("p", { className: "mt-2 text-2xl font-bold text-[var(--appkit-color-text)] tabular-nums leading-none", children: value }), sub && (_jsx("p", { className: "mt-1.5 text-xs text-[var(--appkit-color-text-muted)]", children: sub }))] }), _jsx("div", { className: "flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center", style: { background: gradient, opacity: 0.92 }, children: _jsx(Icon, { className: "w-5 h-5 text-white drop-shadow-sm" }) })] })] }));
13
+ }
14
+ function SkeletonCard() {
15
+ return (_jsxs("div", { className: "relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden p-5 animate-pulse", children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px] bg-[var(--appkit-color-border)]" }), _jsxs("div", { className: "flex items-start justify-between gap-3 pt-1", children: [_jsxs("div", { className: "flex-1 space-y-2", children: [_jsx("div", { className: "h-2.5 w-20 rounded bg-[var(--appkit-color-border)]" }), _jsx("div", { className: "h-7 w-24 rounded bg-[var(--appkit-color-border)]" })] }), _jsx("div", { className: "w-10 h-10 rounded-lg bg-[var(--appkit-color-border)]" })] })] }));
14
16
  }
15
17
  export function DashboardStatsGrid({ stats, isLoading, labels = {}, }) {
16
18
  if (isLoading) {
17
- return (_jsx(Grid, { cols: "statTiles", children: Array.from({ length: 4 }).map((_, i) => (_jsxs(Div, { className: "animate-pulse rounded-xl border border-neutral-200 dark:border-slate-700 p-5", children: [_jsx(Div, { className: "h-3 w-20 rounded bg-neutral-200 dark:bg-slate-700" }), _jsx(Div, { className: "mt-2 h-8 w-24 rounded bg-neutral-200 dark:bg-slate-700" })] }, i))) }));
19
+ return (_jsx(Grid, { cols: "statTiles", children: Array.from({ length: 4 }).map((_, i) => _jsx(SkeletonCard, {}, i)) }));
18
20
  }
19
21
  const currency = stats.currency ?? getDefaultCurrency();
20
- return (_jsxs(Grid, { cols: "statTiles", children: [_jsx(StatCard, { label: labels.totalOrders ?? "Total Orders", value: stats.totalOrders ?? 0 }), _jsx(StatCard, { label: labels.totalRevenue ?? "Total Revenue", value: formatCurrency(stats.totalRevenue ?? 0, currency), color: "green" }), _jsx(StatCard, { label: labels.totalUsers ?? "Total Users", value: stats.totalUsers ?? 0, color: "blue" }), _jsx(StatCard, { label: labels.totalProducts ?? "Total Products", value: stats.totalProducts ?? 0 }), stats.pendingOrders !== undefined && (_jsx(StatCard, { label: labels.pendingOrders ?? "Pending Orders", value: stats.pendingOrders, color: "amber" })), stats.pendingReviews !== undefined && (_jsx(StatCard, { label: labels.pendingReviews ?? "Pending Reviews", value: stats.pendingReviews, color: "amber" }))] }));
22
+ // Brand gradient (left→right, matches logo wordmark)
23
+ const brandGrad = `linear-gradient(135deg, ${BRAND_FROM} 0%, ${BRAND_MID} 55%, ${BRAND_TO} 100%)`;
24
+ const blueGrad = `linear-gradient(135deg, ${BRAND_FROM} 0%, ${BRAND_MID} 100%)`;
25
+ const greenGrad = `linear-gradient(135deg, ${BRAND_MID} 0%, ${BRAND_TO} 100%)`;
26
+ const amberGrad = "linear-gradient(135deg, #f59e0b 0%, #f97316 100%)";
27
+ const roseGrad = "linear-gradient(135deg, #f43f5e 0%, #e11d48 100%)";
28
+ return (_jsxs(Grid, { cols: "statTiles", children: [_jsx(StatCard, { label: labels.totalOrders ?? "Total Orders", value: stats.totalOrders ?? 0, icon: ShoppingBag, gradient: brandGrad, iconColor: "#fff" }), _jsx(StatCard, { label: labels.totalRevenue ?? "Total Revenue", value: formatCurrency(stats.totalRevenue ?? 0, currency), icon: TrendingUp, gradient: greenGrad, iconColor: "#fff" }), _jsx(StatCard, { label: labels.totalUsers ?? "Total Users", value: stats.totalUsers ?? 0, icon: Users, gradient: blueGrad, iconColor: "#fff" }), _jsx(StatCard, { label: labels.totalProducts ?? "Total Products", value: stats.totalProducts ?? 0, icon: Package, gradient: brandGrad, iconColor: "#fff" }), stats.pendingOrders !== undefined && (_jsx(StatCard, { label: labels.pendingOrders ?? "Pending Orders", value: stats.pendingOrders, icon: Clock, gradient: amberGrad, iconColor: "#fff" })), stats.pendingReviews !== undefined && (_jsx(StatCard, { label: labels.pendingReviews ?? "Pending Reviews", value: stats.pendingReviews, icon: Star, gradient: roseGrad, iconColor: "#fff" }))] }));
21
29
  }
@@ -13,9 +13,25 @@ const XAxis = dynamic(() => import("recharts").then((m) => m.XAxis), { ssr: fals
13
13
  const YAxis = dynamic(() => import("recharts").then((m) => m.YAxis), { ssr: false });
14
14
  const CartesianGrid = dynamic(() => import("recharts").then((m) => m.CartesianGrid), { ssr: false });
15
15
  const Tooltip = dynamic(() => import("recharts").then((m) => m.Tooltip), { ssr: false });
16
+ // Shared chart card wrapper
17
+ function ChartCard({ title, children, className = "", }) {
18
+ return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]", style: {
19
+ background: "linear-gradient(to right, var(--appkit-color-primary-700,#1343de) 0%, var(--appkit-color-cobalt,#3570fc) 55%, var(--appkit-color-secondary-400,#84e122) 100%)",
20
+ }, "aria-hidden": "true" }), _jsxs(Div, { className: "p-4 sm:p-6 pt-5 sm:pt-7", children: [title && (_jsx(Heading, { level: 3, className: "text-sm font-semibold text-[var(--appkit-color-text)] mb-4", children: title })), children] })] }));
21
+ }
16
22
  export function AdminRevenueChart({ data, labels = {}, className = "", }) {
17
- return (_jsxs(Div, { className: `bg-white dark:bg-neutral-900 rounded-xl border border-neutral-200 dark:border-neutral-800 p-4 sm:p-6 ${className}`, children: [labels.title && (_jsx(Heading, { level: 3, className: "text-base font-semibold mb-4", children: labels.title })), _jsx(ResponsiveContainer, { width: "100%", height: 300, children: _jsxs(AreaChart, { data: data, children: [_jsx("defs", { children: _jsxs("linearGradient", { id: "colorRevenue", x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "5%", stopColor: "var(--appkit-color-primary)", stopOpacity: 0.8 }), _jsx("stop", { offset: "95%", stopColor: "var(--appkit-color-primary)", stopOpacity: 0 })] }) }), _jsx(CartesianGrid, { strokeDasharray: "3 3" }), _jsx(XAxis, { dataKey: "month" }), _jsx(YAxis, {}), _jsx(Tooltip, {}), _jsx(Area, { type: "monotone", dataKey: "revenue", stroke: "var(--appkit-color-primary)", fillOpacity: 1, fill: "url(#colorRevenue)" })] }) })] }));
23
+ return (_jsx(ChartCard, { title: labels.title, className: className, children: _jsx(ResponsiveContainer, { width: "100%", height: 280, children: _jsxs(AreaChart, { data: data, margin: { top: 4, right: 4, left: 0, bottom: 0 }, children: [_jsxs("defs", { children: [_jsxs("linearGradient", { id: "revenueGrad", x1: "0", y1: "0", x2: "1", y2: "0", children: [_jsx("stop", { offset: "0%", stopColor: "#1343de" }), _jsx("stop", { offset: "55%", stopColor: "#3570fc" }), _jsx("stop", { offset: "100%", stopColor: "#84e122" })] }), _jsxs("linearGradient", { id: "revenueAreaGrad", x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "5%", stopColor: "#3570fc", stopOpacity: 0.25 }), _jsx("stop", { offset: "95%", stopColor: "#3570fc", stopOpacity: 0 })] })] }), _jsx(CartesianGrid, { strokeDasharray: "4 4", stroke: "var(--appkit-color-border)", vertical: false }), _jsx(XAxis, { dataKey: "month", tick: { fontSize: 11, fill: "var(--appkit-color-text-muted)" }, axisLine: false, tickLine: false }), _jsx(YAxis, { tick: { fontSize: 11, fill: "var(--appkit-color-text-muted)" }, axisLine: false, tickLine: false, width: 48 }), _jsx(Tooltip, { contentStyle: {
24
+ background: "var(--appkit-color-surface)",
25
+ border: "1px solid var(--appkit-color-border)",
26
+ borderRadius: "8px",
27
+ fontSize: 12,
28
+ }, cursor: { stroke: "var(--appkit-color-border)", strokeWidth: 1 } }), _jsx(Area, { type: "monotone", dataKey: "revenue", stroke: "url(#revenueGrad)", strokeWidth: 2.5, fillOpacity: 1, fill: "url(#revenueAreaGrad)", dot: false, activeDot: { r: 4, fill: "#3570fc" } })] }) }) }));
18
29
  }
19
30
  export function AdminOrdersChart({ data, labels = {}, className = "", }) {
20
- return (_jsxs(Div, { className: `bg-white dark:bg-neutral-900 rounded-xl border border-neutral-200 dark:border-neutral-800 p-4 sm:p-6 ${className}`, children: [labels.title && (_jsx(Heading, { level: 3, className: "text-base font-semibold mb-4", children: labels.title })), _jsx(ResponsiveContainer, { width: "100%", height: 300, children: _jsxs(BarChart, { data: data, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3" }), _jsx(XAxis, { dataKey: "month" }), _jsx(YAxis, {}), _jsx(Tooltip, {}), _jsx(Bar, { dataKey: "orders", fill: "var(--appkit-color-secondary)" })] }) })] }));
31
+ return (_jsx(ChartCard, { title: labels.title, className: className, children: _jsx(ResponsiveContainer, { width: "100%", height: 280, children: _jsxs(BarChart, { data: data, margin: { top: 4, right: 4, left: 0, bottom: 0 }, barSize: 20, children: [_jsx("defs", { children: _jsxs("linearGradient", { id: "ordersBarGrad", x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: "#3570fc", stopOpacity: 0.9 }), _jsx("stop", { offset: "100%", stopColor: "#84e122", stopOpacity: 0.7 })] }) }), _jsx(CartesianGrid, { strokeDasharray: "4 4", stroke: "var(--appkit-color-border)", vertical: false }), _jsx(XAxis, { dataKey: "month", tick: { fontSize: 11, fill: "var(--appkit-color-text-muted)" }, axisLine: false, tickLine: false }), _jsx(YAxis, { tick: { fontSize: 11, fill: "var(--appkit-color-text-muted)" }, axisLine: false, tickLine: false, width: 40 }), _jsx(Tooltip, { contentStyle: {
32
+ background: "var(--appkit-color-surface)",
33
+ border: "1px solid var(--appkit-color-border)",
34
+ borderRadius: "8px",
35
+ fontSize: 12,
36
+ }, cursor: { fill: "var(--appkit-color-border)", opacity: 0.3 } }), _jsx(Bar, { dataKey: "orders", fill: "url(#ordersBarGrad)", radius: [4, 4, 0, 0] })] }) }) }));
21
37
  }
@@ -1,8 +1,14 @@
1
1
  import React from "react";
2
+ declare const GRADIENTS: Record<string, string>;
2
3
  export interface AdminStatCardProps {
3
4
  label: string;
4
5
  value: string;
6
+ sub?: string;
7
+ /** Emoji or ReactNode icon */
5
8
  icon?: React.ReactNode | string;
9
+ /** Named preset or raw CSS gradient string */
10
+ gradient?: keyof typeof GRADIENTS | string;
6
11
  className?: string;
7
12
  }
8
- export declare function AdminStatCard({ label, value, icon, className, }: AdminStatCardProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function AdminStatCard({ label, value, sub, icon, gradient, className, }: AdminStatCardProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -1,5 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Div, Text, Heading } from "../../../../ui";
3
- export function AdminStatCard({ label, value, icon, className = "", }) {
4
- return (_jsx(Div, { className: `bg-white dark:bg-neutral-900 rounded-xl border border-neutral-200 dark:border-neutral-800 p-4 sm:p-6 ${className}`, children: _jsxs(Div, { className: "flex items-start justify-between", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-sm text-neutral-600 dark:text-neutral-400 mb-1", children: label }), _jsx(Heading, { level: 3, className: "text-2xl font-bold", children: value })] }), icon && _jsx(Div, { className: "text-2xl", children: icon })] }) }));
3
+ const BRAND_FROM = "var(--appkit-color-primary-700, #1343de)";
4
+ const BRAND_MID = "var(--appkit-color-cobalt, #3570fc)";
5
+ const BRAND_TO = "var(--appkit-color-secondary-400, #84e122)";
6
+ const GRADIENTS = {
7
+ brand: `linear-gradient(135deg, ${BRAND_FROM} 0%, ${BRAND_MID} 55%, ${BRAND_TO} 100%)`,
8
+ blue: `linear-gradient(135deg, ${BRAND_FROM} 0%, ${BRAND_MID} 100%)`,
9
+ green: `linear-gradient(135deg, ${BRAND_MID} 0%, ${BRAND_TO} 100%)`,
10
+ amber: "linear-gradient(135deg, #f59e0b 0%, #f97316 100%)",
11
+ rose: "linear-gradient(135deg, #f43f5e 0%, #e11d48 100%)",
12
+ };
13
+ export function AdminStatCard({ label, value, sub, icon, gradient = "brand", className = "", }) {
14
+ const resolvedGradient = GRADIENTS[gradient] ?? gradient;
15
+ return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]", style: { background: resolvedGradient }, "aria-hidden": "true" }), _jsxs(Div, { className: "px-5 pb-5 pt-6 flex items-start justify-between gap-3", children: [_jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)]", children: label }), _jsx(Heading, { level: 3, className: "mt-2 text-2xl font-bold tabular-nums leading-none text-[var(--appkit-color-text)]", children: value }), sub && (_jsx(Text, { className: "mt-1.5 text-xs text-[var(--appkit-color-text-muted)]", children: sub }))] }), icon && (_jsx(Div, { className: "flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center text-xl", style: { background: resolvedGradient }, children: icon }))] })] }));
5
16
  }