@mohasinac/appkit 2.7.49 → 2.7.52

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 (232) hide show
  1. package/dist/_internal/client/features/layout/SidebarCollapseToggle.d.ts +19 -0
  2. package/dist/_internal/client/features/layout/SidebarCollapseToggle.js +15 -0
  3. package/dist/_internal/client/features/layout/index.d.ts +2 -0
  4. package/dist/_internal/client/features/layout/index.js +1 -0
  5. package/dist/_internal/server/features/checkout/actions.js +25 -2
  6. package/dist/_internal/server/features/promotions/actions.js +3 -1
  7. package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.d.ts +1 -0
  8. package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.js +65 -0
  9. package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.d.ts +1 -0
  10. package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.js +163 -0
  11. package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.d.ts +1 -0
  12. package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.js +115 -0
  13. package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.d.ts +1 -0
  14. package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.js +96 -0
  15. package/dist/_internal/shared/actions/action-registry.js +149 -0
  16. package/dist/client.d.ts +45 -6
  17. package/dist/client.js +27 -4
  18. package/dist/constants/api-endpoints.d.ts +42 -0
  19. package/dist/constants/api-endpoints.js +14 -0
  20. package/dist/features/_guide-cls.d.ts +13 -0
  21. package/dist/features/_guide-cls.js +14 -0
  22. package/dist/features/about/components/HelpPageView.js +29 -26
  23. package/dist/features/account/components/BuyerAccountGuideView.d.ts +1 -0
  24. package/dist/features/account/components/BuyerAccountGuideView.js +39 -0
  25. package/dist/features/account/components/BuyerAuctionsGuideView.d.ts +1 -0
  26. package/dist/features/account/components/BuyerAuctionsGuideView.js +38 -0
  27. package/dist/features/account/components/BuyerOrdersGuideView.d.ts +1 -0
  28. package/dist/features/account/components/BuyerOrdersGuideView.js +48 -0
  29. package/dist/features/account/components/BuyerShoppingGuideView.d.ts +1 -0
  30. package/dist/features/account/components/BuyerShoppingGuideView.js +38 -0
  31. package/dist/features/account/components/UserSidebar.js +2 -1
  32. package/dist/features/admin/components/AdminAnalyticsGuideView.d.ts +1 -0
  33. package/dist/features/admin/components/AdminAnalyticsGuideView.js +24 -0
  34. package/dist/features/admin/components/AdminBlogEditorView.js +102 -75
  35. package/dist/features/admin/components/AdminBundleEditorView.js +20 -14
  36. package/dist/features/admin/components/AdminBundlesView.d.ts +1 -3
  37. package/dist/features/admin/components/AdminBundlesView.js +173 -52
  38. package/dist/features/admin/components/AdminCatalogGuideView.d.ts +1 -0
  39. package/dist/features/admin/components/AdminCatalogGuideView.js +28 -0
  40. package/dist/features/admin/components/AdminContentGuideView.d.ts +1 -0
  41. package/dist/features/admin/components/AdminContentGuideView.js +36 -0
  42. package/dist/features/admin/components/AdminCouponEditorView.js +1 -1
  43. package/dist/features/admin/components/AdminGuideHubView.d.ts +5 -0
  44. package/dist/features/admin/components/AdminGuideHubView.js +79 -0
  45. package/dist/features/admin/components/AdminOrdersGuideView.d.ts +1 -0
  46. package/dist/features/admin/components/AdminOrdersGuideView.js +44 -0
  47. package/dist/features/admin/components/AdminProductsView.js +8 -3
  48. package/dist/features/admin/components/AdminSidebar.js +2 -1
  49. package/dist/features/admin/components/AdminSiteConfigGuideView.d.ts +1 -0
  50. package/dist/features/admin/components/AdminSiteConfigGuideView.js +21 -0
  51. package/dist/features/admin/components/AdminStoresGuideView.d.ts +1 -0
  52. package/dist/features/admin/components/AdminStoresGuideView.js +32 -0
  53. package/dist/features/admin/components/AdminTeamGuideView.d.ts +1 -0
  54. package/dist/features/admin/components/AdminTeamGuideView.js +33 -0
  55. package/dist/features/admin/components/AdminTrustGuideView.d.ts +1 -0
  56. package/dist/features/admin/components/AdminTrustGuideView.js +40 -0
  57. package/dist/features/admin/components/AdminUsersGuideView.d.ts +1 -0
  58. package/dist/features/admin/components/AdminUsersGuideView.js +38 -0
  59. package/dist/features/auth/repository/session.repository.js +16 -1
  60. package/dist/features/cart/hooks/useAddToCart.d.ts +2 -0
  61. package/dist/features/cart/hooks/useAddToCart.js +2 -0
  62. package/dist/features/cart/hooks/useGuestCart.d.ts +2 -0
  63. package/dist/features/cart/utils/guest-cart.d.ts +7 -0
  64. package/dist/features/cart/utils/pending-ops.d.ts +6 -0
  65. package/dist/features/categories/components/CategoryBundlesListing.js +3 -25
  66. package/dist/features/categories/components/CategoryProductsListing.js +6 -10
  67. package/dist/features/categories/components/index.d.ts +0 -2
  68. package/dist/features/categories/components/index.js +0 -1
  69. package/dist/features/categories/schemas/firestore.d.ts +9 -0
  70. package/dist/features/events/components/AdminEventEditorView.js +223 -269
  71. package/dist/features/events/components/EventRaffleWinnerView.d.ts +9 -1
  72. package/dist/features/events/components/EventRaffleWinnerView.js +3 -3
  73. package/dist/features/events/components/SpinWheelView.js +3 -3
  74. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +9 -0
  75. package/dist/features/grouped/repository/grouped-listings.repository.js +12 -0
  76. package/dist/features/layout/BottomNavLayout.d.ts +5 -0
  77. package/dist/features/layout/BottomNavLayout.js +21 -0
  78. package/dist/features/layout/TitleBar.js +7 -1
  79. package/dist/features/layout/TitleBarLayout.d.ts +8 -2
  80. package/dist/features/layout/TitleBarLayout.js +11 -7
  81. package/dist/features/media/upload/MediaUploadField.js +1 -1
  82. package/dist/features/orders/schemas/index.d.ts +2 -2
  83. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +12 -6
  84. package/dist/features/products/components/AuctionsIndexListing.js +3 -1
  85. package/dist/features/products/components/MarketplaceBundleCard.d.ts +21 -0
  86. package/dist/features/products/components/MarketplaceBundleCard.js +56 -0
  87. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  88. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +3 -1
  89. package/dist/features/products/components/PrizeDrawEntryActions.js +8 -12
  90. package/dist/features/products/components/ProductDetailActions.d.ts +5 -1
  91. package/dist/features/products/components/ProductDetailActions.js +3 -1
  92. package/dist/features/products/components/ProductDetailPageView.d.ts +2 -0
  93. package/dist/features/products/components/ProductDetailPageView.js +4 -0
  94. package/dist/features/products/components/ProductGrid.js +3 -3
  95. package/dist/features/products/components/ProductsIndexListing.js +18 -9
  96. package/dist/features/products/components/index.d.ts +2 -4
  97. package/dist/features/products/components/index.js +1 -2
  98. package/dist/features/promotions/components/CouponCard.d.ts +11 -1
  99. package/dist/features/promotions/components/CouponCard.js +36 -2
  100. package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +27 -0
  101. package/dist/features/promotions/repository/claimed-coupons.repository.js +115 -0
  102. package/dist/features/promotions/schemas/firestore.d.ts +34 -0
  103. package/dist/features/promotions/schemas/firestore.js +14 -1
  104. package/dist/features/reviews/components/index.d.ts +0 -2
  105. package/dist/features/reviews/components/index.js +0 -1
  106. package/dist/features/seller/components/BrandInlineSelect.js +23 -3
  107. package/dist/features/seller/components/CategoryInlineSelect.js +20 -3
  108. package/dist/features/seller/components/SellerAddressesView.js +3 -2
  109. package/dist/features/seller/components/SellerAnalyticsAlertsView.d.ts +6 -0
  110. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +124 -0
  111. package/dist/features/seller/components/SellerAnalyticsView.d.ts +2 -0
  112. package/dist/features/seller/components/SellerAnalyticsView.js +17 -9
  113. package/dist/features/seller/components/SellerBidsView.js +33 -3
  114. package/dist/features/seller/components/SellerBundlesView.d.ts +5 -0
  115. package/dist/features/seller/components/SellerBundlesView.js +104 -0
  116. package/dist/features/seller/components/SellerClassifiedView.d.ts +5 -0
  117. package/dist/features/seller/components/SellerClassifiedView.js +113 -0
  118. package/dist/features/seller/components/SellerDashboardView.js +19 -7
  119. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +5 -0
  120. package/dist/features/seller/components/SellerDigitalCodesView.js +115 -0
  121. package/dist/features/seller/components/SellerGoogleReviewsView.d.ts +14 -0
  122. package/dist/features/seller/components/SellerGoogleReviewsView.js +95 -0
  123. package/dist/features/seller/components/SellerGroupedListingsView.d.ts +6 -0
  124. package/dist/features/seller/components/SellerGroupedListingsView.js +112 -0
  125. package/dist/features/seller/components/SellerLiveView.d.ts +5 -0
  126. package/dist/features/seller/components/SellerLiveView.js +113 -0
  127. package/dist/features/seller/components/SellerOrdersView.js +53 -2
  128. package/dist/features/seller/components/SellerPayoutMethodsView.d.ts +7 -0
  129. package/dist/features/seller/components/SellerPayoutMethodsView.js +120 -0
  130. package/dist/features/seller/components/SellerPayoutSettingsView.js +50 -12
  131. package/dist/features/seller/components/SellerProductShell.d.ts +3 -1
  132. package/dist/features/seller/components/SellerProductShell.js +35 -11
  133. package/dist/features/seller/components/SellerProductsCards.d.ts +19 -0
  134. package/dist/features/seller/components/SellerProductsCards.js +19 -0
  135. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +14 -0
  136. package/dist/features/seller/components/SellerProductsFilterDrawer.js +22 -0
  137. package/dist/features/seller/components/SellerProductsView.d.ts +3 -1
  138. package/dist/features/seller/components/SellerProductsView.js +92 -49
  139. package/dist/features/seller/components/SellerReviewsView.js +67 -4
  140. package/dist/features/seller/components/SellerShippingConfigsView.d.ts +7 -0
  141. package/dist/features/seller/components/SellerShippingConfigsView.js +149 -0
  142. package/dist/features/seller/components/SellerShippingView.js +37 -10
  143. package/dist/features/seller/components/SellerSidebar.js +2 -1
  144. package/dist/features/seller/components/SellerStoreCategoriesView.d.ts +9 -0
  145. package/dist/features/seller/components/SellerStoreCategoriesView.js +122 -0
  146. package/dist/features/seller/components/SellerStorefrontView.d.ts +0 -7
  147. package/dist/features/seller/components/SellerStorefrontView.js +26 -29
  148. package/dist/features/seller/components/SellerTemplatesView.d.ts +10 -0
  149. package/dist/features/seller/components/SellerTemplatesView.js +265 -0
  150. package/dist/features/seller/components/index.d.ts +26 -0
  151. package/dist/features/seller/components/index.js +13 -0
  152. package/dist/features/seller/components/seller-products-styles.d.ts +7 -0
  153. package/dist/features/seller/components/seller-products-styles.js +14 -0
  154. package/dist/features/shell/FormShell.d.ts +7 -1
  155. package/dist/features/shell/FormShell.js +5 -2
  156. package/dist/features/store-extensions/index.d.ts +4 -0
  157. package/dist/features/store-extensions/index.js +4 -0
  158. package/dist/features/store-extensions/repository/rbac.repositories.d.ts +23 -0
  159. package/dist/features/store-extensions/repository/rbac.repositories.js +32 -0
  160. package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +91 -0
  161. package/dist/features/store-extensions/repository/store-extensions.repositories.js +127 -0
  162. package/dist/features/store-extensions/schemas/firestore.d.ts +244 -0
  163. package/dist/features/store-extensions/schemas/firestore.js +158 -0
  164. package/dist/features/store-extensions/schemas/rbac.d.ts +65 -0
  165. package/dist/features/store-extensions/schemas/rbac.js +43 -0
  166. package/dist/features/stores/api/[storeSlug]/reviews/route.js +49 -7
  167. package/dist/features/stores/components/InteractiveStoreCard.js +7 -3
  168. package/dist/features/stores/components/StoreCapabilitiesGuideView.d.ts +3 -0
  169. package/dist/features/stores/components/StoreCapabilitiesGuideView.js +101 -0
  170. package/dist/features/stores/components/StoreFinanceGuideView.d.ts +3 -0
  171. package/dist/features/stores/components/StoreFinanceGuideView.js +79 -0
  172. package/dist/features/stores/components/StoreGuideHubView.d.ts +5 -0
  173. package/dist/features/stores/components/StoreGuideHubView.js +89 -0
  174. package/dist/features/stores/components/StoreListingsGuideView.d.ts +3 -0
  175. package/dist/features/stores/components/StoreListingsGuideView.js +151 -0
  176. package/dist/features/stores/components/StoreOrdersGuideView.d.ts +3 -0
  177. package/dist/features/stores/components/StoreOrdersGuideView.js +122 -0
  178. package/dist/features/stores/components/StoreProductsListing.js +6 -10
  179. package/dist/features/stores/components/StoreReviewsListing.js +100 -23
  180. package/dist/features/stores/components/StoreSettingsGuideView.d.ts +3 -0
  181. package/dist/features/stores/components/StoreSettingsGuideView.js +56 -0
  182. package/dist/features/stores/components/index.d.ts +12 -1
  183. package/dist/features/stores/components/index.js +6 -1
  184. package/dist/features/stores/hooks/useStores.d.ts +5 -0
  185. package/dist/features/stores/hooks/useStores.js +10 -0
  186. package/dist/features/stores/manifest.js +1 -1
  187. package/dist/features/stores/schemas/firestore.d.ts +2 -0
  188. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +13 -2
  189. package/dist/index.d.ts +60 -9
  190. package/dist/index.js +66 -15
  191. package/dist/next/routing/route-map.d.ts +134 -0
  192. package/dist/next/routing/route-map.js +59 -0
  193. package/dist/providers/db-firebase/__tests__/filter-aliases.test.d.ts +1 -0
  194. package/dist/providers/db-firebase/__tests__/filter-aliases.test.js +93 -0
  195. package/dist/providers/db-firebase/sieve.d.ts +49 -0
  196. package/dist/providers/db-firebase/sieve.js +61 -8
  197. package/dist/react/hooks/useFormStatePreservation.d.ts +17 -0
  198. package/dist/react/hooks/useFormStatePreservation.js +62 -0
  199. package/dist/react/hooks/useInlineRowEdit.d.ts +24 -0
  200. package/dist/react/hooks/useInlineRowEdit.js +68 -0
  201. package/dist/react/index.d.ts +4 -0
  202. package/dist/react/index.js +4 -0
  203. package/dist/repositories/index.d.ts +5 -0
  204. package/dist/repositories/index.js +7 -0
  205. package/dist/seed/actions/demo-seed-actions.d.ts +1 -1
  206. package/dist/seed/claimed-coupons-seed-data.d.ts +13 -0
  207. package/dist/seed/claimed-coupons-seed-data.js +79 -0
  208. package/dist/seed/index.d.ts +2 -0
  209. package/dist/seed/index.js +3 -0
  210. package/dist/seed/manifest.js +17 -0
  211. package/dist/seed/store-extensions-seed-data.d.ts +19 -0
  212. package/dist/seed/store-extensions-seed-data.js +421 -0
  213. package/dist/tailwind-utilities.css +1 -1
  214. package/dist/ui/components/Button.style.css +20 -12
  215. package/dist/ui/components/ClaimCouponButton.d.ts +28 -0
  216. package/dist/ui/components/ClaimCouponButton.js +89 -0
  217. package/dist/ui/components/ListingToolbar.d.ts +8 -1
  218. package/dist/ui/components/ListingToolbar.js +4 -2
  219. package/dist/ui/components/QuickCreateModal.d.ts +15 -0
  220. package/dist/ui/components/QuickCreateModal.js +48 -0
  221. package/dist/ui/components/QuickCreateModal.style.css +84 -0
  222. package/dist/ui/components/StickyBottomBar.d.ts +22 -0
  223. package/dist/ui/components/StickyBottomBar.js +20 -0
  224. package/dist/ui/components/VacationBanner.d.ts +11 -0
  225. package/dist/ui/components/VacationBanner.js +16 -0
  226. package/dist/ui/components/__tests__/BulkActionBar.test.d.ts +1 -0
  227. package/dist/ui/components/__tests__/BulkActionBar.test.js +96 -0
  228. package/dist/ui/components/__tests__/ListingToolbar.test.d.ts +1 -0
  229. package/dist/ui/components/__tests__/ListingToolbar.test.js +125 -0
  230. package/dist/ui/index.d.ts +9 -1
  231. package/dist/ui/index.js +4 -0
  232. package/package.json +8 -2
