@mohasinac/appkit 2.8.4 → 2.8.6

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 (128) hide show
  1. package/dist/_internal/client/features/layout/SidebarCollapseToggle.js +2 -0
  2. package/dist/_internal/server/features/checkout/actions.js +2 -1
  3. package/dist/_internal/server/features/grouped/data.js +9 -8
  4. package/dist/_internal/server/features/products/data.js +2 -1
  5. package/dist/_internal/server/features/refunds/actions.js +2 -1
  6. package/dist/_internal/server/features/seo/sitemap.js +2 -1
  7. package/dist/_internal/server/jobs/core/countersReconcile.js +4 -4
  8. package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
  9. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +8 -8
  10. package/dist/_internal/shared/listing-types/action-tracker.js +0 -2
  11. package/dist/constants/field-names.d.ts +3 -0
  12. package/dist/constants/field-names.js +3 -0
  13. package/dist/core/unit-of-work.js +0 -1
  14. package/dist/features/account/components/NotificationBell.js +1 -0
  15. package/dist/features/account/components/UserOffersPanel.js +1 -0
  16. package/dist/features/account/components/UserSidebar.js +3 -1
  17. package/dist/features/admin/components/AdminCarouselEditorView.js +3 -1
  18. package/dist/features/admin/components/AdminSidebar.js +3 -1
  19. package/dist/features/admin/components/AdminSiteSettingsView.js +3 -1
  20. package/dist/features/admin/components/AdminSupportTicketDetailView.js +0 -3
  21. package/dist/features/admin/components/DashboardStats.js +5 -1
  22. package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +3 -1
  23. package/dist/features/admin/components/analytics/AdminStatCard.js +5 -1
  24. package/dist/features/admin/hooks/useChat.js +1 -0
  25. package/dist/features/admin/repository/chat.repository.js +7 -6
  26. package/dist/features/auctions/components/AuctionBidsTable.js +5 -1
  27. package/dist/features/auctions/components/AuctionBottomActions.js +3 -4
  28. package/dist/features/auctions/repository/bid.repository.js +12 -12
  29. package/dist/features/auth/hooks/useAuth.js +1 -0
  30. package/dist/features/blog/actions/blog-actions.d.ts +2 -2
  31. package/dist/features/blog/components/BlogFeaturedCard.js +3 -1
  32. package/dist/features/blog/components/BlogListView.js +5 -1
  33. package/dist/features/blog/components/BlogPostView.js +3 -1
  34. package/dist/features/blog/schemas/index.d.ts +1 -1
  35. package/dist/features/cart/components/CartDrawer.js +3 -1
  36. package/dist/features/cart/repository/cart.repository.js +2 -1
  37. package/dist/features/categories/components/BrandDetailPageView.js +5 -1
  38. package/dist/features/categories/components/CategoryDetailPageView.js +3 -1
  39. package/dist/features/categories/components/CategoryGrid.js +5 -1
  40. package/dist/features/categories/components/CategoryTableColumns.js +6 -2
  41. package/dist/features/categories/components/CategoryTree.js +3 -1
  42. package/dist/features/categories/components/ConcernCard.js +3 -1
  43. package/dist/features/categories/repository/categories.repository.js +26 -25
  44. package/dist/features/categories/schemas/firestore.d.ts +1 -0
  45. package/dist/features/categories/schemas/firestore.js +1 -0
  46. package/dist/features/categories/schemas/index.d.ts +10 -10
  47. package/dist/features/collections/components/CollectionCard.js +3 -1
  48. package/dist/features/events/components/EventCard.js +3 -1
  49. package/dist/features/events/components/EventParticipateView.js +2 -1
  50. package/dist/features/events/schemas/index.d.ts +3 -3
  51. package/dist/features/faq/actions/faq-actions.d.ts +6 -6
  52. package/dist/features/faq/repository/faqs.repository.js +21 -20
  53. package/dist/features/faq/schemas/index.d.ts +2 -2
  54. package/dist/features/forms/Slider.js +3 -1
  55. package/dist/features/history/hooks/useHistory.js +1 -0
  56. package/dist/features/homepage/components/AdSlot.js +6 -2
  57. package/dist/features/homepage/components/AdvertisementBanner.js +3 -1
  58. package/dist/features/homepage/components/BeforeAfterCard.js +5 -1
  59. package/dist/features/homepage/components/CustomCardsSection.js +5 -1
  60. package/dist/features/homepage/components/HowItWorksSection.js +3 -1
  61. package/dist/features/homepage/components/NewsletterBanner.js +6 -2
  62. package/dist/features/homepage/components/SecurityHighlightsSection.js +3 -1
  63. package/dist/features/homepage/components/StatsCounterSection.js +3 -1
  64. package/dist/features/homepage/components/TrustFeaturesSection.js +3 -1
  65. package/dist/features/homepage/repository/carousel.repository.js +8 -7
  66. package/dist/features/homepage/repository/carousels.repository.js +2 -1
  67. package/dist/features/homepage/repository/homepage-sections.repository.js +6 -5
  68. package/dist/features/layout/AppLayoutShell.js +3 -1
  69. package/dist/features/layout/BottomActionsContext.js +1 -3
  70. package/dist/features/layout/BottomNavbar.js +1 -1
  71. package/dist/features/media/MediaLightbox.js +9 -3
  72. package/dist/features/media/modals/ImageEditor.js +6 -2
  73. package/dist/features/media/upload/MediaUploadField.js +1 -1
  74. package/dist/features/messages/hooks/useConversation.js +2 -0
  75. package/dist/features/messages/hooks/useConversations.js +1 -0
  76. package/dist/features/messages/repository/conversations.repository.js +5 -4
  77. package/dist/features/orders/components/OrdersList.js +3 -1
  78. package/dist/features/orders/repository/orders.repository.js +13 -13
  79. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
  80. package/dist/features/products/components/CompareOverlay.js +5 -1
  81. package/dist/features/products/components/FeatureBadge.js +3 -1
  82. package/dist/features/products/components/MarketplacePrizeDrawCard.js +1 -1
  83. package/dist/features/products/components/ProductGrid.js +3 -1
  84. package/dist/features/products/repository/product-features.repository.js +7 -6
  85. package/dist/features/products/repository/product-templates.repository.js +4 -3
  86. package/dist/features/products/repository/products.repository.js +4 -4
  87. package/dist/features/promotions/repository/claimed-coupons.repository.js +2 -1
  88. package/dist/features/promotions/repository/coupons.repository.js +4 -4
  89. package/dist/features/reviews/components/ReviewDetailShell.js +8 -2
  90. package/dist/features/reviews/components/ReviewModal.js +5 -1
  91. package/dist/features/reviews/components/ReviewSummary.js +3 -1
  92. package/dist/features/reviews/components/ReviewsList.js +5 -1
  93. package/dist/features/reviews/repository/reviews.repository.js +2 -2
  94. package/dist/features/scams/repository/scammer.repository.js +3 -3
  95. package/dist/features/search/components/Search.js +14 -1
  96. package/dist/features/seller/components/PhysicalLocationModal.js +1 -1
  97. package/dist/features/seller/components/SellerAddressesView.js +2 -1
  98. package/dist/features/seller/components/SellerBidsView.js +3 -1
  99. package/dist/features/seller/components/SellerCouponEditorView.js +3 -1
  100. package/dist/features/seller/components/SellerOffersPanel.js +1 -0
  101. package/dist/features/seller/components/SellerPayoutMethodsView.js +1 -0
  102. package/dist/features/seller/components/SellerProductsView.js +5 -1
  103. package/dist/features/seller/components/SellerReviewsView.js +1 -0
  104. package/dist/features/seller/components/SellerShippingConfigsView.js +1 -0
  105. package/dist/features/seller/components/SellerSidebar.js +12 -4
  106. package/dist/features/seller/components/SellerStoreCategoriesView.js +1 -0
  107. package/dist/features/seller/components/SellerTemplatesView.js +1 -0
  108. package/dist/features/shell/FormShell.js +10 -2
  109. package/dist/features/shell/QuickFormDrawer.js +5 -2
  110. package/dist/features/stores/components/StoreHeader.js +5 -1
  111. package/dist/features/support/repository/support.repository.js +8 -7
  112. package/dist/features/wishlist/components/WishlistPage.js +3 -1
  113. package/dist/providers/db-firebase/admin.js +0 -1
  114. package/dist/seed/products-auctions-seed-data.js +107 -0
  115. package/dist/ui/DataTable.js +6 -2
  116. package/dist/ui/components/AvatarDisplay.js +3 -1
  117. package/dist/ui/components/BackgroundRenderer.js +4 -2
  118. package/dist/ui/components/Card.js +6 -2
  119. package/dist/ui/components/ImageGallery.js +3 -1
  120. package/dist/ui/components/ImageLightbox.js +3 -1
  121. package/dist/ui/components/PasswordStrengthIndicator.js +3 -1
  122. package/dist/ui/components/Progress.js +6 -2
  123. package/dist/ui/components/RowActionMenu.js +3 -1
  124. package/dist/ui/components/SiteLogo.js +7 -1
  125. package/dist/ui/components/Skeleton.js +3 -1
  126. package/dist/ui/components/Slider.js +3 -1
  127. package/dist/ui/components/TabStrip.js +3 -1
  128. package/package.json +1 -1
