@mohasinac/appkit 4.1.1 → 4.1.3

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 (187) hide show
  1. package/dist/_internal/client/features/maintenance/index.d.ts +2 -0
  2. package/dist/_internal/client/features/maintenance/index.js +1 -0
  3. package/dist/_internal/client/features/maintenance/views/CloudLogsListView.d.ts +9 -0
  4. package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +55 -0
  5. package/dist/_internal/server/features/auctions/index.d.ts +1 -1
  6. package/dist/_internal/server/features/auctions/index.js +1 -1
  7. package/dist/_internal/server/features/auctions/service.js +2 -2
  8. package/dist/_internal/server/features/maintenance/cloud-logs-data.d.ts +50 -0
  9. package/dist/_internal/server/features/maintenance/cloud-logs-data.js +152 -0
  10. package/dist/_internal/server/jobs/core/onOrderCreate.d.ts +4 -12
  11. package/dist/_internal/server/jobs/core/onOrderCreate.js +3 -3
  12. package/dist/_internal/server/jobs/core/onProductWrite.js +14 -2
  13. package/dist/_internal/shared/features/auctions/config.d.ts +1 -1
  14. package/dist/_internal/shared/features/auctions/config.js +1 -1
  15. package/dist/_internal/shared/features/auctions/schema.js +1 -1
  16. package/dist/_internal/shared/features/cart/schema.js +2 -2
  17. package/dist/_internal/shared/features/events/schema.js +1 -1
  18. package/dist/_internal/shared/features/orders/schema.js +1 -1
  19. package/dist/_internal/shared/features/products/schema.js +3 -3
  20. package/dist/_internal/shared/features/promotions/schema.js +3 -3
  21. package/dist/_internal/shared/fees/calculator.d.ts +6 -7
  22. package/dist/client.d.ts +4 -4
  23. package/dist/client.js +4 -2
  24. package/dist/constants/api-endpoints.d.ts +9 -6
  25. package/dist/constants/api-endpoints.js +3 -2
  26. package/dist/contracts/client-payment-gateway.d.ts +1 -2
  27. package/dist/features/about/components/AboutView.d.ts +6 -16
  28. package/dist/features/about/components/AboutView.js +3 -3
  29. package/dist/features/about/index.d.ts +2 -1
  30. package/dist/features/about/schemas/firestore.d.ts +47 -0
  31. package/dist/features/about/schemas/firestore.js +6 -0
  32. package/dist/features/about/schemas/index.d.ts +210 -0
  33. package/dist/features/about/schemas/index.js +45 -0
  34. package/dist/features/account/hooks/useProfile.js +3 -0
  35. package/dist/features/admin/components/AdminCarouselGroupEditorView.d.ts +6 -0
  36. package/dist/features/admin/components/AdminCarouselGroupEditorView.js +50 -0
  37. package/dist/features/admin/components/AdminCartsView.js +16 -3
  38. package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
  39. package/dist/features/admin/components/AdminCouponsView.js +14 -4
  40. package/dist/features/admin/components/AdminEmployeeEditorView.js +1 -42
  41. package/dist/features/admin/components/AdminMediaView.js +1 -1
  42. package/dist/features/admin/components/AdminNotificationsView.js +31 -19
  43. package/dist/features/admin/components/AdminOrdersView.js +7 -0
  44. package/dist/features/admin/components/AdminPayoutsView.js +18 -3
  45. package/dist/features/admin/components/AdminProductsView.js +17 -2
  46. package/dist/features/admin/components/AdminReviewsView.js +16 -2
  47. package/dist/features/admin/components/AdminScammersView.js +7 -0
  48. package/dist/features/admin/components/AdminSectionsView.js +4 -0
  49. package/dist/features/admin/components/AdminSiteConfigGuideView.js +1 -1
  50. package/dist/features/admin/components/AdminSiteSettingsView.js +44 -2
  51. package/dist/features/admin/components/AdminStoreEditorView.d.ts +2 -1
  52. package/dist/features/admin/components/AdminStoreEditorView.js +4 -4
  53. package/dist/features/admin/components/AdminStoresView.js +1 -1
  54. package/dist/features/admin/components/AdminSupportTicketDetailView.js +23 -5
  55. package/dist/features/admin/components/AdminSupportTicketsView.js +20 -2
  56. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +4 -1
  57. package/dist/features/admin/components/AdminTesterChecklistView.js +8 -0
  58. package/dist/features/admin/components/index.d.ts +2 -0
  59. package/dist/features/admin/components/index.js +1 -0
  60. package/dist/features/admin/constants/filter-tabs.d.ts +55 -41
  61. package/dist/features/admin/constants/filter-tabs.js +55 -27
  62. package/dist/features/admin/schemas/firestore.d.ts +5 -0
  63. package/dist/features/auctions/actions/bid-actions.js +1 -1
  64. package/dist/features/auctions/components/AuctionDetailPageView.js +2 -2
  65. package/dist/features/auctions/components/AuctionsListView.js +8 -1
  66. package/dist/features/auth/components/LoginForm.js +2 -2
  67. package/dist/features/auth/components/RegisterForm.js +2 -2
  68. package/dist/features/auth/permissions/constants.d.ts +16 -0
  69. package/dist/features/auth/permissions/constants.js +53 -0
  70. package/dist/features/auth/schemas/firestore.js +1 -0
  71. package/dist/features/blog/components/BlogFilters.js +1 -1
  72. package/dist/features/cart/schemas/firestore.d.ts +1 -1
  73. package/dist/features/categories/schemas/firestore.d.ts +1 -1
  74. package/dist/features/consultation/components/ConsultationForm.js +1 -1
  75. package/dist/features/events/components/EventFilters.js +2 -0
  76. package/dist/features/homepage/components/WelcomeSection.js +2 -2
  77. package/dist/features/layout/TitleBarLayout.d.ts +2 -2
  78. package/dist/features/layout/TitleBarLayout.js +4 -4
  79. package/dist/features/media/AvatarUpload.js +7 -3
  80. package/dist/features/media/MediaVideo.js +1 -1
  81. package/dist/features/media/finalize.d.ts +9 -13
  82. package/dist/features/media/finalize.js +23 -74
  83. package/dist/features/media/server.d.ts +1 -1
  84. package/dist/features/media/server.js +1 -1
  85. package/dist/features/orders/hooks/useOrders.js +16 -4
  86. package/dist/features/orders/schemas/firestore.d.ts +5 -5
  87. package/dist/features/payments/schemas/firestore.d.ts +2 -2
  88. package/dist/features/pre-orders/components/PreOrdersListView.js +7 -0
  89. package/dist/features/products/components/ArtStickersListView.js +5 -0
  90. package/dist/features/products/components/ProductDetailPageView.d.ts +7 -1
  91. package/dist/features/products/components/ProductForm.js +14 -1
  92. package/dist/features/products/components/ProductsIndexPageView.js +7 -0
  93. package/dist/features/products/constants/action-defs.d.ts +1 -0
  94. package/dist/features/products/constants/action-defs.js +1 -0
  95. package/dist/features/products/schemas/firestore.d.ts +1 -1
  96. package/dist/features/products/schemas/product-templates.d.ts +1 -1
  97. package/dist/features/products/types/index.d.ts +4 -0
  98. package/dist/features/scams/schemas/index.js +2 -2
  99. package/dist/features/search/components/Search.js +3 -3
  100. package/dist/features/seller/components/SellerAuctionsView.js +27 -2
  101. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  102. package/dist/features/seller/components/SellerPayoutsView.js +24 -10
  103. package/dist/features/seller/components/SellerPreOrdersView.js +12 -4
  104. package/dist/features/seller/components/SellerPrizeDrawsView.js +23 -5
  105. package/dist/features/seller/components/SellerProductShell.d.ts +2 -0
  106. package/dist/features/seller/components/SellerProductShell.js +1 -1
  107. package/dist/features/seller/components/SellerProductsView.js +8 -6
  108. package/dist/features/seller/components/SellerReviewsView.js +27 -6
  109. package/dist/features/seller/components/index.d.ts +0 -2
  110. package/dist/features/seller/components/index.js +0 -1
  111. package/dist/features/stores/components/StoreListingsGuideView.js +2 -1
  112. package/dist/features/stores/components/StoreProductsView.js +1 -1
  113. package/dist/features/stores/schemas/firestore.d.ts +11 -0
  114. package/dist/features/stores/schemas/firestore.js +5 -0
  115. package/dist/features/stores/schemas/index.d.ts +2 -2
  116. package/dist/features/tester/actions/checklist-item-actions.d.ts +6 -0
  117. package/dist/features/tester/actions/checklist-item-actions.js +2 -0
  118. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +13 -2
  119. package/dist/features/tester/components/AdminTesterFeedbackListView.js +2 -1
  120. package/dist/features/tester/components/AdminTesterFeedbackReportView.js +9 -5
  121. package/dist/features/tester/components/TesterHubView.js +34 -21
  122. package/dist/features/tester/repository/tester-checklist-item.repository.js +1 -0
  123. package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +18 -4
  124. package/dist/features/tester/repository/tester-checklist-response.repository.js +43 -8
  125. package/dist/features/tester/schemas/firestore.d.ts +8 -3
  126. package/dist/features/tester/schemas/firestore.js +6 -0
  127. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +21 -9
  128. package/dist/features/tester/utils/phases.d.ts +31 -0
  129. package/dist/features/tester/utils/phases.js +46 -0
  130. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +1 -1
  131. package/dist/features/whatsapp-bot/types/index.d.ts +1 -1
  132. package/dist/features/wishlist/types/index.d.ts +3 -0
  133. package/dist/index.d.ts +11 -4
  134. package/dist/index.js +15 -7
  135. package/dist/next/components/NotFoundView.js +2 -1
  136. package/dist/next/routing/route-map.d.ts +4 -12
  137. package/dist/next/routing/route-map.js +2 -4
  138. package/dist/providers/db-firebase/admin.d.ts +1 -0
  139. package/dist/providers/db-firebase/admin.js +1 -1
  140. package/dist/schemas/registry.d.ts +2 -2
  141. package/dist/seed/addresses-seed-data.js +18 -18
  142. package/dist/seed/bids-seed-data.js +4 -4
  143. package/dist/seed/blog-posts-seed-data.js +82 -82
  144. package/dist/seed/carousel-slides-seed-data.js +14 -10
  145. package/dist/seed/carousels-seed-data.js +3 -3
  146. package/dist/seed/cart-seed-data.js +92 -92
  147. package/dist/seed/claimed-coupons-seed-data.js +10 -10
  148. package/dist/seed/conversations-seed-data.js +75 -75
  149. package/dist/seed/coupon-usage-seed-data.js +12 -12
  150. package/dist/seed/coupons-seed-data.js +41 -41
  151. package/dist/seed/events-seed-data.js +105 -105
  152. package/dist/seed/grouped-listings-seed-data.d.ts +2 -137
  153. package/dist/seed/grouped-listings-seed-data.js +79 -143
  154. package/dist/seed/history-seed-data.js +38 -38
  155. package/dist/seed/homepage-sections-seed-data.js +9 -9
  156. package/dist/seed/notifications-seed-data.js +56 -56
  157. package/dist/seed/offers-seed-data.js +91 -91
  158. package/dist/seed/orders-seed-data.js +121 -106
  159. package/dist/seed/payouts-seed-data.js +42 -42
  160. package/dist/seed/products-auctions-seed-data.js +10 -2
  161. package/dist/seed/products-standard-seed-data.js +40 -8
  162. package/dist/seed/reviews-seed-data.js +42 -23
  163. package/dist/seed/scammers-seed-data.js +22 -22
  164. package/dist/seed/sessions-seed-data.js +7 -7
  165. package/dist/seed/shipments-seed-data.js +10 -10
  166. package/dist/seed/site-settings-seed-data.js +82 -0
  167. package/dist/seed/store-addresses-seed-data.js +2 -2
  168. package/dist/seed/store-extensions-seed-data.js +93 -93
  169. package/dist/seed/stores-seed-data.js +2 -0
  170. package/dist/seed/support-tickets-seed-data.js +47 -47
  171. package/dist/seed/users-seed-data.js +55 -89
  172. package/dist/seed/wishlists-seed-data.js +22 -22
  173. package/dist/server.d.ts +2 -1
  174. package/dist/server.js +5 -5
  175. package/dist/styles.css +1 -1
  176. package/dist/tailwind-utilities.css +1 -1
  177. package/dist/ui/components/PageLoader.js +8 -3
  178. package/dist/ui/components/Select.d.ts +8 -1
  179. package/dist/ui/components/Select.js +2 -2
  180. package/dist/ui/components/SiteMark.d.ts +23 -0
  181. package/dist/ui/components/SiteMark.js +76 -0
  182. package/dist/ui/index.d.ts +2 -0
  183. package/dist/ui/index.js +1 -0
  184. package/dist/validation/schemas.d.ts +0 -2
  185. package/dist/validation/schemas.js +0 -2
  186. package/package.json +1 -1
  187. package/scripts/seed-cli.mjs +17 -15
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { normalizeError } from "../../../errors/normalize";
4
4
  import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