@@ -0,0 +1,6 @@
1
+ export interface SellerAnalyticsAlertsViewProps {
2
+ labels?: {
3
+ title?: string;
4
+ };
5
+ }
6
+ export declare function SellerAnalyticsAlertsView({ labels, }: SellerAnalyticsAlertsViewProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,124 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useCallback } from "react";
4
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
5
+ import { Alert, Badge, Button, Div, Form, FormActions, Heading, Input, Row, Section, Select, Text, Toggle, useToast, } from "../../../ui";
6
+ import { apiClient } from "../../../http";
7
+ import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
8
+ // ---------------------------------------------------------------------------
9
+ // Config
10
+ // ---------------------------------------------------------------------------
11
+ const OPERATOR_OPTIONS = [
12
+ { value: ">", label: "Greater than (>)" },
13
+ { value: "<", label: "Less than (<)" },
14
+ { value: ">=", label: "≥" },
15
+ { value: "<=", label: "≤" },
16
+ { value: "==", label: "Equals (==)" },
17
+ ];
18
+ const METRIC_OPTIONS = [
19
+ { value: "daily_revenue", label: "Daily Revenue (₹)" },
20
+ { value: "daily_orders", label: "Daily Orders" },
21
+ { value: "low_stock", label: "Low Stock (products)" },
22
+ { value: "cancelled_orders", label: "Cancelled Orders" },
23
+ { value: "pending_payouts", label: "Pending Payouts (₹)" },
24
+ { value: "avg_rating", label: "Average Rating" },
25
+ { value: "reviews_count", label: "New Reviews" },
26
+ ];
27
+ const WINDOW_OPTIONS = [
28
+ { value: "1", label: "Last 1 hour" },
29
+ { value: "6", label: "Last 6 hours" },
30
+ { value: "24", label: "Last 24 hours" },
31
+ { value: "168", label: "Last 7 days" },
32
+ ];
33
+ const CHANNEL_OPTIONS = [
34
+ { value: "in-app", label: "In-App notification" },
35
+ { value: "email", label: "Email" },
36
+ { value: "whatsapp", label: "WhatsApp" },
37
+ ];
38
+ const OPERATOR_BADGE = {
39
+ ">": "danger",
40
+ "<": "warning",
41
+ ">=": "danger",
42
+ "<=": "warning",
43
+ "==": "info",
44
+ "!=": "default",
45
+ };
46
+ // ---------------------------------------------------------------------------
47
+ // Sub-components
48
+ // ---------------------------------------------------------------------------
49
+ function AlertCard({ alert, onToggle, onDelete, }) {
50
+ const metricLabel = METRIC_OPTIONS.find((m) => m.value === alert.metric)?.label ?? alert.metric;
51
+ const opLabel = OPERATOR_OPTIONS.find((o) => o.value === alert.operator)?.label ?? alert.operator;
52
+ const windowLabel = WINDOW_OPTIONS.find((w) => w.value === String(alert.windowHours))?.label ?? `${alert.windowHours}h`;
53
+ return (_jsxs(Div, { className: "flex items-start justify-between gap-4 p-4 rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900", children: [_jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs(Row, { className: "items-center gap-2 mb-1 flex-wrap", children: [_jsx(Text, { className: "font-medium text-zinc-900 dark:text-zinc-100 text-sm", children: alert.label }), _jsx(Badge, { variant: alert.isActive ? "success" : "default", children: alert.isActive ? "Active" : "Paused" })] }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: [metricLabel, " ", opLabel, " ", alert.threshold, " \u00B7 ", windowLabel] }), _jsx(Row, { className: "gap-1 flex-wrap", children: alert.notifyChannels.map((ch) => (_jsx(Badge, { variant: OPERATOR_BADGE[alert.operator] ?? "default", children: ch }, ch))) }), alert.lastTriggeredAt && (_jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-500 mt-1", children: ["Last triggered: ", new Date(alert.lastTriggeredAt).toLocaleString("en-IN")] }))] }), _jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [_jsx(Toggle, { checked: alert.isActive, onChange: (v) => onToggle(alert.id, v), size: "sm" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => onDelete(alert.id), className: "text-red-500 hover:text-red-600", children: "Delete" })] })] }));
54
+ }
55
+ const EMPTY_DRAFT = {
56
+ label: "",
57
+ metric: "daily_orders",
58
+ operator: ">",
59
+ threshold: "",
60
+ windowHours: "24",
61
+ notifyChannels: ["in-app"],
62
+ };
63
+ export function SellerAnalyticsAlertsView({ labels = {}, }) {
64
+ const queryClient = useQueryClient();
65
+ const { showToast } = useToast();
66
+ const [showForm, setShowForm] = useState(false);
67
+ const [draft, setDraft] = useState(EMPTY_DRAFT);
68
+ const { data, isLoading } = useQuery({
69
+ queryKey: ["seller", "analytics-alerts"],
70
+ queryFn: async () => {
71
+ const res = await apiClient.get(SELLER_ENDPOINTS.ANALYTICS_ALERTS);
72
+ return (res?.items ?? []);
73
+ },
74
+ });
75
+ const alerts = data ?? [];
76
+ const createMutation = useMutation({
77
+ mutationFn: async () => {
78
+ const payload = {
79
+ label: draft.label,
80
+ metric: draft.metric,
81
+ operator: draft.operator,
82
+ threshold: Number(draft.threshold),
83
+ windowHours: Number(draft.windowHours),
84
+ notifyChannels: draft.notifyChannels,
85
+ scope: "seller",
86
+ isActive: true,
87
+ };
88
+ return apiClient.post(SELLER_ENDPOINTS.ANALYTICS_ALERTS, payload);
89
+ },
90
+ onSuccess: () => {
91
+ showToast("Alert created", "success");
92
+ void queryClient.invalidateQueries({ queryKey: ["seller", "analytics-alerts"] });
93
+ setShowForm(false);
94
+ setDraft(EMPTY_DRAFT);
95
+ },
96
+ onError: () => showToast("Failed to create alert", "error"),
97
+ });
98
+ const toggleMutation = useMutation({
99
+ mutationFn: async ({ id, isActive }) => apiClient.patch(SELLER_ENDPOINTS.ANALYTICS_ALERT_BY_ID(id), { isActive }),
100
+ onSuccess: () => {
101
+ void queryClient.invalidateQueries({ queryKey: ["seller", "analytics-alerts"] });
102
+ },
103
+ onError: () => showToast("Failed to update alert", "error"),
104
+ });
105
+ const deleteMutation = useMutation({
106
+ mutationFn: async (id) => apiClient.delete(SELLER_ENDPOINTS.ANALYTICS_ALERT_BY_ID(id)),
107
+ onSuccess: () => {
108
+ showToast("Alert deleted", "success");
109
+ void queryClient.invalidateQueries({ queryKey: ["seller", "analytics-alerts"] });
110
+ },
111
+ onError: () => showToast("Failed to delete alert", "error"),
112
+ });
113
+ const toggleChannel = useCallback((ch) => {
114
+ setDraft((d) => ({
115
+ ...d,
116
+ notifyChannels: d.notifyChannels.includes(ch)
117
+ ? d.notifyChannels.filter((c) => c !== ch)
118
+ : [...d.notifyChannels, ch],
119
+ }));
120
+ }, []);
121
+ return (_jsxs("div", { className: "space-y-6", children: [_jsxs(Row, { className: "items-center justify-between", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: labels.title ?? "Analytics Alerts" }), _jsx(Button, { size: "sm", onClick: () => setShowForm((v) => !v), children: showForm ? "Cancel" : "+ New Alert" })] }), _jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "Get notified when a key metric crosses a threshold. Alerts check the metric over the selected window and fire through your chosen channels." }), showForm && (_jsxs(Section, { className: "border border-zinc-200 dark:border-zinc-700 rounded-xl p-5 space-y-4", children: [_jsx(Heading, { level: 3, className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: "Create Alert" }), _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); createMutation.mutate(); }, children: [_jsxs("div", { className: "grid sm:grid-cols-2 gap-4", children: [_jsx(Input, { label: "Alert label", value: draft.label, onChange: (e) => setDraft((d) => ({ ...d, label: e.target.value })), placeholder: "e.g. Low daily orders", required: true }), _jsx(Select, { label: "Metric", value: draft.metric, onValueChange: (v) => setDraft((d) => ({ ...d, metric: v })), options: METRIC_OPTIONS }), _jsx(Select, { label: "Operator", value: draft.operator, onValueChange: (v) => setDraft((d) => ({ ...d, operator: v })), options: OPERATOR_OPTIONS }), _jsx(Input, { label: "Threshold", type: "number", value: draft.threshold, onChange: (e) => setDraft((d) => ({ ...d, threshold: e.target.value })), placeholder: "e.g. 10", required: true }), _jsx(Select, { label: "Check window", value: draft.windowHours, onValueChange: (v) => setDraft((d) => ({ ...d, windowHours: v })), options: WINDOW_OPTIONS })] }), _jsxs("div", { className: "mt-3", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300 mb-2", children: "Notify via" }), _jsx(Row, { className: "gap-2 flex-wrap", children: CHANNEL_OPTIONS.map((ch) => (_jsx("button", { type: "button", onClick: () => toggleChannel(ch.value), className: `px-3 py-1.5 rounded-full text-xs border transition-colors ${draft.notifyChannels.includes(ch.value)
122
+ ? "bg-[var(--appkit-color-primary)] text-white border-transparent"
123
+ : "bg-white dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 border-zinc-200 dark:border-zinc-700"}`, children: ch.label }, ch.value))) })] }), _jsx(FormActions, { align: "right", className: "mt-4", children: _jsx(Button, { type: "submit", isLoading: createMutation.isPending, children: createMutation.isPending ? "Creating…" : "Create Alert" }) })] })] })), isLoading && (_jsx("div", { className: "space-y-2", children: [1, 2, 3].map((i) => (_jsx("div", { className: "h-16 rounded-lg bg-zinc-100 dark:bg-zinc-800 animate-pulse" }, i))) })), !isLoading && alerts.length === 0 && !showForm && (_jsx(Alert, { variant: "info", children: "No alerts configured. Create an alert to get notified when a key metric crosses a threshold." })), !isLoading && alerts.length > 0 && (_jsx("div", { className: "space-y-3", children: alerts.map((alert) => (_jsx(AlertCard, { alert: alert, onToggle: (id, isActive) => toggleMutation.mutate({ id, isActive }), onDelete: (id) => deleteMutation.mutate(id) }, alert.id))) }))] }));
124
+ }
@@ -3,6 +3,8 @@ import type { StackedViewShellProps } from "../../../ui";
3
3
  export interface SellerAnalyticsViewProps extends Omit<StackedViewShellProps, "sections"> {
4
4
  labels?: {
5
5
  title?: string;
6
+ dateFrom?: string;
7
+ dateTo?: string;
6
8
  };
7
9
  renderDateRange?: (from: string, to: string, onChange: (from: string, to: string) => void) => React.ReactNode;
8
10
  renderStats?: (isLoading: boolean) => React.ReactNode;
@@ -1,17 +1,25 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React from "react";
4
- import { StackedViewShell } from "../../../ui";
4
+ import { StackedViewShell, Row, Text } from "../../../ui";
5
+ function DefaultDateRange({ from, to, onChange, labels, }) {
6
+ return (_jsxs(Row, { gap: "md", align: "center", wrap: true, className: "flex-wrap", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm text-[var(--appkit-color-text-muted)]", children: [_jsx(Text, { size: "sm", variant: "secondary", children: labels.dateFrom ?? "From" }), _jsx("input", { type: "date", value: from, onChange: (e) => onChange(e.target.value, to), className: "appkit-input text-sm py-1.5 px-3" })] }), _jsxs("label", { className: "flex items-center gap-2 text-sm text-[var(--appkit-color-text-muted)]", children: [_jsx(Text, { size: "sm", variant: "secondary", children: labels.dateTo ?? "To" }), _jsx("input", { type: "date", value: to, onChange: (e) => onChange(from, e.target.value), className: "appkit-input text-sm py-1.5 px-3" })] })] }));
7
+ }
5
8
  export function SellerAnalyticsView({ labels = {}, renderDateRange, renderStats, renderCharts, renderTopProducts, isLoading = false, ...rest }) {
6
9
  const [from, setFrom] = React.useState("");
7
10
  const [to, setTo] = React.useState("");
11
+ const handleDateChange = (f, t) => {
12
+ setFrom(f);
13
+ setTo(t);
14
+ };
15
+ const dateRange = (renderDateRange ?? ((f, t, onCh) => (_jsx(DefaultDateRange, { from: f, to: t, onChange: onCh, labels: labels }))))(from, to, handleDateChange);
16
+ const statsSection = (renderStats ?? (() => null))(isLoading);
17
+ const chartsSection = (renderCharts ?? (() => null))();
18
+ const topProducts = (renderTopProducts ?? (() => null))();
8
19
  return (_jsx(StackedViewShell, { portal: "seller", ...rest, title: labels.title, sections: [
9
- renderDateRange?.(from, to, (f, t) => {
10
- setFrom(f);
11
- setTo(t);
12
- }),
13
- renderStats?.(isLoading),
14
- renderCharts?.(),
15
- renderTopProducts?.(),
20
+ dateRange,
21
+ statsSection,
22
+ chartsSection,
23
+ topProducts,
16
24
  ] }));
17
25
  }
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useCallback } from "react";
3
+ import React, { useState, useCallback } from "react";
4
4
  import { X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { Badge, Div, FilterChipGroup, ListingToolbar, Pagination, Text } from "../../../ui";
7
+ import { Badge, BulkActionBar, Div, FilterChipGroup, ListingToolbar, Pagination, Text } from "../../../ui";
8
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
9
  import { SELLER_BID_STATUS_TABS } from "../../admin/constants/filter-tabs";
10
10
  import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
@@ -122,5 +122,35 @@ export function SellerBidsView({ endpoint = SELLER_ENDPOINTS.BIDS }) {
122
122
  },
123
123
  ];
124
124
  const selection = useBulkSelection({ items: rows ?? [], keyExtractor: (r) => r.id });
125
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by bidder name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No bids found for your auctions" })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Bid Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })), allId: "" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
125
+ // S-STORE-4-B group bids by productId (auction) with collapsible sections.
126
+ const [collapsedGroups, setCollapsedGroups] = useState(new Set());
127
+ const groupedRows = React.useMemo(() => {
128
+ const groups = new Map();
129
+ for (const row of rows ?? []) {
130
+ const key = row.productId || row.productTitle;
131
+ const g = groups.get(key) ?? { title: row.productTitle, bids: [] };
132
+ g.bids.push(row);
133
+ groups.set(key, g);
134
+ }
135
+ return Array.from(groups.entries()).map(([id, g]) => ({ id, ...g }));
136
+ }, [rows]);
137
+ const toggleGroup = (id) => setCollapsedGroups((s) => {
138
+ const next = new Set(s);
139
+ next.has(id) ? next.delete(id) : next.add(id);
140
+ return next;
141
+ });
142
+ // S-STORE-4-B — bulk actions: cancel / retract.
143
+ const bulkCancel = useCallback(async () => {
144
+ if (!selection.selectedIds.length)
145
+ return;
146
+ await Promise.all(selection.selectedIds.map((id) => fetch(`/api/store/bids/${id}`, { method: "DELETE" }).catch(() => null)));
147
+ selection.clearSelection();
148
+ }, [selection]);
149
+ const bulkActions = [
150
+ { id: "cancel", label: "Cancel selected", onClick: () => void bulkCancel(), variant: "danger" },
151
+ ];
152
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by bidder name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedIds.length > 0 && (_jsx("div", { className: "sticky z-20 px-3 sm:px-4 py-2 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700", style: { top: "calc(var(--header-height, 0px) + 88px)" }, children: _jsx(BulkActionBar, { selectedCount: selection.selectedIds.length, onClearSelection: selection.clearSelection, actions: bulkActions }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), table.get("grouped") === "0" ? (_jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No bids found for your auctions", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: () => selection.toggleAll() })) : (_jsxs("div", { className: "space-y-3", children: [groupedRows.length === 0 && !isLoading && (_jsx(Text, { className: "text-sm text-zinc-500", children: "No bids found for your auctions." })), groupedRows.map((group) => {
153
+ const collapsed = collapsedGroups.has(group.id);
154
+ return (_jsxs(Div, { className: "rounded-lg border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", children: [_jsxs("button", { type: "button", onClick: () => toggleGroup(group.id), className: "w-full flex items-center justify-between px-4 py-2.5 text-sm font-semibold text-[var(--appkit-color-text)] hover:bg-[var(--appkit-color-surface-raised)]", children: [_jsxs("span", { className: "truncate", children: [group.title, " ", _jsxs("span", { className: "text-xs text-[var(--appkit-color-text-muted)] font-normal", children: ["\u00B7 ", group.bids.length, " bid", group.bids.length === 1 ? "" : "s"] })] }), _jsx("span", { "aria-hidden": true, className: "text-[var(--appkit-color-text-muted)]", children: collapsed ? "▸" : "▾" })] }), !collapsed && (_jsx("div", { className: "border-t border-[var(--appkit-color-border)]", children: _jsx(DataTable, { rows: group.bids, columns: columns, isLoading: false, emptyLabel: "", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle }) }))] }, group.id));
155
+ })] }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Bid Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })), allId: "" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
126
156
  }
@@ -0,0 +1,5 @@
1
+ export interface SellerBundlesViewProps {
2
+ onCreateClick?: () => void;
3
+ onBulkDelete?: (ids: string[]) => Promise<void>;
4
+ }
5
+ export declare function SellerBundlesView({ onCreateClick, onBulkDelete, }: SellerBundlesViewProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,104 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useCallback } from "react";
4
+ import { Plus } from "lucide-react";
5
+ import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
+ import { BulkActionBar, Button, DataTable, Div, ListingToolbar, Pagination, Text, } from "../../../ui";
8
+ import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
+ import { ROUTES } from "../../..";
10
+ import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
11
+ import { TABLE_KEYS } from "../../../constants/table-keys";
12
+ const PAGE_SIZE = 25;
13
+ const DEFAULT_SORT = "-createdAt";
14
+ const SORT_OPTIONS = [
15
+ { value: "-createdAt", label: "Newest" },
16
+ { value: "createdAt", label: "Oldest" },
17
+ { value: "productTitle", label: "Name A–Z" },
18
+ { value: "-productTitle", label: "Name Z–A" },
19
+ { value: "price", label: "Price: Low–High" },
20
+ { value: "-price", label: "Price: High–Low" },
21
+ ];
22
+ const COLUMNS = [
23
+ {
24
+ key: "title",
25
+ header: "Bundle",
26
+ render: (row) => _jsx(Text, { className: "text-sm font-medium", children: row.title }),
27
+ },
28
+ {
29
+ key: "price",
30
+ header: "Price",
31
+ render: (row) => _jsx(Text, { className: "text-sm tabular-nums", children: row.price }),
32
+ },
33
+ {
34
+ key: "itemCount",
35
+ header: "Items",
36
+ render: (row) => (_jsx("span", { className: "inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium bg-violet-100 text-violet-700 dark:bg-violet-900/40 dark:text-violet-300 tabular-nums", children: row.itemCount })),
37
+ },
38
+ {
39
+ key: "status",
40
+ header: "Status",
41
+ render: (row) => (_jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize ${row.status === "active"
42
+ ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"
43
+ : "bg-zinc-100 text-zinc-600 dark:bg-slate-800 dark:text-slate-400"}`, children: row.status })),
44
+ },
45
+ {
46
+ key: "createdAt",
47
+ header: "Created",
48
+ render: (row) => _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.createdAt }),
49
+ },
50
+ ];
51
+ export function SellerBundlesView({ onCreateClick, onBulkDelete, }) {
52
+ const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
53
+ const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
54
+ const [view] = useState("table");
55
+ const commitSearch = useCallback(() => {
56
+ table.set(TABLE_KEYS.QUERY, searchInput.trim());
57
+ }, [searchInput, table]);
58
+ const resetAll = useCallback(() => {
59
+ table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
60
+ setSearchInput("");
61
+ }, [table]);
62
+ const hasActiveState = !!table.get(TABLE_KEYS.QUERY) || table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT;
63
+ const { rows, total, isLoading, errorMessage } = useSellerListingData({
64
+ queryKey: ["seller", "bundles"],
65
+ endpoint: SELLER_ENDPOINTS.PRODUCTS,
66
+ page: table.getNumber(TABLE_KEYS.PAGE, 1),
67
+ pageSize: PAGE_SIZE,
68
+ sorts: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
69
+ filters: "listingType==bundle",
70
+ q: table.get(TABLE_KEYS.QUERY) || undefined,
71
+ mapRows: (response) => toRecordArray(response.products).map((item, index) => ({
72
+ id: toStringValue(item.id, `bundle-${index}`),
73
+ raw: item,
74
+ title: toStringValue(item.productTitle ?? item.title, "Untitled bundle"),
75
+ price: toRupees(item.price),
76
+ itemCount: Array.isArray(item.bundleProductIds)
77
+ ? item.bundleProductIds.length
78
+ : Number(item.bundleItemCount ?? 0),
79
+ status: toStringValue(item.status, "draft"),
80
+ createdAt: toRelativeDate(item.createdAt),
81
+ })),
82
+ getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
83
+ });
84
+ const currentPage = table.getNumber(TABLE_KEYS.PAGE, 1);
85
+ const totalPages = Math.ceil(total / PAGE_SIZE);
86
+ const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
87
+ const bulkActions = onBulkDelete
88
+ ? [{
89
+ id: "bulk-delete",
90
+ label: "Delete selected",
91
+ variant: "danger",
92
+ onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
93
+ }]
94
+ : [];
95
+ const handleCreate = useCallback(() => {
96
+ if (onCreateClick) {
97
+ onCreateClick();
98
+ }
99
+ else {
100
+ window.location.href = String(ROUTES.STORE.PRODUCTS_NEW);
101
+ }
102
+ }, [onCreateClick]);
103
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search bundles...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, showTableView: true, view: view, onViewChange: () => { }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: handleCreate, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: "New Bundle" })] }) }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), isLoading ? (_jsx(Div, { className: "space-y-2", children: Array.from({ length: 5 }).map((_, i) => (_jsx(Div, { className: "h-14 animate-pulse rounded-xl border border-zinc-100 dark:border-slate-700 bg-zinc-50 dark:bg-slate-800" }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No bundles yet \u2014 create a bundle to group multiple products together" }) })) : (_jsx(DataTable, { columns: COLUMNS, data: rows, keyExtractor: (r) => r.id, selectable: bulkActions.length > 0, selectedIds: selection.selectedIds, onSelectionChange: (ids) => selection.setSelectedIds(ids) }))] })] }));
104
+ }
@@ -0,0 +1,5 @@
1
+ export interface SellerClassifiedViewProps {
2
+ onCreateClick?: () => void;
3
+ onBulkDelete?: (ids: string[]) => Promise<void>;
4
+ }
5
+ export declare function SellerClassifiedView({ onCreateClick, onBulkDelete, }: SellerClassifiedViewProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,113 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useCallback } from "react";
4
+ import { Plus } from "lucide-react";
5
+ import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
+ import { BulkActionBar, Button, DataTable, Div, ListingToolbar, Pagination, Text, } from "../../../ui";
8
+ import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
+ import { ROUTES } from "../../..";
10
+ import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
11
+ import { TABLE_KEYS } from "../../../constants/table-keys";
12
+ const PAGE_SIZE = 25;
13
+ const DEFAULT_SORT = "-createdAt";
14
+ const SORT_OPTIONS = [
15
+ { value: "-createdAt", label: "Newest" },
16
+ { value: "createdAt", label: "Oldest" },
17
+ { value: "productTitle", label: "Name A–Z" },
18
+ { value: "price", label: "Price: Low–High" },
19
+ { value: "-price", label: "Price: High–Low" },
20
+ ];
21
+ const COLUMNS = [
22
+ {
23
+ key: "title",
24
+ header: "Listing",
25
+ render: (row) => _jsx(Text, { className: "text-sm font-medium", children: row.title }),
26
+ },
27
+ {
28
+ key: "price",
29
+ header: "Price",
30
+ render: (row) => _jsx(Text, { className: "text-sm tabular-nums", children: row.price }),
31
+ },
32
+ {
33
+ key: "city",
34
+ header: "Location",
35
+ render: (row) => (_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.city || "—" })),
36
+ },
37
+ {
38
+ key: "acceptsShipping",
39
+ header: "Shipping",
40
+ render: (row) => (_jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${row.acceptsShipping
41
+ ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"
42
+ : "bg-zinc-100 text-zinc-600 dark:bg-slate-800 dark:text-slate-400"}`, children: row.acceptsShipping ? "Ships" : "Meetup only" })),
43
+ },
44
+ {
45
+ key: "status",
46
+ header: "Status",
47
+ render: (row) => (_jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize ${row.status === "active"
48
+ ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"
49
+ : "bg-zinc-100 text-zinc-600 dark:bg-slate-800 dark:text-slate-400"}`, children: row.status })),
50
+ },
51
+ {
52
+ key: "createdAt",
53
+ header: "Created",
54
+ render: (row) => _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.createdAt }),
55
+ },
56
+ ];
57
+ export function SellerClassifiedView({ onCreateClick, onBulkDelete, }) {
58
+ const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
59
+ const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
60
+ const [view] = useState("table");
61
+ const commitSearch = useCallback(() => {
62
+ table.set(TABLE_KEYS.QUERY, searchInput.trim());
63
+ }, [searchInput, table]);
64
+ const resetAll = useCallback(() => {
65
+ table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
66
+ setSearchInput("");
67
+ }, [table]);
68
+ const hasActiveState = !!table.get(TABLE_KEYS.QUERY) || table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT;
69
+ const { rows, total, isLoading, errorMessage } = useSellerListingData({
70
+ queryKey: ["seller", "classified"],
71
+ endpoint: SELLER_ENDPOINTS.PRODUCTS,
72
+ page: table.getNumber(TABLE_KEYS.PAGE, 1),
73
+ pageSize: PAGE_SIZE,
74
+ sorts: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
75
+ filters: "listingType==classified",
76
+ q: table.get(TABLE_KEYS.QUERY) || undefined,
77
+ mapRows: (response) => toRecordArray(response.products).map((item, index) => {
78
+ const classified = (item.classified ?? {});
79
+ const meetupArea = (classified.meetupArea ?? {});
80
+ return {
81
+ id: toStringValue(item.id, `classified-${index}`),
82
+ raw: item,
83
+ title: toStringValue(item.productTitle ?? item.title, "Untitled"),
84
+ price: toRupees(item.price),
85
+ city: toStringValue(meetupArea.city ?? classified.city, ""),
86
+ acceptsShipping: Boolean(classified.acceptsShipping),
87
+ status: toStringValue(item.status, "draft"),
88
+ createdAt: toRelativeDate(item.createdAt),
89
+ };
90
+ }),
91
+ getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
92
+ });
93
+ const currentPage = table.getNumber(TABLE_KEYS.PAGE, 1);
94
+ const totalPages = Math.ceil(total / PAGE_SIZE);
95
+ const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
96
+ const bulkActions = onBulkDelete
97
+ ? [{
98
+ id: "bulk-delete",
99
+ label: "Delete selected",
100
+ variant: "danger",
101
+ onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
102
+ }]
103
+ : [];
104
+ const handleCreate = useCallback(() => {
105
+ if (onCreateClick) {
106
+ onCreateClick();
107
+ }
108
+ else {
109
+ window.location.href = String(ROUTES.STORE.CLASSIFIED_NEW);
110
+ }
111
+ }, [onCreateClick]);
112
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search classified listings...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, showTableView: true, view: view, onViewChange: () => { }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: handleCreate, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: "New Classified" })] }) }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), isLoading ? (_jsx(Div, { className: "space-y-2", children: Array.from({ length: 5 }).map((_, i) => (_jsx(Div, { className: "h-14 animate-pulse rounded-xl border border-zinc-100 dark:border-slate-700 bg-zinc-50 dark:bg-slate-800" }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No classified listings yet \u2014 post your first buy/sell/trade ad" }) })) : (_jsx(DataTable, { columns: COLUMNS, data: rows, keyExtractor: (r) => r.id, selectable: bulkActions.length > 0, selectedIds: selection.selectedIds, onSelectionChange: (ids) => selection.setSelectedIds(ids) }))] })] }));
113
+ }
@@ -1,11 +1,23 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { StackedViewShell } from "../../../ui";
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { StackedViewShell, Div, Text } from "../../../ui";
4
+ function DefaultStatsPlaceholder({ isLoading }) {
5
+ return (_jsx("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-4", children: [1, 2, 3, 4].map((i) => (_jsx(Div, { className: "rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] p-5", children: isLoading ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "h-3 w-16 animate-pulse rounded bg-[var(--appkit-color-border)]" }), _jsx("div", { className: "mt-3 h-6 w-20 animate-pulse rounded bg-[var(--appkit-color-border)]" })] })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { size: "xs", variant: "secondary", className: "uppercase tracking-widest font-semibold", children: ["Stat ", i] }), _jsx(Text, { size: "xl", className: "mt-2 font-bold tabular-nums", children: "\u2014" })] })) }, i))) }));
6
+ }
7
+ function DefaultQuickActionsPlaceholder() {
8
+ return (_jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-4", children: [1, 2, 3, 4].map((i) => (_jsx(Div, { className: "h-12 animate-pulse rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-border)]" }, i))) }));
9
+ }
3
10
  export function SellerDashboardView({ labels = {}, renderStats, renderQuickActions, renderRecentListings, renderRevenueChart, renderTopProducts, isLoading = false, ...rest }) {
11
+ const stats = (renderStats ?? ((busy) => _jsx(DefaultStatsPlaceholder, { isLoading: busy })))(isLoading);
12
+ const quickActions = (renderQuickActions ?? (() => _jsx(DefaultQuickActionsPlaceholder, {})))();
13
+ const revenueChart = (renderRevenueChart ?? (() => null))();
14
+ const topProducts = (renderTopProducts ?? (() => null))();
15
+ const recentListings = (renderRecentListings ?? (() => null))();
4
16
  return (_jsx(StackedViewShell, { portal: "seller", ...rest, title: labels.title, sections: [
5
- renderStats?.(isLoading),
6
- renderQuickActions?.(),
7
- renderRevenueChart?.(),
8
- renderTopProducts?.(),
9
- renderRecentListings?.(),
17
+ stats,
18
+ quickActions,
19
+ revenueChart,
20
+ topProducts,
21
+ recentListings,
10
22
  ] }));
11
23
  }
@@ -0,0 +1,5 @@
1
+ export interface SellerDigitalCodesViewProps {
2
+ onCreateClick?: () => void;
3
+ onBulkDelete?: (ids: string[]) => Promise<void>;
4
+ }
5
+ export declare function SellerDigitalCodesView({ onCreateClick, onBulkDelete, }: SellerDigitalCodesViewProps): import("react/jsx-runtime").JSX.Element;