@@ -9,7 +9,9 @@ const CHEVRON_STYLE = {
9
9
  export function SidebarCollapseToggle({ expanded, onToggle, expandedLabel = "Collapse sidebar", collapsedLabel = "Expand sidebar", }) {
10
10
  return (_jsx("button", { type: "button", onClick: onToggle, "aria-label": expanded ? expandedLabel : collapsedLabel, "aria-expanded": expanded,
11
11
  // eslint-disable-next-line lir/no-inline-static-style
12
+ // audit-inline-style-ok: pass-through style prop
12
13
  style: HANDLE_STYLE, className: "w-9 shrink-0 flex items-center justify-center cursor-pointer rounded-r-[1.25rem] shadow-lg transition-all duration-200 hover:shadow-xl hover:brightness-110 active:scale-[0.96]", children: _jsx("svg", { className: `w-4 h-4 drop-shadow-sm transition-transform duration-300 ${expanded ? "" : "rotate-180"}`,
13
14
  // eslint-disable-next-line lir/no-inline-static-style
15
+ // audit-inline-style-ok: pass-through style prop
14
16
  style: CHEVRON_STYLE, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M15 19l-7-7 7-7" }) }) }));
15
17
  }
@@ -7,6 +7,7 @@
7
7
  * concerns; the action only does transactional order placement.
8
8
  */
9
9
  import { ApiError, ValidationError, NotFoundError, ERROR_MESSAGES } from "../../../../errors";
10
+ import { ORDER_FIELDS } from "../../../../constants/field-names";
10
11
  import { serverLogger } from "../../../../monitoring";
11
12
  import { unitOfWork, siteSettingsRepository, userRepository, storeRepository, couponsRepository, notificationRepository, claimedCouponsRepository, } from "../../../../repositories";
12
13
  import { failedCheckoutRepository } from "../../../../features/checkout/repository/failed-checkout.repository";
@@ -38,7 +39,7 @@ async function claimDigitalCodeForOrder(db, productId, orderId, userId, opts) {
38
39
  .collection(PRODUCT_COLLECTION)
39
40
  .doc(productId)
40
41
  .collection(PRODUCT_CODES_SUBCOLLECTION);
41
- const snap = await codesRef.where("status", "==", "available").limit(1).get();
42
+ const snap = await codesRef.where(ORDER_FIELDS.STATUS, "==", "available").limit(1).get();
42
43
  if (snap.empty) {
43
44
  serverLogger.warn("claimDigitalCode: code pool exhausted", { productId, orderId });
44
45
  return;
@@ -2,6 +2,7 @@ import { cache } from "react";
2
2
  import { getAdminDb } from "../../../../providers/db-firebase";
3
3
  import { GROUPED_LISTINGS_COLLECTION, } from "../../../../features/grouped/schemas/firestore";
4
4
  import { productRepository } from "../../../../repositories";
5
+ import { PRODUCT_FIELDS, CATEGORY_FIELDS } from "../../../../constants/field-names";
5
6
  import { GROUPED_LISTINGS_FEATURED_LIMIT, GROUPED_LISTINGS_PAGE_SIZE, GROUPED_LISTINGS_SITEMAP_LIMIT, } from "../../../shared/features/grouped/config";
6
7
  function mapDoc(doc) {
7
8
  const data = doc.data();
@@ -43,7 +44,7 @@ export const getGroupedListingForDetail = cache(async (slug) => {
43
44
  const db = getAdminDb();
44
45
  const snap = await db
45
46
  .collection(GROUPED_LISTINGS_COLLECTION)
46
- .where("slug", "==", slug)
47
+ .where(CATEGORY_FIELDS.SLUG, "==", slug)
47
48
  .limit(1)
48
49
  .get();
49
50
  if (snap.empty) {
@@ -77,17 +78,17 @@ export async function listGroupedListings(params = {}) {
77
78
  const db = getAdminDb();
78
79
  let q = db
79
80
  .collection(GROUPED_LISTINGS_COLLECTION)
80
- .where("isActive", "==", true);
81
+ .where(CATEGORY_FIELDS.IS_ACTIVE, "==", true);
81
82
  if (params.featuredOnly)
82
- q = q.where("isFeatured", "==", true);
83
+ q = q.where(CATEGORY_FIELDS.IS_FEATURED, "==", true);
83
84
  if (params.storeId)
84
- q = q.where("storeId", "==", params.storeId);
85
+ q = q.where(PRODUCT_FIELDS.STORE_ID, "==", params.storeId);
85
86
  if (params.brandSlug)
86
- q = q.where("brandSlug", "==", params.brandSlug);
87
+ q = q.where(PRODUCT_FIELDS.BRAND_SLUG, "==", params.brandSlug);
87
88
  if (params.categorySlug)
88
- q = q.where("categorySlug", "==", params.categorySlug);
89
+ q = q.where(PRODUCT_FIELDS.CATEGORY_SLUG, "==", params.categorySlug);
89
90
  const snap = await q
90
- .orderBy("createdAt", "desc")
91
+ .orderBy(PRODUCT_FIELDS.CREATED_AT, "desc")
91
92
  .limit(params.limit ?? GROUPED_LISTINGS_PAGE_SIZE)
92
93
  .get();
93
94
  return snap.docs.map(mapDoc);
@@ -105,7 +106,7 @@ export async function listSitemapGroupedListings() {
105
106
  const db = getAdminDb();
106
107
  const snap = await db
107
108
  .collection(GROUPED_LISTINGS_COLLECTION)
108
- .where("isActive", "==", true)
109
+ .where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
109
110
  .select("slug", "updatedAt")
110
111
  .limit(GROUPED_LISTINGS_SITEMAP_LIMIT)
111
112
  .get();
@@ -4,6 +4,7 @@ import { reviewRepository } from "../../../../repositories";
4
4
  import { getAdminDb } from "../../../../providers/db-firebase";
5
5
  import { PRODUCT_COLLECTION } from "../../../../features/products/schemas/firestore";
6
6
  import { PRODUCTS_SITEMAP_LIMIT } from "../../../shared/features/products/config";
7
+ import { PRODUCT_FIELDS } from "../../../../constants/field-names";
7
8
  // ---------------------------------------------------------------------------
8
9
  // Request-scoped cache — each function is deduplicated per RSC render tree.
9
10
  // Both generateMetadata() and the page component get the same promise.
@@ -22,7 +23,7 @@ export async function listSitemapProducts() {
22
23
  const db = getAdminDb();
23
24
  const snapshot = await db
24
25
  .collection(PRODUCT_COLLECTION)
25
- .where("status", "==", "published")
26
+ .where(PRODUCT_FIELDS.STATUS, "==", "published")
26
27
  .select("slug", "id", "updatedAt", "listingType")
27
28
  .limit(PRODUCTS_SITEMAP_LIMIT)
28
29
  .get();
@@ -16,6 +16,7 @@
16
16
  import { randomUUID } from "crypto";
17
17
  import { getProviders } from "../../../../contracts/registry";
18
18
  import { orderRepository } from "../../../..";
19
+ import { ORDER_FIELDS } from "../../../../constants/field-names";
19
20
  import { NotFoundError, ValidationError } from "../../../../errors";
20
21
  import { serverLogger } from "../../../../monitoring";
21
22
  import { applyRefundDeductionAction } from "../payouts/actions";
@@ -93,7 +94,7 @@ export async function processRefundAction(input) {
93
94
  .doc(item.productId)
94
95
  .collection(PRODUCT_CODES_SUBCOLLECTION)
95
96
  .where("orderId", "==", input.orderId)
96
- .where("status", "==", "available")
97
+ .where(ORDER_FIELDS.STATUS, "==", "available")
97
98
  .limit(1)
98
99
  .get()
99
100
  .then((snap) => {
@@ -7,6 +7,7 @@ import { CATEGORIES_COLLECTION, CATEGORY_FIELDS } from "../../../../features/cat
7
7
  import { STORE_COLLECTION, STORE_FIELDS } from "../../../../features/stores";
8
8
  import { SCAMMER_COLLECTION } from "../../../../features/scams/schemas/firestore";
9
9
  import { serverLogger } from "../../../../monitoring/server-logger";
10
+ import { PRODUCT_FIELDS } from "../../../../constants/field-names";
10
11
  // Product field strings — matches consumer field-names.ts
11
12
  const PRODUCT_STATUS = "status";
12
13
  const PRODUCT_STATUS_PUBLISHED = "published";
@@ -211,7 +212,7 @@ async function fetchScammerUrls(baseUrl) {
211
212
  const db = getAdminDb();
212
213
  const snap = await db
213
214
  .collection(SCAMMER_COLLECTION)
214
- .where("status", "==", "verified")
215
+ .where(PRODUCT_FIELDS.STATUS, "==", "verified")
215
216
  .select("seo", "updatedAt")
216
217
  .limit(2000)
217
218
  .get();
@@ -7,7 +7,7 @@ const ORDERS_COLLECTION = "orders";
7
7
  async function reconcileCategories(ctx) {
8
8
  const snap = await ctx.db
9
9
  .collection(PRODUCT_COLLECTION)
10
- .where("status", "==", ProductStatusValues.PUBLISHED)
10
+ .where(ORDER_FIELDS.STATUS, "==", ProductStatusValues.PUBLISHED)
11
11
  .limit(QUERY_LIMIT)
12
12
  .get();
13
13
  ctx.logger.info(`[categories] ${snap.size} published products found`);
@@ -63,13 +63,13 @@ async function reconcileStores(ctx) {
63
63
  const [productsSnap, ordersSnap, reviewStats] = await Promise.all([
64
64
  ctx.db
65
65
  .collection(PRODUCT_COLLECTION)
66
- .where("sellerId", "==", sellerId)
67
- .where("status", "==", ProductStatusValues.PUBLISHED)
66
+ .where(ORDER_FIELDS.SELLER_ID, "==", sellerId)
67
+ .where(ORDER_FIELDS.STATUS, "==", ProductStatusValues.PUBLISHED)
68
68
  .limit(QUERY_LIMIT)
69
69
  .get(),
70
70
  ctx.db
71
71
  .collection(ORDERS_COLLECTION)
72
- .where("sellerId", "==", sellerId)
72
+ .where(ORDER_FIELDS.SELLER_ID, "==", sellerId)
73
73
  .where(ORDER_FIELDS.STATUS, "==", ORDER_FIELDS.STATUS_VALUES.DELIVERED)
74
74
  .limit(QUERY_LIMIT)
75
75
  .get(),
@@ -21,8 +21,8 @@ export declare const blogPostInputSchema: z.ZodObject<{
21
21
  content: string;
22
22
  isFeatured: boolean;
23
23
  youtubeId?: string | undefined;
24
- coverImage?: string | undefined;
25
24
  excerpt?: string | undefined;
25
+ coverImage?: string | undefined;
26
26
  metaTitle?: string | undefined;
27
27
  metaDescription?: string | undefined;
28
28
  }, {
@@ -34,8 +34,8 @@ export declare const blogPostInputSchema: z.ZodObject<{
34
34
  youtubeId?: string | undefined;
35
35
  tags?: string[] | undefined;
36
36
  isFeatured?: boolean | undefined;
37
- coverImage?: string | undefined;
38
37
  excerpt?: string | undefined;
38
+ coverImage?: string | undefined;
39
39
  metaTitle?: string | undefined;
40
40
  metaDescription?: string | undefined;
41
41
  }>;
@@ -60,8 +60,8 @@ export declare const blogPostUpdateSchema: z.ZodObject<Omit<{
60
60
  tags?: string[] | undefined;
61
61
  content?: string | undefined;
62
62
  isFeatured?: boolean | undefined;
63
- coverImage?: string | undefined;
64
63
  excerpt?: string | undefined;
64
+ coverImage?: string | undefined;
65
65
  metaTitle?: string | undefined;
66
66
  metaDescription?: string | undefined;
67
67
  }, {
@@ -72,8 +72,8 @@ export declare const blogPostUpdateSchema: z.ZodObject<Omit<{
72
72
  tags?: string[] | undefined;
73
73
  content?: string | undefined;
74
74
  isFeatured?: boolean | undefined;
75
- coverImage?: string | undefined;
76
75
  excerpt?: string | undefined;
76
+ coverImage?: string | undefined;
77
77
  metaTitle?: string | undefined;
78
78
  metaDescription?: string | undefined;
79
79
  }>;
@@ -314,6 +314,10 @@ export declare const bundleUpdateSchema: z.ZodObject<{
314
314
  isActive?: boolean | undefined;
315
315
  bundleProductIds?: string[] | undefined;
316
316
  bundleKind?: "brand" | "special" | undefined;
317
+ bundleItemDetails?: {
318
+ productId: string;
319
+ drawCount?: number | undefined;
320
+ }[] | undefined;
317
321
  bundlePriceInPaise?: number | undefined;
318
322
  bundleQueryRule?: {
319
323
  type: "static";
@@ -329,10 +333,6 @@ export declare const bundleUpdateSchema: z.ZodObject<{
329
333
  limit: number;
330
334
  orderBy?: "price-asc" | "price-desc" | "createdAt-desc" | undefined;
331
335
  } | undefined;
332
- bundleItemDetails?: {
333
- productId: string;
334
- drawCount?: number | undefined;
335
- }[] | undefined;
336
336
  }, {
337
337
  description?: string | undefined;
338
338
  name?: string | undefined;
@@ -344,6 +344,10 @@ export declare const bundleUpdateSchema: z.ZodObject<{
344
344
  isActive?: boolean | undefined;
345
345
  bundleProductIds?: string[] | undefined;
346
346
  bundleKind?: "brand" | "special" | undefined;
347
+ bundleItemDetails?: {
348
+ productId: string;
349
+ drawCount?: number | undefined;
350
+ }[] | undefined;
347
351
  bundlePriceInPaise?: number | undefined;
348
352
  bundleQueryRule?: {
349
353
  type: "static";
@@ -359,10 +363,6 @@ export declare const bundleUpdateSchema: z.ZodObject<{
359
363
  limit: number;
360
364
  orderBy?: "price-asc" | "price-desc" | "createdAt-desc" | undefined;
361
365
  } | undefined;
362
- bundleItemDetails?: {
363
- productId: string;
364
- drawCount?: number | undefined;
365
- }[] | undefined;
366
366
  }>;
367
367
  export type BundleCreateBody = z.infer<typeof bundleCreateSchema>;
368
368
  export type BundleUpdateBody = z.infer<typeof bundleUpdateSchema>;
@@ -20,7 +20,6 @@ function defaultSink(event) {
20
20
  if (typeof window === "undefined") {
21
21
  // SSR / Node — best-effort console log; production servers can swap
22
22
  // the sink to a real logger via `setActionTrackerSink`.
23
- // eslint-disable-next-line no-console
24
23
  if (process.env.NODE_ENV !== "production") {
25
24
  console.debug("[action-tracker]", event);
26
25
  }
@@ -30,7 +29,6 @@ function defaultSink(event) {
30
29
  // deployed; for now stay silent in prod to avoid 404 noise + dev-mode
31
30
  // logs surface the events.
32
31
  if (process.env.NODE_ENV !== "production") {
33
- // eslint-disable-next-line no-console
34
32
  console.debug("[action-tracker]", event);
35
33
  }
36
34
  }
@@ -410,6 +410,7 @@ export declare const CATEGORY_FIELDS: {
410
410
  readonly IS_FEATURED: "isFeatured";
411
411
  readonly SHOW_ON_HOMEPAGE: "showOnHomepage";
412
412
  readonly ICON: "icon";
413
+ readonly ORDER: "order";
413
414
  readonly DISPLAY_ORDER: "displayOrder";
414
415
  readonly IS_ACTIVE: "isActive";
415
416
  readonly CATEGORY_TYPE: "categoryType";
@@ -599,6 +600,7 @@ export declare const SUPPORT_TICKET_FIELDS: {
599
600
  readonly USER_ID: "userId";
600
601
  readonly STORE_ID: "storeId";
601
602
  readonly SUBJECT: "subject";
603
+ readonly CATEGORY: "category";
602
604
  readonly STATUS: "status";
603
605
  readonly PRIORITY: "priority";
604
606
  readonly CREATED_AT: "createdAt";
@@ -620,6 +622,7 @@ export declare const SUPPORT_TICKET_FIELDS: {
620
622
  };
621
623
  export declare const CAROUSEL_FIELDS: {
622
624
  readonly ID: "id";
625
+ readonly NAME: "name";
623
626
  readonly TITLE: "title";
624
627
  readonly ORDER: "order";
625
628
  readonly ACTIVE: "active";
@@ -449,6 +449,7 @@ export const CATEGORY_FIELDS = {
449
449
  IS_FEATURED: "isFeatured",
450
450
  SHOW_ON_HOMEPAGE: "showOnHomepage",
451
451
  ICON: "icon",
452
+ ORDER: "order",
452
453
  DISPLAY_ORDER: "displayOrder",
453
454
  IS_ACTIVE: "isActive",
454
455
  // SB-UNI — categoryType discriminator (category/brand/sublisting/bundle)
@@ -679,6 +680,7 @@ export const SUPPORT_TICKET_FIELDS = {
679
680
  USER_ID: "userId",
680
681
  STORE_ID: "storeId",
681
682
  SUBJECT: "subject",
683
+ CATEGORY: "category",
682
684
  STATUS: "status",
683
685
  PRIORITY: "priority",
684
686
  CREATED_AT: "createdAt",
@@ -703,6 +705,7 @@ export const SUPPORT_TICKET_FIELDS = {
703
705
  // ============================================================================
704
706
  export const CAROUSEL_FIELDS = {
705
707
  ID: "id",
708
+ NAME: "name",
706
709
  TITLE: "title",
707
710
  ORDER: "order",
708
711
  ACTIVE: "active",
@@ -1,4 +1,3 @@
1
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-require-imports
2
1
  const getAdminDb = () => module.require("../providers/db-firebase").getAdminDb();
3
2
  import { DatabaseError } from "../errors";
4
3
  import { serverLogger } from "../monitoring";
@@ -50,6 +50,7 @@ export function NotificationBell({ limit = 10, viewAllHref, labels, icons, rende
50
50
  const emitSuccess = onMarkAllReadSuccess ?? showSuccess;
51
51
  const emitError = onMarkAllReadError ?? showError;
52
52
  const handleMarkAllRead = useCallback(async () => {
53
+ // toast-intentionally-silent — uses emitError(labels.error) callback for feedback
53
54
  try {
54
55
  await markAllRead(undefined);
55
56
  refetch();
@@ -65,6 +65,7 @@ export function UserOffersPanel({ fetchEndpoint = "/api/user/offers", onAcceptCo
65
65
  const [fetchError, setFetchError] = useState("");
66
66
  const [showLoginModal, setShowLoginModal] = useState(false);
67
67
  const loadOffers = useCallback(async () => {
68
+ // toast-intentionally-silent — error rendered inline via setFetchError()
68
69
  setLoading(true);
69
70
  setFetchError("");
70
71
  try {
@@ -77,7 +77,9 @@ export function UserSidebar({ items, groups, mobileOpen = false, onCloseMobile,
77
77
  // ── Persistent sidebar variant (desktop left slide-over, mobile bottom-sheet) ──────
78
78
  if (variant === "sidebar") {
79
79
  const handleToggle = onToggle ?? close;
80
- return (_jsxs(_Fragment, { children: [desktopOpen && (_jsx(Div, { className: "hidden md:block fixed inset-0 bg-black/40 backdrop-blur-[2px] z-30", onClick: handleToggle, "aria-hidden": "true" })), _jsxs(Div, { className: "hidden md:flex fixed left-0 z-40 transition-transform duration-300", style: {
80
+ return (_jsxs(_Fragment, { children: [desktopOpen && (_jsx(Div, { className: "hidden md:block fixed inset-0 bg-black/40 backdrop-blur-[2px] z-30", onClick: handleToggle, "aria-hidden": "true" })), _jsxs(Div, { className: "hidden md:flex fixed left-0 z-40 transition-transform duration-300",
81
+ // audit-inline-style-ok: dynamic CSS
82
+ style: {
81
83
  top: "var(--header-height, 3.5rem)",
82
84
  height: "calc(100vh - var(--header-height, 3.5rem))",
83
85
  width: "13rem",
@@ -59,7 +59,9 @@ const TEXT_ALIGN_OPTIONS = [
59
59
  // ── Zone Picker ──────────────────────────────────────────────────────────────
60
60
  function ZonePicker({ selected, onChange, disabled, }) {
61
61
  const zones = [1, 2, 3, 4, 5, 6];
62
- return (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "Zone (row \u00D7 col grid)" }), _jsx("div", { className: "grid gap-1", style: { gridTemplateRows: "repeat(2, 1fr)", gridTemplateColumns: "repeat(3, 1fr)", width: 150 }, children: zones.map((z) => {
62
+ return (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "Zone (row \u00D7 col grid)" }), _jsx("div", { className: "grid gap-1",
63
+ // audit-inline-style-ok: dynamic CSS
64
+ style: { gridTemplateRows: "repeat(2, 1fr)", gridTemplateColumns: "repeat(3, 1fr)", width: 150 }, children: zones.map((z) => {
63
65
  const isDisabled = disabled?.(z) ?? false;
64
66
  return (_jsx("button", { type: "button", disabled: isDisabled, onClick: () => !isDisabled && onChange(z), className: `rounded text-xs font-mono py-2 border transition-colors ${selected === z
65
67
  ? "bg-primary text-white border-primary"
@@ -47,7 +47,9 @@ export function AdminSidebar({ renderNavItems, renderHeader, renderFooter, activ
47
47
  const mobileNavContent = groups ? (_jsx(GroupsContent, { groups: groups, activePath: activePath, onItemClick: close })) : (_jsxs(Nav, { "aria-label": "Admin sidebar", className: "py-3", children: [renderHeader?.(), renderNavItems?.(activePath), renderFooter?.()] }));
48
48
  if (variant === "sidebar") {
49
49
  const handleToggle = onToggle ?? close;
50
- return (_jsxs(_Fragment, { children: [desktopOpen && (_jsx(Div, { className: "hidden md:block fixed inset-0 bg-black/40 backdrop-blur-[2px] z-30", onClick: handleToggle, "aria-hidden": "true" })), _jsxs(Div, { className: "hidden md:flex fixed left-0 z-40 transition-transform duration-300", style: {
50
+ return (_jsxs(_Fragment, { children: [desktopOpen && (_jsx(Div, { className: "hidden md:block fixed inset-0 bg-black/40 backdrop-blur-[2px] z-30", onClick: handleToggle, "aria-hidden": "true" })), _jsxs(Div, { className: "hidden md:flex fixed left-0 z-40 transition-transform duration-300",
51
+ // audit-inline-style-ok: dynamic CSS
52
+ style: {
51
53
  top: "var(--header-height, 3.5rem)",
52
54
  height: "calc(100vh - var(--header-height, 3.5rem))",
53
55
  width: "18rem",
@@ -374,7 +374,9 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
374
374
  ["legal", "⑫ Legal"],
375
375
  ["whatsapp", "⑬ WhatsApp"],
376
376
  ["notifications", "⑭ Notifications"],
377
- ].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(Text, { 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(_Fragment, { children: [_jsx(Text, { 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: 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: "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(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: "space-y-4 pt-4", 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 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _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 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _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 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] })] }), _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: "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(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 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(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: "text-sm font-medium text-zinc-600 dark:text-zinc-400 pt-2", 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: "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(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`, children: _jsx(Span, { weight: "medium", className: "text-zinc-400 select-none p-2", 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(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: "space-y-4 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", 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: "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(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: "space-y-4 pt-4", children: [_jsx(Text, { 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: "notifications", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); notifChannelsMutation.mutate(); }, className: "space-y-6 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", 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, { size: "xs", weight: "semibold", className: "rounded-full bg-success-surface px-2 py-0.5 text-success", 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-1", 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: "space-y-5 pt-4", children: [[
377
+ ].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(Text, { 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(_Fragment, { children: [_jsx(Text, { 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: 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: "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(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: "space-y-4 pt-4", 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 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _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 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _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 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] })] }), _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: "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(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 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(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: "text-sm font-medium text-zinc-600 dark:text-zinc-400 pt-2", 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: "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(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`, children: _jsx(Span, { weight: "medium", className: "text-zinc-400 select-none p-2",
378
+ // audit-inline-style-ok: computed pixel offset
379
+ 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(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: "space-y-4 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", 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: "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(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: "space-y-4 pt-4", children: [_jsx(Text, { 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: "notifications", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); notifChannelsMutation.mutate(); }, className: "space-y-6 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", 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, { size: "xs", weight: "semibold", className: "rounded-full bg-success-surface px-2 py-0.5 text-success", 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-1", 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: "space-y-5 pt-4", children: [[
378
380
  ["Terms of Service", termsHtml, setTermsHtml],
379
381
  ["Privacy Policy", privacyHtml, setPrivacyHtml],
380
382
  ["Refund Policy", refundHtml, setRefundHtml],
@@ -250,7 +250,6 @@ export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject,
250
250
  ["bidId", "Bid ID"],
251
251
  ].map(([key, label]) => (_jsx(Input, { label: label, type: "text", value: parties[key] ?? "", onChange: (e) => setParties((p) => ({ ...p, [key]: e.target.value })), placeholder: label }, key)))] }), isOrderModificationRequest && (_jsx(OrderItemsPanel, { linkedOrderId: linkedOrderId, orderItemsOpen: orderItemsOpen, setOrderItemsOpen: setOrderItemsOpen, orderItems: orderItems, setOrderItems: setOrderItems, loadOrderItems: loadOrderItems, applyOrderItems: applyOrderItems })), isStoreChangeRequest && (_jsx(StoreChangePanel, { linkedStoreId: linkedStoreId, storeStatus: storeStatus, setStoreStatus: setStoreStatus, storeIsVerified: storeIsVerified, setStoreIsVerified: setStoreIsVerified, storeIsFeatured: storeIsFeatured, setStoreIsFeatured: setStoreIsFeatured, applyStoreChange: applyStoreChange })), isUnbanRequest && (_jsx(UnbanRequestPanel, { linkedUserId: linkedUserId, liftHardBan: liftHardBan, liftSoftBanTickets: liftSoftBanTickets })), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "button", isLoading: updateMutation.isPending, disabled: !ticketId || updateMutation.isPending, onClick: () => updateMutation.mutate(), children: "Save changes" })] })] }) }));
252
252
  }
253
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
254
253
  function OrderItemsPanel(props) {
255
254
  const { linkedOrderId, orderItemsOpen, setOrderItemsOpen, orderItems, setOrderItems, loadOrderItems, applyOrderItems, } = props;
256
255
  const updateQty = (idx, raw) => {
@@ -259,7 +258,6 @@ function OrderItemsPanel(props) {
259
258
  };
260
259
  return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-info/40 bg-info-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-info uppercase tracking-wide", children: "Modify order items" }), linkedOrderId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Editing order: ", _jsx("code", { className: "font-mono", children: linkedOrderId })] }), !orderItemsOpen ? (_jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: loadOrderItems.isPending, disabled: loadOrderItems.isPending, onClick: () => loadOrderItems.mutate(), children: "Load current items" })) : (_jsxs(_Fragment, { children: [orderItems.length === 0 ? (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Order has no items." })) : (_jsx(Div, { className: "flex flex-col gap-2", children: orderItems.map((it, idx) => (_jsxs(Div, { className: "flex items-center gap-2 rounded border border-zinc-200 dark:border-zinc-700 px-2 py-1", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "text-xs font-medium truncate", children: it.productTitle }), _jsxs(Text, { size: "xs", color: "muted", children: [it.productId, " \u00B7 \u20B9", (it.unitPrice / 100).toFixed(2), "/ea"] })] }), _jsx("input", { type: "number", min: "0", value: it.quantity, onChange: (e) => updateQty(idx, e.target.value), className: "w-16 rounded border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-2 py-1 text-xs" })] }, `${it.productId}-${idx}`))) })), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setOrderItemsOpen(false), children: "Cancel" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: applyOrderItems.isPending, disabled: applyOrderItems.isPending, onClick: () => applyOrderItems.mutate(), children: "Apply changes" })] })] }))] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "Order ID" }), " ", "field in the Linked parties panel above to enable this action."] }))] }));
261
260
  }
262
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
263
261
  function StoreChangePanel(props) {
264
262
  const { linkedStoreId, storeStatus, setStoreStatus, storeIsVerified, setStoreIsVerified, storeIsFeatured, setStoreIsFeatured, applyStoreChange, } = props;
265
263
  return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-warning/40 bg-warning-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-warning uppercase tracking-wide", children: "Apply store change" }), linkedStoreId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Editing store: ", _jsx("code", { className: "font-mono", children: linkedStoreId })] }), _jsx(Select, { label: "Store status", options: [
@@ -269,7 +267,6 @@ function StoreChangePanel(props) {
269
267
  { label: "Rejected", value: "rejected" },
270
268
  ], value: storeStatus, onValueChange: setStoreStatus }), _jsx(Toggle, { label: "Verified badge", checked: storeIsVerified, onChange: setStoreIsVerified }), _jsx(Toggle, { label: "Featured store", checked: storeIsFeatured, onChange: setStoreIsFeatured }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: applyStoreChange.isPending, disabled: applyStoreChange.isPending, onClick: () => applyStoreChange.mutate(), children: "Apply store change" })] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "Store slug" }), " ", "field in the Linked parties panel above to enable this action."] }))] }));
271
269
  }
272
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
273
270
  function UnbanRequestPanel(props) {
274
271
  const { linkedUserId, liftHardBan, liftSoftBanTickets } = props;
275
272
  return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-error/40 bg-error-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-error uppercase tracking-wide", children: "Lift account ban" }), linkedUserId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Appellant: ", _jsx("code", { className: "font-mono", children: linkedUserId })] }), _jsx(Text, { size: "xs", color: "muted", children: "Resolves the ticket and appends an audit note on success." }), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: liftHardBan.isPending, disabled: liftHardBan.isPending, onClick: () => liftHardBan.mutate(), children: "Lift hard ban" }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: liftSoftBanTickets.isPending, disabled: liftSoftBanTickets.isPending, onClick: () => liftSoftBanTickets.mutate(), children: "Lift ticket soft ban" })] })] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "User slug" }), " ", "field in the Linked parties panel above to enable these actions."] }))] }));
@@ -15,7 +15,11 @@ const BRAND_FROM = "var(--appkit-color-primary-700, #1343de)";
15
15
  const BRAND_MID = "var(--appkit-color-cobalt, #3570fc)";
16
16
  const BRAND_TO = "var(--appkit-color-secondary-400, #84e122)";
17
17
  function StatCard({ label, value, sub, icon: Icon, gradient, iconColor }) {
18
- return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${__O.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 ${__O.hidden}`, children: [_jsx(Text, { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)] truncate", children: label }), _jsx(Text, { className: "mt-2 text-xl font-bold text-[var(--appkit-color-text)] tabular-nums leading-tight break-words", children: value }), sub && (_jsx(Text, { 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" }) })] })] }));
18
+ return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${__O.hidden} shadow-sm hover:shadow-md transition-shadow`, children: [_jsx(Div, { className: "absolute top-0 left-0 right-0 h-[3px]",
19
+ // audit-inline-style-ok: runtime theme gradient
20
+ 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 ${__O.hidden}`, children: [_jsx(Text, { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)] truncate", children: label }), _jsx(Text, { className: "mt-2 text-xl font-bold text-[var(--appkit-color-text)] tabular-nums leading-tight break-words", children: value }), sub && (_jsx(Text, { 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",
21
+ // audit-inline-style-ok: runtime theme gradient
22
+ style: { background: gradient, opacity: 0.92 }, children: _jsx(Icon, { className: "w-5 h-5 text-white drop-shadow-sm" }) })] })] }));
19
23
  }
20
24
  function SkeletonCard() {
21
25
  return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${__O.hidden} ${__P.p5} 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)]" })] })] }));
@@ -19,7 +19,9 @@ const CartesianGrid = dynamic(() => import("recharts").then((m) => m.CartesianGr
19
19
  const Tooltip = dynamic(() => import("recharts").then((m) => m.Tooltip), { ssr: false });
20
20
  // Shared chart card wrapper
21
21
  function ChartCard({ title, children, className = "", }) {
22
- 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: {
22
+ 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]",
23
+ // audit-inline-style-ok: dynamic CSS
24
+ style: {
23
25
  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%)",
24
26
  }, "aria-hidden": "true" }), _jsxs(Div, { className: `${__P.p4} sm:${__P.p6} 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] })] }));
25
27
  }
@@ -12,5 +12,9 @@ const GRADIENTS = {
12
12
  };
13
13
  export function AdminStatCard({ label, value, sub, icon, gradient = "brand", className = "", }) {
14
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 }))] })] }));
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]",
16
+ // audit-inline-style-ok: dynamic CSS
17
+ 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",
18
+ // audit-inline-style-ok: dynamic CSS
19
+ style: { background: resolvedGradient }, children: icon }))] })] }));
16
20
  }