5
5
  import { sortBy } from "@mohasinac/appkit";
@@ -54,7 +54,7 @@ export function AdminCouponsView({ children, onBulkArchive, onBulkDelete, ...pro
54
54
  title: "Coupons",
55
55
  searchPlaceholder: "Search codes, campaigns, or seller scopes",
56
56
  emptyLabel: "No coupons found",
57
- filterKeys: ["type"],
57
+ filterKeys: ["type", "showExpired"],
58
58
  defaultSort: sortBy("createdAt", "DESC"),
59
59
  queryKey: ["admin", "coupons", "listing"],
60
60
  endpoint: ADMIN_ENDPOINTS.COUPONS,
@@ -69,7 +69,14 @@ export function AdminCouponsView({ children, onBulkArchive, onBulkDelete, ...pro
69
69
  raw: item,
70
70
  })),
71
71
  getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
72
- buildFilters: (state) => state.type && state.type !== "All" ? sieveFilter("type", SIEVE_OP.EQ, state.type) : undefined,
72
+ buildFilters: (state) => {
73
+ const parts = [];
74
+ if (state.type && state.type !== "All")
75
+ parts.push(sieveFilter("type", SIEVE_OP.EQ, state.type));
76
+ if (state.showExpired !== "true")
77
+ parts.push(sieveFilter("validity.isActive", SIEVE_OP.EQ, true));
78
+ return parts.join(",") || undefined;
79
+ },
73
80
  primaryAction: {
74
81
  label: "Add Coupon",
75
82
  onClick: ({ openCreatePanel }) => openCreatePanel(),
@@ -95,7 +102,10 @@ export function AdminCouponsView({ children, onBulkArchive, onBulkDelete, ...pro
95
102
  onClick: handlers[id],
96
103
  }));
97
104
  },
98
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Type", tabs: ADMIN_COUPON_TYPE_TABS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) })),
105
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Type", tabs: ADMIN_COUPON_TYPE_TABS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) }), _jsx(FilterChipGroup, { label: "Expired coupons", tabs: [
106
+ { id: "", label: "Hide expired" },
107
+ { id: "true", label: "Show all" },
108
+ ], value: pendingFilters.showExpired ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showExpired: id })), allId: "" })] })),
99
109
  renderCards: (rows, _view, selectionCtx, isLoading) => {
100
110
  rowsRef.current = rows;
101
111
  return isLoading ? (_jsx(Div, { gap: "3", className: "fluid-grid-card", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Stack, { border: "skeleton", className: "animate-pulse", gap: "3", rounded: "xl", padding: "md", children: [_jsx(Div, { className: "h-6 w-2/3", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" })] }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "text-center", padding: "y-4xl", children: _jsx(Text, { color: "faint", children: "No coupons found" }) })) : (_jsx(Div, { gap: "3", className: "fluid-grid-card", children: rows.map((row) => (_jsx(CouponCard, { coupon: row.raw, onEdit: selectionCtx.openEditPanel, onToggleActive: handleToggle, onDelete: handleDelete }, row.id))) }));
@@ -6,7 +6,7 @@ import { useQueryClient } from "@tanstack/react-query";
6
6
  import { Button, Checkbox, ConfirmDeleteModal, Details, Div, Form, FormActions, Input, Label, Select, SideDrawer, Span, Stack, Summary, Text, useToast } from "../../../ui";
7
7
  import { apiClient } from "../../../http";
8
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
- import { PERMISSION_GROUPS, } from "../../auth/permissions/constants";
9
+ import { PERMISSION_GROUPS, PERMISSION_DOMAINS, getPermissionsForDomain, formatPermLabel, } from "../../auth/permissions/constants";
10
10
  const __O = {
11
11
  yAuto: "overflow-y-auto",
12
12
  };
@@ -32,47 +32,6 @@ const GROUP_OPTIONS = [
32
32
  { label: "Scam Moderator", value: "scam_moderator" },
33
33
  { label: "Custom", value: "custom" },
34
34
  ];
35
- const PERMISSION_DOMAINS = [
36
- { label: "Dashboard", prefix: "admin:dashboard:" },
37
- { label: "Users & Bans", prefix: "admin:users:|admin:user-bans:" },
38
- { label: "Products", prefix: "admin:products:" },
39
- { label: "Orders & Returns", prefix: "admin:orders:|admin:returns:" },
40
- { label: "Stores", prefix: "admin:stores:|admin:store-addresses:" },
41
- { label: "Finance", prefix: "admin:analytics:|admin:payouts:" },
42
- { label: "Catalog", prefix: "admin:categories:|admin:brands:|admin:coupons:|admin:deals:|admin:featured:" },
43
- {
44
- label: "Content",
45
- prefix: "admin:reviews:|admin:blog:|admin:bids:|admin:media:",
46
- },
47
- {
48
- label: "Site / CMS",
49
- prefix: "admin:site:|admin:navigation:|admin:sections:|admin:carousel:|admin:ads:|admin:faqs:|admin:newsletter:|admin:contact:",
50
- },
51
- { label: "Events", prefix: "admin:events:|admin:event-entries:" },
52
- { label: "Support & Safety", prefix: "admin:support-tickets:|admin:scammers:" },
53
- { label: "System", prefix: "admin:sessions:|admin:notifications:|admin:carts:|admin:wishlists:|admin:feature-flags:|admin:copilot:|admin:team:" },
54
- ];
55
- function matchesDomain(perm, prefix) {
56
- return prefix.split("|").some((p) => perm.startsWith(p));
57
- }
58
- function getPermissionsForDomain(prefix) {
59
- const allPerms = Object.values(PERMISSION_GROUPS).flat();
60
- const seen = new Set();
61
- const result = [];
62
- for (const p of allPerms) {
63
- if (matchesDomain(p, prefix) && !seen.has(p)) {
64
- seen.add(p);
65
- result.push(p);
66
- }
67
- }
68
- return result;
69
- }
70
- function formatPermLabel(perm) {
71
- const parts = perm.split(":");
72
- return parts[parts.length - 1]
73
- .replace(/-/g, " ")
74
- .replace(/\b\w/g, (c) => c.toUpperCase());
75
- }
76
35
  function PermissionDomainsPanel({ permissions, togglePerm }) {
77
36
  return (_jsxs(Stack, { gap: "sm", children: [_jsxs(Span, { size: "sm", weight: "medium", color: "muted", children: ["Permissions", _jsxs(Span, { size: "xs", weight: "normal", className: "ml-2", color: "muted", children: ["(", permissions.size, " selected)"] })] }), _jsx(Div, { className: `divide-y divide-zinc-100 divide-[var(--appkit-color-border)] max-h-[42vh] ${__O.yAuto}`, rounded: "xl", border: "default", children: PERMISSION_DOMAINS.map((domain) => {
78
37
  const domainPerms = getPermissionsForDomain(domain.prefix);
@@ -64,7 +64,7 @@ function MediaBrowser({ onCopy }) {
64
64
  return files;
65
65
  return files.filter((f) => f.name.toLowerCase().includes(q));
66
66
  }, [files, search]);
67
- return (_jsxs(Stack, { className: `${__P.p4}`, gap: "3", rounded: "xl", surface: "default", border: "default", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Browse existing media" }), _jsxs(Row, { align: "center", gap: "sm", wrap: true, children: [_jsx(Select, { value: prefix, onChange: (e) => setPrefix(e.target.value), options: PREFIX_OPTIONS, "aria-label": "Prefix filter", className: "min-w-[140px]" }), _jsx(Input, { type: "text", placeholder: "Search filename\u2026", value: search, onChange: (e) => setSearch(e.target.value), className: "flex-1 min-w-[160px]" })] }), error && (_jsx(Alert, { variant: "error", title: "Load failed", children: error })), _jsxs(Div, { layout: "grid", gap: "2", className: "grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6", children: [filtered.map((f) => {
67
+ return (_jsxs(Stack, { className: `${__P.p4}`, gap: "3", rounded: "xl", surface: "default", border: "default", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Browse existing media" }), _jsxs(Row, { align: "center", gap: "sm", wrap: true, children: [_jsx(Select, { value: prefix, onChange: (e) => setPrefix(e.target.value), options: PREFIX_OPTIONS, "aria-label": "Prefix filter", wrapperClassName: "min-w-[140px]" }), _jsx(Input, { type: "text", placeholder: "Search filename\u2026", value: search, onChange: (e) => setSearch(e.target.value), className: "flex-1 min-w-[160px]" })] }), error && (_jsx(Alert, { variant: "error", title: "Load failed", children: error })), _jsxs(Div, { layout: "grid", gap: "2", className: "grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6", children: [filtered.map((f) => {
68
68
  const isImage = (f.contentType ?? "").startsWith("image/");
69
69
  return (_jsxs(Div, { className: "group overflow-hidden", surface: "muted", rounded: "lg", border: "default", children: [_jsx(Row, { color: "muted", textSize: "xs", className: "relative aspect-square", surface: "subtle", align: "center", justify: "center", children: isImage ? (_jsx(MediaImage, { src: f.downloadURL, alt: f.name, size: "thumbnail" })) : (_jsx(Text, { align: "center", paddingX: "x-xs", className: "break-all", children: f.contentType ?? "file" })) }), _jsxs(Div, { className: "p-[0.375rem]", children: [_jsx(Text, { className: "truncate text-[10px] font-mono", color: "muted", children: f.name.split("/").pop() }), _jsx(Button, { type: "button", variant: "ghost", textSize: "xs", className: "mt-1 w-full", onClick: () => onCopy(f.downloadURL), children: ACTIONS.MEDIA["copy-url"].label })] })] }, f.name));
70
70
  }), filtered.length === 0 && !isLoading && (_jsx(Text, { paddingY: "lg", className: "col-span-full", color: "muted", size: "sm", align: "center", children: "No files found." }))] }), _jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Text, { size: "xs", color: "muted", children: [filtered.length, " file(s)", nextPageToken ? " · more available" : ""] }), nextPageToken && (_jsx(Button, { type: "button", variant: "outline", onClick: () => void load(nextPageToken), disabled: isLoading, children: isLoading ? "Loading…" : "Load more" }))] })] }));
@@ -8,7 +8,7 @@ import { useQueryClient } from "@tanstack/react-query";
8
8
  import { ConfirmDeleteModal, FilterChipGroup, ListingLayout, RowActionMenu, useToast } from "../../../ui";
9
9
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
10
10
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
11
- import { ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
11
+ import { ADMIN_BULK_ACTIONS, ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
12
12
  import { useBulkAction } from "../../../react";
13
13
  import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
14
14
  import { DataListingView } from "./DataListingView";
@@ -64,7 +64,7 @@ export function AdminNotificationsView({ children, ...props }) {
64
64
  title: "Notifications",
65
65
  searchPlaceholder: "Search by title or user ID",
66
66
  emptyLabel: "No notifications found",
67
- filterKeys: ["type"],
67
+ filterKeys: ["type", "readState"],
68
68
  defaultSort: sortBy("createdAt", "DESC"),
69
69
  queryKey: ["admin", "notifications", "listing"],
70
70
  endpoint: ADMIN_ENDPOINTS.ADMIN_NOTIFICATIONS,
@@ -83,25 +83,33 @@ export function AdminNotificationsView({ children, ...props }) {
83
83
  type: toStringValue(item.type, ""),
84
84
  })),
85
85
  getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
86
- buildFilters: (state) => state.type && state.type !== "All" ? sieveFilter("type", SIEVE_OP.EQ, state.type) : undefined,
87
- buildBulkActions: (selection) => [
88
- {
89
- id: ROW_ACTION_ID.MARK_READ,
90
- label: ACTIONS.ADMIN["mark-read"].label,
91
- variant: "primary",
92
- loading: bulkNotifs.isLoading,
93
- onClick: () => {
86
+ buildFilters: (state) => {
87
+ const parts = [];
88
+ if (state.type && state.type !== "All")
89
+ parts.push(sieveFilter("type", SIEVE_OP.EQ, state.type));
90
+ if (state.readState === "unread")
91
+ parts.push(sieveFilter("isRead", SIEVE_OP.EQ, false));
92
+ else if (state.readState === "read")
93
+ parts.push(sieveFilter("isRead", SIEVE_OP.EQ, true));
94
+ return parts.join(",") || undefined;
95
+ },
96
+ // Rule #7: bulk-action array sourced from the ADMIN_BULK_ACTIONS preset.
97
+ buildBulkActions: (selection) => ADMIN_BULK_ACTIONS.notifications.map((id) => ({
98
+ id,
99
+ label: ROW_ACTION_META[id].label,
100
+ destructive: ROW_ACTION_META[id].destructive,
101
+ variant: id === ROW_ACTION_ID.MARK_READ ? "primary" : "secondary",
102
+ loading: id === ROW_ACTION_ID.MARK_READ ? bulkNotifs.isLoading : undefined,
103
+ onClick: () => {
104
+ if (id === ROW_ACTION_ID.MARK_READ) {
94
105
  void bulkNotifs.execute({ action: "mark_read", ids: selection.selectedIds });
95
106
  selection.clearSelection();
96
- },
97
- },
98
- {
99
- id: ROW_ACTION_ID.DELETE,
100
- label: ACTIONS.ADMIN["delete-notification"].label,
101
- variant: "secondary",
102
- onClick: () => setBulkDeleteIds(selection.selectedIds),
107
+ }
108
+ else {
109
+ setBulkDeleteIds(selection.selectedIds);
110
+ }
103
111
  },
104
- ],
112
+ })),
105
113
  renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
106
114
  {
107
115
  label: ACTIONS.ADMIN["resend-notification"].label,
@@ -113,7 +121,11 @@ export function AdminNotificationsView({ children, ...props }) {
113
121
  onClick: () => setDeleteTarget(row),
114
122
  },
115
123
  ] })),
116
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Type", tabs: NOTIF_TYPES.map((opt) => ({ id: opt, label: opt })), value: pendingFilters.type || "All", onChange: (v) => setPendingFilters((p) => ({ ...p, type: v })) })),
124
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Type", tabs: NOTIF_TYPES.map((opt) => ({ id: opt, label: opt })), value: pendingFilters.type || "All", onChange: (v) => setPendingFilters((p) => ({ ...p, type: v })) }), _jsx(FilterChipGroup, { label: "Read state", tabs: [
125
+ { id: "", label: "All" },
126
+ { id: "unread", label: "Unread" },
127
+ { id: "read", label: "Read" },
128
+ ], value: pendingFilters.readState ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, readState: id })), allId: "" })] })),
117
129
  };
118
130
  return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(ConfirmDeleteModal, { isOpen: Boolean(deleteTarget) || Boolean(bulkDeleteIds), onClose: () => {
119
131
  setDeleteTarget(null);
@@ -3,10 +3,12 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
3
3
  import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
4
4
  import { sortBy } from "@mohasinac/appkit";
5
5
  import React, { useState, useCallback } from "react";
6
+ import { useRouter } from "next/navigation";
6
7
  import { FilterChipGroup, ListingLayout, useToast } from "../../../ui";
7
8
  import { apiClient } from "../../../http";
8
9
  import { QuickEditMenu } from "./QuickEditMenu";
9
10
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
11
+ import { ROUTES } from "../../../next/routing/route-map";
10
12
  import { ADMIN_BULK_ACTIONS, ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
11
13
  import { ADMIN_ORDER_STATUS_TABS } from "../constants/filter-tabs";
12
14
  import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../hooks/useAdminListingData";
@@ -14,6 +16,7 @@ import { DataListingView } from "./DataListingView";
14
16
  import { AdminOrderEditorView } from "./AdminOrderEditorView";
15
17
  export function AdminOrdersView({ children, ...props }) {
16
18
  const { showToast } = useToast();
19
+ const router = useRouter();
17
20
  const [drawerOpen, setDrawerOpen] = useState(false);
18
21
  const [selectedRow, setSelectedRow] = useState(null);
19
22
  const handleQuickStatus = useCallback(async (id, status) => {
@@ -94,6 +97,10 @@ export function AdminOrdersView({ children, ...props }) {
94
97
  setDrawerOpen(true);
95
98
  },
96
99
  },
100
+ {
101
+ label: "Open full page",
102
+ onClick: () => router.push(String(ROUTES.ADMIN.ORDER_DETAIL(row.id))),
103
+ },
97
104
  {
98
105
  label: "Update status",
99
106
  separator: true,
@@ -104,7 +104,7 @@ export function AdminPayoutsView({ children, ...props }) {
104
104
  title: "Payouts",
105
105
  searchPlaceholder: "Search stores, payout IDs, or order groups",
106
106
  emptyLabel: "No payouts found",
107
- filterKeys: ["status"],
107
+ filterKeys: ["status", "showAllPayouts"],
108
108
  defaultSort: sortBy("createdAt", "DESC"),
109
109
  queryKey: ["admin", "payouts", "listing"],
110
110
  endpoint: ADMIN_ENDPOINTS.PAYOUTS,
@@ -121,7 +121,19 @@ export function AdminPayoutsView({ children, ...props }) {
121
121
  updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
122
122
  })),
123
123
  getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
124
- buildFilters: (state) => state.status && state.status !== "All" ? sieveFilter("status", SIEVE_OP.EQ, state.status) : undefined,
124
+ buildFilters: (state) => {
125
+ // An explicit status chip always wins. Otherwise, hide the noise
126
+ // (already-paid payouts) by default — mirrors the isSold-hide-by-
127
+ // default precedent — with an escape hatch to see everything.
128
+ // Pipe-joined EQ, not NEQ — the Firestore rule requiring orderBy to
129
+ // match the inequality field does not apply to IN queries, so this
130
+ // stays compatible with the default createdAt sort.
131
+ if (state.status && state.status !== "All")
132
+ return sieveFilter("status", SIEVE_OP.EQ, state.status);
133
+ return state.showAllPayouts !== "true"
134
+ ? sieveFilter("status", SIEVE_OP.EQ, "pending|processing|failed|cancelled")
135
+ : undefined;
136
+ },
125
137
  toolbarExtra: (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", action: ACTIONS.ADMIN["calculate-payouts"], size: "sm", isLoading: calculating, disabled: calculating, onClick: handleCalculatePayouts }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleExportCsv, children: ACTIONS.ADMIN["export-csv"].label })] })),
126
138
  // Rule #7: bulk-action array sourced from the ADMIN_BULK_ACTIONS preset.
127
139
  buildBulkActions: (selection) => ADMIN_BULK_ACTIONS.payouts.map((id) => ({
@@ -140,7 +152,10 @@ export function AdminPayoutsView({ children, ...props }) {
140
152
  disabled: row.status.toLowerCase() === "paid" || row.status.toLowerCase() === "cancelled",
141
153
  },
142
154
  ] })),
143
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_PAYOUT_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })),
155
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_PAYOUT_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Paid payouts", tabs: [
156
+ { id: "", label: "Hide paid" },
157
+ { id: "true", label: "Show all" },
158
+ ], value: pendingFilters.showAllPayouts ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showAllPayouts: id })), allId: "" })] })),
144
159
  };
145
160
  return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(Modal, { isOpen: markPaidOpen, onClose: closePaidModal, title: "Mark payout as paid", children: _jsxs(Form, { onSubmit: (e) => {
146
161
  e.preventDefault();
@@ -108,7 +108,7 @@ export function AdminProductsView({ children, ...props }) {
108
108
  title: "Products",
109
109
  searchPlaceholder: "Search products, SKUs, or seller names",
110
110
  emptyLabel: "No products found",
111
- filterKeys: ["status", "type", "showSold"],
111
+ filterKeys: ["status", "type", "showSold", "showEnded", "showClosed"],
112
112
  defaultSort: sortBy("createdAt", "DESC"),
113
113
  queryKey: ["admin", "products", "listing"],
114
114
  endpoint: ADMIN_ENDPOINTS.PRODUCTS,
@@ -149,6 +149,15 @@ export function AdminProductsView({ children, ...props }) {
149
149
  const typeFilter = TYPE_FILTER_MAP[state.type];
150
150
  if (typeFilter)
151
151
  parts.push(typeFilter);
152
+ // Ended/closed only apply to auctions/prize-draws — auctionEndDate
153
+ // and prizeRevealWindowEnd do not exist on other listing types, so
154
+ // these are no-ops unless the matching Type tab is also selected.
155
+ if (typeFilter === "listingType==auction" && state.showEnded !== "true") {
156
+ parts.push(sieveFilter("auctionEndDate", SIEVE_OP.GTE, new Date().toISOString()));
157
+ }
158
+ if (typeFilter === "listingType==prize-draw" && state.showClosed !== "true") {
159
+ parts.push(sieveFilter("prizeRevealWindowEnd", SIEVE_OP.GTE, new Date().toISOString()));
160
+ }
152
161
  return parts.join(",") || undefined;
153
162
  },
154
163
  primaryAction: {
@@ -222,7 +231,13 @@ export function AdminProductsView({ children, ...props }) {
222
231
  renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_PRODUCT_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Type", tabs: ADMIN_PRODUCT_LISTING_TYPE_TABS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) }), _jsx(FilterChipGroup, { label: "Sold", tabs: [
223
232
  { id: "", label: "Hide sold" },
224
233
  { id: "true", label: "Show sold" },
225
- ], value: pendingFilters.showSold ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showSold: id })), allId: "" })] })),
234
+ ], value: pendingFilters.showSold ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showSold: id })), allId: "" }), _jsx(FilterChipGroup, { label: "Ended", tabs: [
235
+ { id: "", label: "Hide ended" },
236
+ { id: "true", label: "Show ended" },
237
+ ], value: pendingFilters.showEnded ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showEnded: id })), allId: "" }), _jsx(FilterChipGroup, { label: "Closed", tabs: [
238
+ { id: "", label: "Hide closed" },
239
+ { id: "true", label: "Show closed" },
240
+ ], value: pendingFilters.showClosed ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showClosed: id })), allId: "" })] })),
226
241
  renderEditor: ({ editId, closePanel }) => (_jsx(AdminProductEditorView, { productId: editId ?? undefined, initialData: editId ? rawByIdRef.current[editId] : undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })),
227
242
  resolveEditorTitle: ({ isCreate }) => (isCreate ? "Add Product" : "Edit Product"),
228
243
  };
@@ -99,8 +99,22 @@ export function AdminReviewsView({ children, ...props }) {
99
99
  getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
100
100
  buildFilters: (f) => {
101
101
  const parts = [];
102
- if (f.status && f.status !== "All")
102
+ // Default (f.status === "") to pending-only — hides the moderation
103
+ // noise (already-approved/rejected reviews) without a redundant
104
+ // separate toggle, since Status is already a 1-click 3-value chip
105
+ // group. Explicit "All" is reachable via renderFilterPanel allId
106
+ // override below, which makes the FilterChipGroup write the literal
107
+ // string "All" (not "") when clicked — otherwise "never touched" and
108
+ // "explicitly chose All" are indistinguishable.
109
+ if (f.status === "All") {
110
+ // explicit "show everything" — no status filter
111
+ }
112
+ else if (f.status) {
103
113
  parts.push(sieveFilter("status", SIEVE_OP.EQ, f.status));
114
+ }
115
+ else {
116
+ parts.push(sieveFilter("status", SIEVE_OP.EQ, "pending"));
117
+ }
104
118
  if (f.rating && f.rating !== "All")
105
119
  parts.push(sieveFilter("rating", SIEVE_OP.EQ, f.rating));
106
120
  return parts.join(",") || undefined;
@@ -145,7 +159,7 @@ export function AdminReviewsView({ children, ...props }) {
145
159
  onClick: () => handleViewReview(row),
146
160
  },
147
161
  ] })),
148
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_REVIEW_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Rating", tabs: ADMIN_REVIEW_RATING_TABS, value: pendingFilters.rating ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, rating: id })) })] })),
162
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status (defaults to Pending)", tabs: ADMIN_REVIEW_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })), allId: "__all__" }), _jsx(FilterChipGroup, { label: "Rating", tabs: ADMIN_REVIEW_RATING_TABS, value: pendingFilters.rating ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, rating: id })) })] })),
149
163
  };
150
164
  return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(ViewReviewModal, { review: viewReview, isOpen: Boolean(viewReview), onClose: () => setViewReview(null) }), _jsx(Modal, { isOpen: replyOpen, onClose: () => {
151
165
  setReplyOpen(false);
@@ -3,9 +3,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
4
4
  import { sortBy } from "@mohasinac/appkit";
5
5
  import React, { useState } from "react";
6
+ import { useRouter } from "next/navigation";
6
7
  import { FilterChipGroup, ListingLayout, RowActionMenu, Span, Stack, Text } from "../../../ui";
7
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
8
9
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
10
+ import { ROUTES } from "../../../next/routing/route-map";
9
11
  import { ADMIN_SCAMMER_STATUS_TABS } from "../constants/filter-tabs";
10
12
  import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
11
13
  import { DataListingView } from "./DataListingView";
@@ -36,6 +38,7 @@ const SCAMMER_COLUMNS = [
36
38
  },
37
39
  ];
38
40
  export function AdminScammersView({ children, ...props }) {
41
+ const router = useRouter();
39
42
  const [drawerOpen, setDrawerOpen] = useState(false);
40
43
  const [selectedRow, setSelectedRow] = useState(null);
41
44
  if (React.Children.count(children) > 0) {
@@ -91,6 +94,10 @@ export function AdminScammersView({ children, ...props }) {
91
94
  setDrawerOpen(true);
92
95
  },
93
96
  },
97
+ {
98
+ label: "Open full page",
99
+ onClick: () => router.push(String(ROUTES.ADMIN.SCAMMER_BY_ID(row.id))),
100
+ },
94
101
  ] })),
95
102
  renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_SCAMMER_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })),
96
103
  };
@@ -200,6 +200,8 @@ export function AdminSectionsView({ children }) {
200
200
  },
201
201
  onSuccess: async () => {
202
202
  await queryClient.invalidateQueries({ queryKey: ["admin", "sections", "listing"] });
203
+ // Public-facing useHomepageSections() reads under a separate key.
204
+ await queryClient.invalidateQueries({ queryKey: ["homepage-sections"] });
203
205
  toast.showToast(mode === "create" ? "Section created." : "Section updated.", "success");
204
206
  setIsModalOpen(false);
205
207
  },
@@ -215,6 +217,8 @@ export function AdminSectionsView({ children }) {
215
217
  },
216
218
  onSuccess: async () => {
217
219
  await queryClient.invalidateQueries({ queryKey: ["admin", "sections", "listing"] });
220
+ // Public-facing useHomepageSections() reads under a separate key.
221
+ await queryClient.invalidateQueries({ queryKey: ["homepage-sections"] });
218
222
  setReorderServerSnapshot(cloneReorderItems(reorderDraft));
219
223
  setReorderUndoStack([]);
220
224
  toast.showToast("Section order updated.", "success");
@@ -13,7 +13,7 @@ export function AdminSiteConfigGuideView() {
13
13
  { name: "watermark", desc: "Watermark text and opacity applied to all product images via the /api/media proxy." },
14
14
  { name: "fees", desc: "platformFee (%), Razorpay gateway fee pass-through toggle, GST rate on platform fees." },
15
15
  { name: "integrations", desc: "API keys: Razorpay (live/test, disabled by default — manual payment is the default), Resend, WhatsApp Cloud API. Keys are encrypted at rest — never log or export." },
16
- { name: "shipping", desc: "Default shipping carriers, free shipping threshold (paise), COD availability toggle." },
16
+ { name: "shipping", desc: "Default shipping carriers, free shipping threshold (), COD availability toggle." },
17
17
  { name: "auctionConfig", desc: "Global bid increment default, auto-extend window (minutes), minimum auction duration." },
18
18
  { name: "platformLimits", desc: "Cart item cap (50), wishlist cap (20), history cap (50), support ticket limits (2 general + 1 per order)." },
19
19
  { name: "legalPages", desc: "Body HTML for Terms of Service, Privacy Policy, Refund Policy, and Shipping Policy. Rendered on their respective static pages." },
@@ -189,7 +189,18 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
189
189
  const [aboutSubtitle, setAboutSubtitle] = React.useState("");
190
190
  const [aboutMissionTitle, setAboutMissionTitle] = React.useState("");
191
191
  const [aboutMissionText, setAboutMissionText] = React.useState("");
192
+ const [aboutHowItWorksTitle, setAboutHowItWorksTitle] = React.useState("");
193
+ const [aboutHowItems, setAboutHowItems] = React.useState([]);
194
+ const [aboutValuesTitle, setAboutValuesTitle] = React.useState("");
195
+ const [aboutValueItems, setAboutValueItems] = React.useState([]);
196
+ const [aboutMilestonesTitle, setAboutMilestonesTitle] = React.useState("");
197
+ const [aboutMilestones, setAboutMilestones] = React.useState([]);
198
+ const [aboutTeamTitle, setAboutTeamTitle] = React.useState("");
199
+ const [aboutTeamSubtitle, setAboutTeamSubtitle] = React.useState("");
200
+ const [aboutTeamMembers, setAboutTeamMembers] = React.useState([]);
192
201
  const [aboutCtaTitle, setAboutCtaTitle] = React.useState("");
202
+ const [aboutCtaSell, setAboutCtaSell] = React.useState("");
203
+ const [aboutCtaShop, setAboutCtaShop] = React.useState("");
193
204
  // ⑭ Notification channels
194
205
  const [notifEmailEnabled, setNotifEmailEnabled] = React.useState(false);
195
206
  const [notifEmailMinPriority, setNotifEmailMinPriority] = React.useState("normal");
@@ -326,7 +337,18 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
326
337
  setAboutSubtitle(s.aboutContent?.subtitle ?? "");
327
338
  setAboutMissionTitle(s.aboutContent?.missionTitle ?? "");
328
339
  setAboutMissionText(s.aboutContent?.missionText ?? "");
340
+ setAboutHowItWorksTitle(s.aboutContent?.howItWorksTitle ?? "");
341
+ setAboutHowItems(s.aboutContent?.howItems ?? []);
342
+ setAboutValuesTitle(s.aboutContent?.valuesTitle ?? "");
343
+ setAboutValueItems(s.aboutContent?.valueItems ?? []);
344
+ setAboutMilestonesTitle(s.aboutContent?.milestonesTitle ?? "");
345
+ setAboutMilestones(s.aboutContent?.milestones ?? []);
346
+ setAboutTeamTitle(s.aboutContent?.teamTitle ?? "");
347
+ setAboutTeamSubtitle(s.aboutContent?.teamSubtitle ?? "");
348
+ setAboutTeamMembers(s.aboutContent?.teamMembers ?? []);
329
349
  setAboutCtaTitle(s.aboutContent?.ctaTitle ?? "");
350
+ setAboutCtaSell(s.aboutContent?.ctaSell ?? "");
351
+ setAboutCtaShop(s.aboutContent?.ctaShop ?? "");
330
352
  setWaPhoneNumberId(s.credentialsMasked?.whatsappPhoneNumberId ?? "");
331
353
  setWaCloudApiToken(s.credentialsMasked?.whatsappCloudApiToken ?? "");
332
354
  setWaAdminNotifyNumbers(s.credentialsMasked?.whatsappAdminNotifyNumbers ?? "");
@@ -362,7 +384,24 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
362
384
  // save action for the whole form, not 19 per-tab mutations.
363
385
  function buildFullPayload() {
364
386
  return {
365
- aboutContent: { title: aboutTitle, subtitle: aboutSubtitle, missionTitle: aboutMissionTitle, missionText: aboutMissionText, ctaTitle: aboutCtaTitle },
387
+ aboutContent: {
388
+ title: aboutTitle,
389
+ subtitle: aboutSubtitle,
390
+ missionTitle: aboutMissionTitle,
391
+ missionText: aboutMissionText,
392
+ howItWorksTitle: aboutHowItWorksTitle,
393
+ howItems: aboutHowItems,
394
+ valuesTitle: aboutValuesTitle,
395
+ valueItems: aboutValueItems,
396
+ milestonesTitle: aboutMilestonesTitle,
397
+ milestones: aboutMilestones,
398
+ teamTitle: aboutTeamTitle,
399
+ teamSubtitle: aboutTeamSubtitle,
400
+ teamMembers: aboutTeamMembers,
401
+ ctaTitle: aboutCtaTitle,
402
+ ctaSell: aboutCtaSell,
403
+ ctaShop: aboutCtaShop,
404
+ },
366
405
  siteName, tagline, logo: logoUrl, favicon: faviconUrl,
367
406
  maintenance: { enabled: maintenanceMode, message: maintenanceMessage },
368
407
  background: {
@@ -452,6 +491,9 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
452
491
  onSuccess: () => {
453
492
  showToast("Site settings saved.", "success");
454
493
  queryClient.invalidateQueries({ queryKey: ["admin", "site-settings"] });
494
+ // Public-facing consumers (Navbar, checkout, watermark, etc.) read via the
495
+ // shared useSiteSettings() hook under a different key — must invalidate both.
496
+ queryClient.invalidateQueries({ queryKey: ["site-settings"] });
455
497
  },
456
498
  onError: (err) => showToast(err?.message ?? "Failed to save site settings.", "error"),
457
499
  });
@@ -491,7 +533,7 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
491
533
  ["procurement", "⑮ Procurement"],
492
534
  ["emi", "⑯ EMI"],
493
535
  ["gst", "⑰ GST"],
494
- ].map(([value, label]) => (_jsx(TabsTrigger, { value: value, children: label }, value))) }), _jsx(TabsContent, { value: "about", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Override the About page hero and mission text. Leave blank to use the platform defaults." }), _jsx(Input, { label: "Hero title", value: aboutTitle, onChange: (e) => setAboutTitle(e.target.value), placeholder: "About LetItRip" }), _jsx(Input, { label: "Hero subtitle", value: aboutSubtitle, onChange: (e) => setAboutSubtitle(e.target.value), placeholder: "Connecting buyers, sellers, and bidders in one vibrant marketplace" }), _jsx(Input, { label: "Mission section title", value: aboutMissionTitle, onChange: (e) => setAboutMissionTitle(e.target.value), placeholder: "Our Mission" }), _jsxs(_Fragment, { children: [_jsx(Text, { className: "mb-1", color: "muted", size: "sm", weight: "medium", children: "Mission text" }), _jsx(Textarea, { value: aboutMissionText, onChange: (e) => setAboutMissionText(e.target.value), placeholder: "LetItRip was built to democratise commerce\u2026", rows: 4 })] }), _jsx(Input, { label: "CTA banner title", value: aboutCtaTitle, onChange: (e) => setAboutCtaTitle(e.target.value), placeholder: "Ready to get started?" })] }) }), _jsx(TabsContent, { value: "branding", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Input, { label: "Site name", value: siteName, onChange: (e) => setSiteName(e.target.value), placeholder: "LetItRip" }), _jsx(Input, { label: "Tagline", value: tagline, onChange: (e) => setTagline(e.target.value), placeholder: "India's Largest Collectibles Marketplace" }), _jsx(ImageUpload, { label: "Logo", currentImage: logoUrl, onUpload: (file) => upload(file, "store"), onChange: setLogoUrl }), _jsx(ImageUpload, { label: "Favicon", currentImage: faviconUrl, onUpload: (file) => upload(file, "store"), onChange: setFaviconUrl }), _jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "Maintenance mode", checked: maintenanceMode, onChange: setMaintenanceMode }), _jsx(Input, { label: "Maintenance message", value: maintenanceMessage, onChange: (e) => setMaintenanceMessage(e.target.value), placeholder: "We're back soon.", disabled: !maintenanceMode })] })] }) }), _jsx(TabsContent, { value: "appearance", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Sets the background behind the public shell, dashboard sidebars, and any Section/Card that opts into it. Leave the value blank for a plain surface." }), _jsxs(Grid, { cols: 2, gap: "lg", children: [_jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "md", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Light mode" }), _jsx(Select, { label: "Type", options: BG_TYPE_OPTIONS, value: lightBgType, onValueChange: (v) => setLightBgType(v) }), lightBgType === "color" ? (_jsx(Input, { type: "color", label: "Color", value: lightBgValue || "#ffffff", onChange: (e) => setLightBgValue(e.target.value), className: "h-10 w-full cursor-pointer", bare: true }) /* audit-hex-tokens-ok: native color picker requires literal hex string fallback */) : lightBgType === "gradient" ? (_jsx(Textarea, { label: "CSS gradient expression", rows: 2, value: lightBgValue, onChange: (e) => setLightBgValue(e.target.value), placeholder: "linear-gradient(...)" })) : (_jsx(MediaUploadField, { label: lightBgType === "video" ? "Background video" : "Background image", kind: lightBgType === "video" ? "video" : "image", value: lightBgValue, onChange: setLightBgValue, onUpload: (file) => upload(file, "store") })), _jsx(Toggle, { label: "Dim overlay", checked: lightBgOverlayEnabled, onChange: setLightBgOverlayEnabled }), lightBgOverlayEnabled && (_jsxs(_Fragment, { children: [_jsx(Input, { type: "color", label: "Overlay color", value: lightBgOverlayColor, onChange: (e) => setLightBgOverlayColor(e.target.value), className: "h-10 w-full cursor-pointer", bare: true }), " ", _jsx(Slider, { label: "Overlay opacity", min: 0, max: 100, value: lightBgOverlayOpacity, onChange: setLightBgOverlayOpacity })] })), _jsx(Text, { size: "xs", color: "muted", children: "Preview" }), _jsx(Div, { className: "relative h-24 w-full", overflow: "hidden", rounded: "md", border: "default", children: _jsx(BackgroundRenderer, { mode: "light", lightMode: {
536
+ ].map(([value, label]) => (_jsx(TabsTrigger, { value: value, children: label }, value))) }), _jsx(TabsContent, { value: "about", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Override the About page content. Leave blank to use the platform defaults." }), _jsx(Input, { label: "Hero title", value: aboutTitle, onChange: (e) => setAboutTitle(e.target.value), placeholder: "About LetItRip" }), _jsx(Input, { label: "Hero subtitle", value: aboutSubtitle, onChange: (e) => setAboutSubtitle(e.target.value), placeholder: "Connecting buyers, sellers, and bidders in one vibrant marketplace" }), _jsx(Input, { label: "Mission section title", value: aboutMissionTitle, onChange: (e) => setAboutMissionTitle(e.target.value), placeholder: "Our Mission" }), _jsxs(_Fragment, { children: [_jsx(Text, { className: "mb-1", color: "muted", size: "sm", weight: "medium", children: "Mission text" }), _jsx(Textarea, { value: aboutMissionText, onChange: (e) => setAboutMissionText(e.target.value), placeholder: "LetItRip was built to democratise commerce\u2026", rows: 4 })] }), _jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "md", children: [_jsx(Input, { label: '"How it works" section title', value: aboutHowItWorksTitle, onChange: (e) => setAboutHowItWorksTitle(e.target.value), placeholder: "How It Works" }), aboutHowItems.map((item, i) => (_jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "sm", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Text, { size: "xs", weight: "medium", color: "muted", children: ["Step ", i + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setAboutHowItems(aboutHowItems.filter((_, idx) => idx !== i)), children: "\u2715" })] }), _jsx(Input, { label: "Icon (emoji)", value: item.icon, onChange: (e) => { const next = [...aboutHowItems]; next[i] = { ...item, icon: e.target.value }; setAboutHowItems(next); }, placeholder: "\uD83D\uDED2" }), _jsx(Input, { label: "Title", value: item.title, onChange: (e) => { const next = [...aboutHowItems]; next[i] = { ...item, title: e.target.value }; setAboutHowItems(next); } }), _jsx(Textarea, { value: item.text, onChange: (e) => { const next = [...aboutHowItems]; next[i] = { ...item, text: e.target.value }; setAboutHowItems(next); }, rows: 2 })] }, i))), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setAboutHowItems([...aboutHowItems, { title: "", text: "", icon: "✨", tone: "indigo" }]), children: "+ Add step" })] }), _jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "md", children: [_jsx(Input, { label: "Values section title", value: aboutValuesTitle, onChange: (e) => setAboutValuesTitle(e.target.value), placeholder: "Our Values" }), aboutValueItems.map((item, i) => (_jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "sm", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Text, { size: "xs", weight: "medium", color: "muted", children: ["Value ", i + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setAboutValueItems(aboutValueItems.filter((_, idx) => idx !== i)), children: "\u2715" })] }), _jsx(Input, { label: "Icon (emoji)", value: item.icon, onChange: (e) => { const next = [...aboutValueItems]; next[i] = { ...item, icon: e.target.value }; setAboutValueItems(next); }, placeholder: "\uD83D\uDEE1\uFE0F" }), _jsx(Input, { label: "Title", value: item.title, onChange: (e) => { const next = [...aboutValueItems]; next[i] = { ...item, title: e.target.value }; setAboutValueItems(next); } }), _jsx(Textarea, { value: item.text, onChange: (e) => { const next = [...aboutValueItems]; next[i] = { ...item, text: e.target.value }; setAboutValueItems(next); }, rows: 2 })] }, i))), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setAboutValueItems([...aboutValueItems, { title: "", text: "", icon: "✨" }]), children: "+ Add value" })] }), _jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "md", children: [_jsx(Input, { label: "Milestones section title", value: aboutMilestonesTitle, onChange: (e) => setAboutMilestonesTitle(e.target.value), placeholder: "Our Journey" }), aboutMilestones.map((item, i) => (_jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "sm", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Text, { size: "xs", weight: "medium", color: "muted", children: ["Milestone ", i + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setAboutMilestones(aboutMilestones.filter((_, idx) => idx !== i)), children: "\u2715" })] }), _jsx(Input, { label: "Year", value: item.year, onChange: (e) => { const next = [...aboutMilestones]; next[i] = { ...item, year: e.target.value }; setAboutMilestones(next); }, placeholder: "2024" }), _jsx(Textarea, { value: item.text, onChange: (e) => { const next = [...aboutMilestones]; next[i] = { ...item, text: e.target.value }; setAboutMilestones(next); }, rows: 2 })] }, i))), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setAboutMilestones([...aboutMilestones, { year: "", text: "" }]), children: "+ Add milestone" })] }), _jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "md", children: [_jsx(Input, { label: "Team section title", value: aboutTeamTitle, onChange: (e) => setAboutTeamTitle(e.target.value), placeholder: "Meet the Team" }), _jsx(Input, { label: "Team section subtitle", value: aboutTeamSubtitle, onChange: (e) => setAboutTeamSubtitle(e.target.value), placeholder: "The people building LetItRip" }), aboutTeamMembers.map((member, i) => (_jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "sm", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Text, { size: "xs", weight: "medium", color: "muted", children: ["Team member ", i + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setAboutTeamMembers(aboutTeamMembers.filter((_, idx) => idx !== i)), children: "\u2715" })] }), _jsx(ImageUpload, { label: "Photo", currentImage: member.photoUrl, onUpload: (file) => upload(file, "store"), onChange: (url) => { const next = [...aboutTeamMembers]; next[i] = { ...member, photoUrl: url }; setAboutTeamMembers(next); } }), _jsx(Input, { label: "Name", value: member.name, onChange: (e) => { const next = [...aboutTeamMembers]; next[i] = { ...member, name: e.target.value }; setAboutTeamMembers(next); } }), _jsx(Input, { label: "Role", value: member.role, onChange: (e) => { const next = [...aboutTeamMembers]; next[i] = { ...member, role: e.target.value }; setAboutTeamMembers(next); }, placeholder: "Founder & Developer" }), _jsx(Textarea, { value: member.bio, onChange: (e) => { const next = [...aboutTeamMembers]; next[i] = { ...member, bio: e.target.value }; setAboutTeamMembers(next); }, rows: 3 }), _jsxs(Row, { gap: "md", children: [_jsx(Toggle, { label: "Founder", checked: !!member.isFounder, onChange: (v) => { const next = [...aboutTeamMembers]; next[i] = { ...member, isFounder: v }; setAboutTeamMembers(next); } }), _jsx(Toggle, { label: "Developer", checked: !!member.isDeveloper, onChange: (v) => { const next = [...aboutTeamMembers]; next[i] = { ...member, isDeveloper: v }; setAboutTeamMembers(next); } })] })] }, i))), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setAboutTeamMembers([...aboutTeamMembers, { name: "", role: "", bio: "" }]), children: "+ Add team member" })] }), _jsx(Input, { label: "CTA banner title", value: aboutCtaTitle, onChange: (e) => setAboutCtaTitle(e.target.value), placeholder: "Ready to get started?" }), _jsx(Input, { label: "Sell CTA copy", value: aboutCtaSell, onChange: (e) => setAboutCtaSell(e.target.value), placeholder: "Start selling in minutes" }), _jsx(Input, { label: "Shop CTA copy", value: aboutCtaShop, onChange: (e) => setAboutCtaShop(e.target.value), placeholder: "Browse the marketplace" })] }) }), _jsx(TabsContent, { value: "branding", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Input, { label: "Site name", value: siteName, onChange: (e) => setSiteName(e.target.value), placeholder: "LetItRip" }), _jsx(Input, { label: "Tagline", value: tagline, onChange: (e) => setTagline(e.target.value), placeholder: "India's Largest Collectibles Marketplace" }), _jsx(ImageUpload, { label: "Logo", currentImage: logoUrl, onUpload: (file) => upload(file, "store"), onChange: setLogoUrl }), _jsx(ImageUpload, { label: "Favicon", currentImage: faviconUrl, onUpload: (file) => upload(file, "store"), onChange: setFaviconUrl }), _jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "Maintenance mode", checked: maintenanceMode, onChange: setMaintenanceMode }), _jsx(Input, { label: "Maintenance message", value: maintenanceMessage, onChange: (e) => setMaintenanceMessage(e.target.value), placeholder: "We're back soon.", disabled: !maintenanceMode })] })] }) }), _jsx(TabsContent, { value: "appearance", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); saveAllMutation.mutate(); }, className: "pt-[var(--appkit-space-4)]", spacing: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Sets the background behind the public shell, dashboard sidebars, and any Section/Card that opts into it. Leave the value blank for a plain surface." }), _jsxs(Grid, { cols: 2, gap: "lg", children: [_jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "md", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Light mode" }), _jsx(Select, { label: "Type", options: BG_TYPE_OPTIONS, value: lightBgType, onValueChange: (v) => setLightBgType(v) }), lightBgType === "color" ? (_jsx(Input, { type: "color", label: "Color", value: lightBgValue || "#ffffff", onChange: (e) => setLightBgValue(e.target.value), className: "h-10 w-full cursor-pointer", bare: true }) /* audit-hex-tokens-ok: native color picker requires literal hex string fallback */) : lightBgType === "gradient" ? (_jsx(Textarea, { label: "CSS gradient expression", rows: 2, value: lightBgValue, onChange: (e) => setLightBgValue(e.target.value), placeholder: "linear-gradient(...)" })) : (_jsx(MediaUploadField, { label: lightBgType === "video" ? "Background video" : "Background image", kind: lightBgType === "video" ? "video" : "image", value: lightBgValue, onChange: setLightBgValue, onUpload: (file) => upload(file, "store") })), _jsx(Toggle, { label: "Dim overlay", checked: lightBgOverlayEnabled, onChange: setLightBgOverlayEnabled }), lightBgOverlayEnabled && (_jsxs(_Fragment, { children: [_jsx(Input, { type: "color", label: "Overlay color", value: lightBgOverlayColor, onChange: (e) => setLightBgOverlayColor(e.target.value), className: "h-10 w-full cursor-pointer", bare: true }), " ", _jsx(Slider, { label: "Overlay opacity", min: 0, max: 100, value: lightBgOverlayOpacity, onChange: setLightBgOverlayOpacity })] })), _jsx(Text, { size: "xs", color: "muted", children: "Preview" }), _jsx(Div, { className: "relative h-24 w-full", overflow: "hidden", rounded: "md", border: "default", children: _jsx(BackgroundRenderer, { mode: "light", lightMode: {
495
537
  type: lightBgType,
496
538
  value: lightBgValue,
497
539
  overlay: { enabled: lightBgOverlayEnabled, color: lightBgOverlayColor, opacity: lightBgOverlayOpacity / 100 },
@@ -6,6 +6,7 @@ export interface AdminStoreEditorViewProps {
6
6
  storeName?: string;
7
7
  currentStatus?: string;
8
8
  currentIsVerified?: boolean;
9
+ currentIsFeatured?: boolean;
9
10
  currentCapabilities?: string[];
10
11
  }
11
- export declare function AdminStoreEditorView({ open, onClose, storeId, storeName, currentStatus, currentIsVerified, currentCapabilities, }: AdminStoreEditorViewProps): React.JSX.Element;
12
+ export declare function AdminStoreEditorView({ open, onClose, storeId, storeName, currentStatus, currentIsVerified, currentIsFeatured, currentCapabilities, }: AdminStoreEditorViewProps): React.JSX.Element;
@@ -48,12 +48,12 @@ const STATUS_OPTIONS = [
48
48
  { label: "Rejected", value: "rejected" },
49
49
  ];
50
50
  // --- Component ---------------------------------------------------------------
51
- export function AdminStoreEditorView({ open, onClose, storeId, storeName, currentStatus, currentIsVerified, currentCapabilities, }) {
51
+ export function AdminStoreEditorView({ open, onClose, storeId, storeName, currentStatus, currentIsVerified, currentIsFeatured, currentCapabilities, }) {
52
52
  const queryClient = useQueryClient();
53
53
  const { showToast } = useToast();
54
54
  const [storeStatus, setStoreStatus] = React.useState(currentStatus ?? "pending");
55
55
  const [adminNotes, setAdminNotes] = React.useState("");
56
- const [isFeatured, setIsFeatured] = React.useState(false);
56
+ const [isFeatured, setIsFeatured] = React.useState(currentIsFeatured ?? false);
57
57
  const [isVerified, setIsVerified] = React.useState(currentIsVerified ?? false);
58
58
  const [suspensionReason, setSuspensionReason] = React.useState("");
59
59
  const [capabilities, setCapabilities] = React.useState(new Set(currentCapabilities ?? ["suggest_brands", "create_coupons"]));
@@ -71,12 +71,12 @@ export function AdminStoreEditorView({ open, onClose, storeId, storeName, curren
71
71
  if (open) {
72
72
  setStoreStatus(currentStatus ?? "pending");
73
73
  setAdminNotes("");
74
- setIsFeatured(false);
74
+ setIsFeatured(currentIsFeatured ?? false);
75
75
  setIsVerified(currentIsVerified ?? false);
76
76
  setSuspensionReason("");
77
77
  setCapabilities(new Set(currentCapabilities ?? ["suggest_brands", "create_coupons"]));
78
78
  }
79
- }, [open, currentStatus, currentIsVerified, currentCapabilities]);
79
+ }, [open, currentStatus, currentIsVerified, currentIsFeatured, currentCapabilities]);
80
80
  const saveMutation = useApiMutation({
81
81
  mutationFn: async () => {
82
82
  await apiClient.patch(ADMIN_ENDPOINTS.STORE_BY_ID(storeId), {
@@ -109,7 +109,7 @@ export function AdminStoresView({ children, ...props }) {
109
109
  },
110
110
  renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_STORE_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })),
111
111
  };
112
- return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminStoreEditorView, { open: Boolean(editorRow), onClose: () => setEditorRow(null), storeId: editorRow?.id, storeName: editorRow?.primary, currentStatus: editorRow?.status?.toLowerCase(), currentIsVerified: Boolean(editorRow?._raw?.isVerified), currentCapabilities: Array.isArray(editorRow?._raw?.capabilities)
112
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminStoreEditorView, { open: Boolean(editorRow), onClose: () => setEditorRow(null), storeId: editorRow?.id, storeName: editorRow?.primary, currentStatus: editorRow?.status?.toLowerCase(), currentIsVerified: Boolean(editorRow?._raw?.isVerified), currentIsFeatured: Boolean(editorRow?._raw?.isFeatured), currentCapabilities: Array.isArray(editorRow?._raw?.capabilities)
113
113
  ? editorRow._raw.capabilities
114
114
  : undefined })] }));
115
115
  }