@mohasinac/appkit 2.7.50 → 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 (143) hide show
  1. package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.d.ts +1 -0
  2. package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.js +65 -0
  3. package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.d.ts +1 -0
  4. package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.js +163 -0
  5. package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.d.ts +1 -0
  6. package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.js +115 -0
  7. package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.d.ts +1 -0
  8. package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.js +96 -0
  9. package/dist/client.d.ts +29 -6
  10. package/dist/client.js +16 -4
  11. package/dist/constants/api-endpoints.d.ts +42 -0
  12. package/dist/constants/api-endpoints.js +14 -0
  13. package/dist/features/_guide-cls.d.ts +13 -0
  14. package/dist/features/_guide-cls.js +14 -0
  15. package/dist/features/about/components/HelpPageView.js +29 -26
  16. package/dist/features/account/components/BuyerAccountGuideView.d.ts +1 -0
  17. package/dist/features/account/components/BuyerAccountGuideView.js +39 -0
  18. package/dist/features/account/components/BuyerAuctionsGuideView.d.ts +1 -0
  19. package/dist/features/account/components/BuyerAuctionsGuideView.js +38 -0
  20. package/dist/features/account/components/BuyerOrdersGuideView.d.ts +1 -0
  21. package/dist/features/account/components/BuyerOrdersGuideView.js +48 -0
  22. package/dist/features/account/components/BuyerShoppingGuideView.d.ts +1 -0
  23. package/dist/features/account/components/BuyerShoppingGuideView.js +38 -0
  24. package/dist/features/admin/components/AdminAnalyticsGuideView.d.ts +1 -0
  25. package/dist/features/admin/components/AdminAnalyticsGuideView.js +24 -0
  26. package/dist/features/admin/components/AdminBlogEditorView.js +100 -77
  27. package/dist/features/admin/components/AdminBundleEditorView.js +20 -14
  28. package/dist/features/admin/components/AdminBundlesView.d.ts +1 -3
  29. package/dist/features/admin/components/AdminBundlesView.js +173 -52
  30. package/dist/features/admin/components/AdminCatalogGuideView.d.ts +1 -0
  31. package/dist/features/admin/components/AdminCatalogGuideView.js +28 -0
  32. package/dist/features/admin/components/AdminContentGuideView.d.ts +1 -0
  33. package/dist/features/admin/components/AdminContentGuideView.js +36 -0
  34. package/dist/features/admin/components/AdminCouponEditorView.js +1 -1
  35. package/dist/features/admin/components/AdminGuideHubView.d.ts +5 -0
  36. package/dist/features/admin/components/AdminGuideHubView.js +79 -0
  37. package/dist/features/admin/components/AdminOrdersGuideView.d.ts +1 -0
  38. package/dist/features/admin/components/AdminOrdersGuideView.js +44 -0
  39. package/dist/features/admin/components/AdminProductsView.js +8 -3
  40. package/dist/features/admin/components/AdminSiteConfigGuideView.d.ts +1 -0
  41. package/dist/features/admin/components/AdminSiteConfigGuideView.js +21 -0
  42. package/dist/features/admin/components/AdminStoresGuideView.d.ts +1 -0
  43. package/dist/features/admin/components/AdminStoresGuideView.js +32 -0
  44. package/dist/features/admin/components/AdminTeamGuideView.d.ts +1 -0
  45. package/dist/features/admin/components/AdminTeamGuideView.js +33 -0
  46. package/dist/features/admin/components/AdminTrustGuideView.d.ts +1 -0
  47. package/dist/features/admin/components/AdminTrustGuideView.js +40 -0
  48. package/dist/features/admin/components/AdminUsersGuideView.d.ts +1 -0
  49. package/dist/features/admin/components/AdminUsersGuideView.js +38 -0
  50. package/dist/features/categories/components/CategoryBundlesListing.js +3 -33
  51. package/dist/features/categories/components/index.d.ts +0 -2
  52. package/dist/features/categories/components/index.js +0 -1
  53. package/dist/features/events/components/AdminEventEditorView.js +223 -269
  54. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +9 -0
  55. package/dist/features/grouped/repository/grouped-listings.repository.js +12 -0
  56. package/dist/features/media/upload/MediaUploadField.js +1 -1
  57. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +3 -1
  58. package/dist/features/products/components/AuctionsIndexListing.js +3 -1
  59. package/dist/features/products/components/MarketplaceBundleCard.d.ts +21 -0
  60. package/dist/features/products/components/MarketplaceBundleCard.js +56 -0
  61. package/dist/features/products/components/ProductsIndexListing.js +3 -1
  62. package/dist/features/products/components/index.d.ts +2 -4
  63. package/dist/features/products/components/index.js +1 -2
  64. package/dist/features/promotions/schemas/firestore.js +1 -1
  65. package/dist/features/reviews/components/index.d.ts +0 -2
  66. package/dist/features/reviews/components/index.js +0 -1
  67. package/dist/features/seller/components/BrandInlineSelect.js +23 -3
  68. package/dist/features/seller/components/CategoryInlineSelect.js +20 -3
  69. package/dist/features/seller/components/SellerAnalyticsAlertsView.d.ts +6 -0
  70. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +124 -0
  71. package/dist/features/seller/components/SellerAnalyticsView.d.ts +2 -0
  72. package/dist/features/seller/components/SellerAnalyticsView.js +17 -9
  73. package/dist/features/seller/components/SellerBundlesView.d.ts +5 -0
  74. package/dist/features/seller/components/SellerBundlesView.js +104 -0
  75. package/dist/features/seller/components/SellerClassifiedView.d.ts +5 -0
  76. package/dist/features/seller/components/SellerClassifiedView.js +113 -0
  77. package/dist/features/seller/components/SellerDashboardView.js +19 -7
  78. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +5 -0
  79. package/dist/features/seller/components/SellerDigitalCodesView.js +115 -0
  80. package/dist/features/seller/components/SellerGoogleReviewsView.d.ts +14 -0
  81. package/dist/features/seller/components/SellerGoogleReviewsView.js +95 -0
  82. package/dist/features/seller/components/SellerGroupedListingsView.d.ts +6 -0
  83. package/dist/features/seller/components/SellerGroupedListingsView.js +112 -0
  84. package/dist/features/seller/components/SellerLiveView.d.ts +5 -0
  85. package/dist/features/seller/components/SellerLiveView.js +113 -0
  86. package/dist/features/seller/components/SellerOrdersView.js +18 -2
  87. package/dist/features/seller/components/SellerPayoutMethodsView.d.ts +7 -0
  88. package/dist/features/seller/components/SellerPayoutMethodsView.js +120 -0
  89. package/dist/features/seller/components/SellerPayoutSettingsView.js +50 -12
  90. package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
  91. package/dist/features/seller/components/SellerProductShell.js +9 -7
  92. package/dist/features/seller/components/SellerProductsFilterDrawer.js +3 -1
  93. package/dist/features/seller/components/SellerProductsView.js +48 -54
  94. package/dist/features/seller/components/SellerShippingConfigsView.d.ts +7 -0
  95. package/dist/features/seller/components/SellerShippingConfigsView.js +149 -0
  96. package/dist/features/seller/components/SellerShippingView.js +37 -10
  97. package/dist/features/seller/components/SellerStoreCategoriesView.d.ts +9 -0
  98. package/dist/features/seller/components/SellerStoreCategoriesView.js +122 -0
  99. package/dist/features/seller/components/SellerStorefrontView.d.ts +0 -7
  100. package/dist/features/seller/components/SellerStorefrontView.js +26 -29
  101. package/dist/features/seller/components/SellerTemplatesView.d.ts +10 -0
  102. package/dist/features/seller/components/SellerTemplatesView.js +265 -0
  103. package/dist/features/seller/components/index.d.ts +26 -0
  104. package/dist/features/seller/components/index.js +13 -0
  105. package/dist/features/stores/api/[storeSlug]/reviews/route.js +49 -7
  106. package/dist/features/stores/components/StoreCapabilitiesGuideView.d.ts +3 -0
  107. package/dist/features/stores/components/StoreCapabilitiesGuideView.js +101 -0
  108. package/dist/features/stores/components/StoreFinanceGuideView.d.ts +3 -0
  109. package/dist/features/stores/components/StoreFinanceGuideView.js +79 -0
  110. package/dist/features/stores/components/StoreGuideHubView.d.ts +5 -0
  111. package/dist/features/stores/components/StoreGuideHubView.js +89 -0
  112. package/dist/features/stores/components/StoreListingsGuideView.d.ts +3 -0
  113. package/dist/features/stores/components/StoreListingsGuideView.js +151 -0
  114. package/dist/features/stores/components/StoreOrdersGuideView.d.ts +3 -0
  115. package/dist/features/stores/components/StoreOrdersGuideView.js +122 -0
  116. package/dist/features/stores/components/StoreReviewsListing.js +100 -23
  117. package/dist/features/stores/components/StoreSettingsGuideView.d.ts +3 -0
  118. package/dist/features/stores/components/StoreSettingsGuideView.js +56 -0
  119. package/dist/features/stores/components/index.d.ts +12 -1
  120. package/dist/features/stores/components/index.js +6 -1
  121. package/dist/features/stores/hooks/useStores.d.ts +5 -0
  122. package/dist/features/stores/hooks/useStores.js +10 -0
  123. package/dist/features/stores/manifest.js +1 -1
  124. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +13 -2
  125. package/dist/index.d.ts +54 -9
  126. package/dist/index.js +58 -15
  127. package/dist/next/routing/route-map.d.ts +55 -0
  128. package/dist/next/routing/route-map.js +25 -0
  129. package/dist/providers/db-firebase/__tests__/filter-aliases.test.d.ts +1 -0
  130. package/dist/providers/db-firebase/__tests__/filter-aliases.test.js +93 -0
  131. package/dist/providers/db-firebase/sieve.d.ts +49 -0
  132. package/dist/providers/db-firebase/sieve.js +61 -8
  133. package/dist/repositories/index.d.ts +1 -0
  134. package/dist/repositories/index.js +2 -0
  135. package/dist/tailwind-utilities.css +1 -1
  136. package/dist/ui/components/ListingToolbar.d.ts +8 -1
  137. package/dist/ui/components/ListingToolbar.js +4 -2
  138. package/dist/ui/components/__tests__/BulkActionBar.test.d.ts +1 -0
  139. package/dist/ui/components/__tests__/BulkActionBar.test.js +96 -0
  140. package/dist/ui/components/__tests__/ListingToolbar.test.d.ts +1 -0
  141. package/dist/ui/components/__tests__/ListingToolbar.test.js +125 -0
  142. package/dist/ui/index.d.ts +1 -1
  143. package/package.json +8 -2
@@ -0,0 +1,9 @@
1
+ import { BaseRepository } from "../../../providers/db-firebase";
2
+ import { type GroupedListingDocument } from "../schemas/firestore";
3
+ export declare class GroupedListingsRepository extends BaseRepository<GroupedListingDocument> {
4
+ constructor();
5
+ listByStore(storeId: string): Promise<{
6
+ items: GroupedListingDocument[];
7
+ }>;
8
+ }
9
+ export declare const groupedListingsRepository: GroupedListingsRepository;
@@ -0,0 +1,12 @@
1
+ import { BaseRepository } from "../../../providers/db-firebase";
2
+ import { GROUPED_LISTINGS_COLLECTION, } from "../schemas/firestore";
3
+ export class GroupedListingsRepository extends BaseRepository {
4
+ constructor() {
5
+ super(GROUPED_LISTINGS_COLLECTION);
6
+ }
7
+ async listByStore(storeId) {
8
+ const items = await this.findBy("storeId", storeId);
9
+ return { items };
10
+ }
11
+ }
12
+ export const groupedListingsRepository = new GroupedListingsRepository();
@@ -78,7 +78,7 @@ export function MediaUploadField({ label, value, onChange, onChangeField, onUplo
78
78
  // SB-UNI-Z5 2026-05-13 — `kind` derives accept/maxSizeMB when not set
79
79
  // explicitly; explicit props still win. "*" + 50 stay as the floor for
80
80
  // back-compat with callers that pass neither.
81
- kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText, captureSource = "both", captureMode = "photo", enableTrim = true, enableThumbnail = true, onThumbnailChange, showYoutube = false, showExternal = false, onAbort, onStagedUrlsChange, isPersisted = false, }) {
81
+ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText, captureSource = "both", captureMode = "photo", enableTrim = true, enableThumbnail = true, onThumbnailChange, showYoutube = true, showExternal = true, onAbort, onStagedUrlsChange, isPersisted = false, }) {
82
82
  const [isLoading, setIsLoading] = useState(false);
83
83
  const [error, setError] = useState(null);
84
84
  const [pendingVideoUrl, setPendingVideoUrl] = useState(null);
@@ -129,7 +129,9 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
129
129
  }, [localCart, showToast]);
130
130
  const selection = useBulkSelection({ items: preOrders, keyExtractor: (p) => p.id });
131
131
  const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
132
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PREORDER_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: preOrders.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show closed" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showClosed, onClick: () => table.set(TABLE_KEYS.SHOW_CLOSED, showClosed ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showClosed ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showClosed ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
132
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PREORDER_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: preOrders.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, toggles: [
133
+ { label: "Show closed", active: showClosed, onChange: (next) => table.set(TABLE_KEYS.SHOW_CLOSED, next ? "true" : "") },
134
+ ] }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
133
135
  {
134
136
  id: ACTION_ID.ADD_TO_CART,
135
137
  label: ACTION_META[ACTION_ID.ADD_TO_CART].label,
@@ -111,7 +111,9 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
111
111
  isWishlisted: (productId) => wishlistedIds.has(productId),
112
112
  };
113
113
  const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-3 gap-4";
114
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: AUCTION_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: auctions.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show ended" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showEnded, onClick: () => table.set(TABLE_KEYS.SHOW_ENDED, showEnded ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showEnded ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showEnded ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
114
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: AUCTION_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: auctions.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, toggles: [
115
+ { label: "Show ended", active: showEnded, onChange: (next) => table.set(TABLE_KEYS.SHOW_ENDED, next ? "true" : "") },
116
+ ] }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
115
117
  {
116
118
  id: ACTION_ID.WATCH_AUCTION,
117
119
  label: "Add to Watchlist",
@@ -0,0 +1,21 @@
1
+ import type { CategoryDocument } from "../../categories/schemas/firestore";
2
+ export type MarketplaceBundleCardData = Pick<CategoryDocument, "id" | "name" | "slug" | "bundleItemDetails" | "bundleProductIds" | "bundlePriceInPaise" | "bundleStockStatus" | "display">;
3
+ export interface MarketplaceBundleCardLabels {
4
+ bundleBadge?: string;
5
+ inStockBadge?: string;
6
+ outOfStockBadge?: string;
7
+ itemsLabel?: (count: number) => string;
8
+ }
9
+ export interface MarketplaceBundleCardProps {
10
+ bundle: MarketplaceBundleCardData;
11
+ className?: string;
12
+ variant?: "grid" | "list";
13
+ selectable?: boolean;
14
+ isSelected?: boolean;
15
+ onSelect?: (id: string, selected: boolean) => void;
16
+ href?: string;
17
+ hrefBuilder?: (bundle: MarketplaceBundleCardData) => string;
18
+ onNavigate?: (href: string) => void;
19
+ labels?: MarketplaceBundleCardLabels;
20
+ }
21
+ export declare function MarketplaceBundleCard({ bundle, className, variant, selectable, isSelected, onSelect, href, hrefBuilder, onNavigate, labels, }: MarketplaceBundleCardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback } from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import { useLongPress } from "../../../react/hooks/useLongPress";
6
+ import { ROUTES } from "../../../next";
7
+ import { formatCurrency } from "../../../utils";
8
+ import { getDefaultCurrency } from "../../../core/baseline-resolver";
9
+ import { BaseListingCard, Div, Row, Span, Text, TextLink, } from "../../../ui";
10
+ import { THEME_CONSTANTS } from "../../../tokens";
11
+ const DEFAULT_LABELS = {
12
+ bundleBadge: "Bundle",
13
+ inStockBadge: "In stock",
14
+ outOfStockBadge: "Out of stock",
15
+ itemsLabel: (count) => `${count} item${count !== 1 ? "s" : ""}`,
16
+ };
17
+ function resolveHref(bundle, href, hrefBuilder) {
18
+ if (href)
19
+ return href;
20
+ if (hrefBuilder)
21
+ return hrefBuilder(bundle);
22
+ return ROUTES.PUBLIC.BUNDLE_DETAIL(bundle.slug);
23
+ }
24
+ export function MarketplaceBundleCard({ bundle, className = "", variant = "grid", selectable = false, isSelected = false, onSelect, href, hrefBuilder, onNavigate, labels, }) {
25
+ const router = useRouter();
26
+ const mergedLabels = { ...DEFAULT_LABELS, ...labels };
27
+ const detailHref = resolveHref(bundle, href, hrefBuilder);
28
+ const longPress = useLongPress(() => onSelect?.(bundle.id, !isSelected));
29
+ const memberCount = bundle.bundleProductIds?.length ?? 0;
30
+ const stock = bundle.bundleStockStatus ?? "in_stock";
31
+ const cover = bundle.display?.coverImage;
32
+ const price = bundle.bundlePriceInPaise;
33
+ const collageTiles = (bundle.bundleItemDetails ?? [])
34
+ .filter((d) => Boolean(d.imageURL))
35
+ .slice(0, 4);
36
+ const showCollage = collageTiles.length >= 2;
37
+ const overflow = memberCount - collageTiles.length;
38
+ const handleNavigate = useCallback(() => {
39
+ if (onNavigate) {
40
+ onNavigate(String(detailHref));
41
+ return;
42
+ }
43
+ router.push(String(detailHref));
44
+ }, [detailHref, onNavigate, router]);
45
+ return (_jsxs(BaseListingCard, { isSelected: isSelected, variant: variant, className: className, onMouseDown: !isSelected ? longPress.onMouseDown : undefined, onMouseUp: !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: !isSelected ? longPress.onTouchEnd : undefined, children: [_jsxs(BaseListingCard.Hero, { aspect: "square", variant: variant, children: [_jsx(TextLink, { href: String(detailHref), className: "absolute inset-0 block", children: showCollage ? (_jsx(Div, { className: `grid h-full w-full gap-0.5 bg-[var(--appkit-color-surface-muted)] ${collageTiles.length === 2 ? "grid-cols-2 grid-rows-1" : "grid-cols-2 grid-rows-2"}`, children: collageTiles.map((tile, i) => (_jsxs(Div, { className: "relative overflow-hidden bg-[var(--appkit-color-surface-muted)]", children: [_jsx("img", { src: tile.imageURL, alt: tile.title ?? `${bundle.name} item ${i + 1}`, loading: "lazy", className: "absolute inset-0 h-full w-full object-cover" }), i === collageTiles.length - 1 && overflow > 0 && (_jsxs(Div, { className: "absolute inset-0 flex items-center justify-center bg-black/55 text-sm font-semibold text-white", children: ["+", overflow] }))] }, `${tile.productId}-${i}`))) })) : cover ? (_jsx(Div, { className: "absolute inset-0 bg-[var(--appkit-color-surface-muted)]", children: _jsx("img", { src: cover, alt: bundle.name, loading: "lazy", className: "h-full w-full object-cover" }) })) : (_jsx(Div, { className: "absolute inset-0 flex items-center justify-center bg-[var(--appkit-color-surface-muted)]", children: _jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)]", children: "No image" }) })) }), _jsxs(Div, { className: "absolute right-2 top-2 flex flex-col items-end gap-1", children: [_jsx(Span, { className: "inline-flex items-center rounded-full bg-violet-600 px-2 py-0.5 text-xs font-medium text-white", children: mergedLabels.bundleBadge }), _jsx(Span, { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${stock === "out_of_stock"
46
+ ? "bg-zinc-500 text-white"
47
+ : "bg-emerald-600 text-white"}`, children: stock === "out_of_stock" ? mergedLabels.outOfStockBadge : mergedLabels.inStockBadge })] }), stock === "out_of_stock" && (_jsx(Div, { className: "absolute inset-0 flex items-center justify-center bg-black/40", children: _jsx(Span, { className: "rounded bg-zinc-900/80 px-3 py-1 text-xs font-bold uppercase tracking-wider text-white", children: mergedLabels.outOfStockBadge }) })), onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (event) => {
48
+ event.stopPropagation();
49
+ onSelect(bundle.id, !isSelected);
50
+ }, className: selectable || isSelected
51
+ ? "opacity-100"
52
+ : "opacity-0 group-hover:opacity-100 transition-opacity" }))] }), _jsxs(BaseListingCard.Info, { variant: variant, children: [_jsx(TextLink, { href: String(detailHref), children: _jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-medium text-zinc-900 dark:text-zinc-100`, children: bundle.name }) }), _jsxs(Row, { justify: "between", className: "mt-1 gap-2", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: price
53
+ ? formatCurrency(price / 100, getDefaultCurrency())
54
+ : "—" }), _jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)]", children: mergedLabels.itemsLabel(memberCount) })] }), _jsx("div", { className: "mt-2 w-full cursor-pointer rounded-md bg-violet-600 py-1.5 text-center text-xs font-semibold text-white transition-colors hover:bg-violet-700 active:scale-[0.98]", role: "button", tabIndex: 0, onClick: handleNavigate, onKeyDown: (e) => { if (e.key === "Enter" || e.key === " ")
55
+ handleNavigate(); }, children: "View Bundle" })] })] }));
56
+ }
@@ -132,7 +132,9 @@ export function ProductsIndexListing({ initialData }) {
132
132
  router.push(String(ROUTES.USER.CART));
133
133
  });
134
134
  }, [localCart, router, requireAuth]);
135
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search products...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PRODUCT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show sold" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showSold, onClick: () => table.set(TABLE_KEYS.SHOW_SOLD, showSold ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showSold ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showSold ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
135
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search products...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PRODUCT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, toggles: [
136
+ { label: "Show sold", active: showSold, onChange: (next) => table.set(TABLE_KEYS.SHOW_SOLD, next ? "true" : "") },
137
+ ] }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
136
138
  {
137
139
  id: ACTION_ID.ADD_TO_CART,
138
140
  label: ACTION_META[ACTION_ID.ADD_TO_CART].label,
@@ -15,6 +15,8 @@ export { PrizeRevealModal } from "./PrizeRevealModal";
15
15
  export type { PrizeRevealModalProps, PrizeRevealResponse, } from "./PrizeRevealModal";
16
16
  export { MarketplacePrizeDrawCard } from "./MarketplacePrizeDrawCard";
17
17
  export type { MarketplacePrizeDrawCardProps, MarketplacePrizeDrawCardData, MarketplacePrizeDrawCardLabels, } from "./MarketplacePrizeDrawCard";
18
+ export { MarketplaceBundleCard } from "./MarketplaceBundleCard";
19
+ export type { MarketplaceBundleCardProps, MarketplaceBundleCardData, MarketplaceBundleCardLabels, } from "./MarketplaceBundleCard";
18
20
  export { PrizeDrawsIndexListing } from "./PrizeDrawsIndexListing";
19
21
  export type { PrizeDrawsIndexListingProps } from "./PrizeDrawsIndexListing";
20
22
  export { PrizeDrawEntryActions } from "./PrizeDrawEntryActions";
@@ -30,14 +32,10 @@ export { BuyBar } from "./BuyBar";
30
32
  export type { BuyBarProps } from "./BuyBar";
31
33
  export { ProductsView } from "./ProductsView";
32
34
  export type { ProductsViewProps } from "./ProductsView";
33
- export { AuctionsView } from "./AuctionsView";
34
- export type { AuctionsViewProps } from "./AuctionsView";
35
35
  export { ProductDetailView } from "./ProductDetailView";
36
36
  export type { ProductDetailViewProps } from "./ProductDetailView";
37
37
  export { AuctionDetailView } from "./AuctionDetailView";
38
38
  export type { AuctionDetailViewProps } from "./AuctionDetailView";
39
- export { PreOrdersView } from "./PreOrdersView";
40
- export type { PreOrdersViewProps } from "./PreOrdersView";
41
39
  export { PreOrderDetailView } from "./PreOrderDetailView";
42
40
  export type { PreOrderDetailViewProps } from "./PreOrderDetailView";
43
41
  export { ProductInfo } from "./ProductInfo";
@@ -7,6 +7,7 @@ export { PrizeDrawItemsEditor } from "./PrizeDrawItemsEditor";
7
7
  export { PrizeDrawCollage } from "./PrizeDrawCollage";
8
8
  export { PrizeRevealModal } from "./PrizeRevealModal";
9
9
  export { MarketplacePrizeDrawCard } from "./MarketplacePrizeDrawCard";
10
+ export { MarketplaceBundleCard } from "./MarketplaceBundleCard";
10
11
  export { PrizeDrawsIndexListing } from "./PrizeDrawsIndexListing";
11
12
  // PrizeDrawsListingView + PrizeDrawDetailPageView are server components that
12
13
  // import productRepository (→ firebase-admin). Re-exporting them from this
@@ -20,10 +21,8 @@ export { FeatureBadge, FeatureBadgeList } from "./FeatureBadge";
20
21
  export { ProductFeaturesProvider, useProductFeatures, } from "./ProductFeaturesContext";
21
22
  export { BuyBar } from "./BuyBar";
22
23
  export { ProductsView } from "./ProductsView";
23
- export { AuctionsView } from "./AuctionsView";
24
24
  export { ProductDetailView } from "./ProductDetailView";
25
25
  export { AuctionDetailView } from "./AuctionDetailView";
26
- export { PreOrdersView } from "./PreOrdersView";
27
26
  export { PreOrderDetailView } from "./PreOrderDetailView";
28
27
  export { ProductInfo } from "./ProductInfo";
29
28
  export { ProductTabs } from "./ProductTabs";
@@ -61,7 +61,7 @@ export const COUPON_TYPE_LABELS = {
61
61
  };
62
62
  export const DEFAULT_COUPON_DATA = {
63
63
  usage: { currentUsage: 0 },
64
- validity: { isActive: false, startDate: new Date(), endDate: undefined },
64
+ validity: { isActive: true, startDate: new Date(), endDate: undefined },
65
65
  restrictions: { firstTimeUserOnly: false, combineWithSellerCoupons: false },
66
66
  stats: { totalUses: 0, totalRevenue: 0, totalDiscount: 0 },
67
67
  };
@@ -6,8 +6,6 @@ export type { ReviewSummaryProps } from "./ReviewSummary";
6
6
  export { ReviewSummary } from "./ReviewSummary";
7
7
  export type { ViewReviewModalProps } from "./ReviewModal";
8
8
  export { ViewReviewModal } from "./ReviewModal";
9
- export { ReviewsListView } from "./ReviewsListView";
10
- export type { ReviewsListViewProps } from "./ReviewsListView";
11
9
  export { ReviewsIndexListing } from "./ReviewsIndexListing";
12
10
  export type { ReviewsIndexListingProps } from "./ReviewsIndexListing";
13
11
  export type { ReviewDetailPageViewProps } from "./ReviewDetailPageView";
@@ -2,5 +2,4 @@ export { ReviewCard, ReviewsList } from "./ReviewsList";
2
2
  export { ReviewFilters, REVIEW_SORT_OPTIONS, REVIEW_FILTER_KEYS, REVIEW_ADMIN_SORT_OPTIONS, REVIEW_SELLER_SORT_OPTIONS, REVIEW_PUBLIC_SORT_OPTIONS, getReviewFilterKeys, getReviewSortOptions, } from "./ReviewFilters";
3
3
  export { ReviewSummary } from "./ReviewSummary";
4
4
  export { ViewReviewModal } from "./ReviewModal";
5
- export { ReviewsListView } from "./ReviewsListView";
6
5
  export { ReviewsIndexListing } from "./ReviewsIndexListing";
@@ -4,14 +4,16 @@ import { InlineCreateSelect } from "../../../ui/components/InlineCreateSelect";
4
4
  import { BrandQuickCreateForm } from "../../admin/components/BrandQuickCreateForm";
5
5
  import { apiClient } from "../../../http";
6
6
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
7
- async function loadBrandOptions(query, page) {
7
+ // Admin: paginated search via /api/admin/brands (admin/mod only).
8
+ // Response via successResponse({ data: items, total }) → apiClient unwraps to { data, total }.
9
+ async function loadAdminBrandOptions(query, page) {
8
10
  const params = new URLSearchParams({
9
11
  q: query,
10
12
  page: String(page),
11
13
  pageSize: "20",
12
14
  });
13
15
  const res = await apiClient.get(`${ADMIN_ENDPOINTS.BRANDS}?${params}`);
14
- const raw = res.brands ?? res.items ?? [];
16
+ const raw = res.data ?? res.items ?? [];
15
17
  const items = raw.map((b) => ({
16
18
  value: String(b.id ?? ""),
17
19
  label: String(b.name ?? ""),
@@ -19,8 +21,26 @@ async function loadBrandOptions(query, page) {
19
21
  const total = res.total ?? items.length;
20
22
  return { items, hasMore: page * 20 < total, nextPage: page + 1 };
21
23
  }
24
+ // Seller/public: unauthenticated /api/brands endpoint returns { items, total }.
25
+ // Client-side query filtering so all sellers can access the brand list.
26
+ async function loadPublicBrandOptions(query, page) {
27
+ const params = new URLSearchParams({
28
+ page: String(page),
29
+ pageSize: "100",
30
+ active: "true",
31
+ });
32
+ const res = await apiClient.get(`/api/brands?${params}`);
33
+ const all = (res.items ?? []).map((b) => ({
34
+ value: String(b.id ?? ""),
35
+ label: String(b.name ?? ""),
36
+ }));
37
+ const q = query.trim().toLowerCase();
38
+ const items = q ? all.filter((b) => b.label.toLowerCase().includes(q)) : all;
39
+ return { items, hasMore: false, nextPage: page + 1 };
40
+ }
22
41
  export function BrandInlineSelect({ value, onChange, placeholder = "Search brands…", disabled, allowCreate = true, }) {
23
- return (_jsx(InlineCreateSelect, { value: value || null, onChange: (v) => onChange(v ?? ""), loadOptions: loadBrandOptions, placeholder: placeholder, disabled: disabled, createLabel: "brand", drawerTitle: "Create Brand", renderCreateForm: allowCreate
42
+ const loadOptions = allowCreate ? loadAdminBrandOptions : loadPublicBrandOptions;
43
+ return (_jsx(InlineCreateSelect, { value: value || null, onChange: (v) => onChange(v ?? ""), loadOptions: loadOptions, placeholder: placeholder, disabled: disabled, createLabel: "brand", drawerTitle: "Create Brand", renderCreateForm: allowCreate
24
44
  ? ({ onCreated, onCancel }) => (_jsx(BrandQuickCreateForm, { onSaved: (id, name) => onCreated({ value: id, label: name }), onCancel: onCancel }))
25
45
  : undefined }));
26
46
  }
@@ -4,7 +4,9 @@ import { InlineCreateSelect } from "../../../ui/components/InlineCreateSelect";
4
4
  import { CategoryQuickCreateForm } from "../../admin/components/CategoryQuickCreateForm";
5
5
  import { apiClient } from "../../../http";
6
6
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
7
- async function loadCategoryOptions(query, page) {
7
+ // Admin: paginated + server-side search via /api/admin/categories (admin/mod only).
8
+ // Response via successResponse({ data: items, total }) → apiClient unwraps to { data, total, ... }.
9
+ async function loadAdminCategoryOptions(query, page) {
8
10
  const params = new URLSearchParams({
9
11
  q: query,
10
12
  page: String(page),
@@ -12,15 +14,30 @@ async function loadCategoryOptions(query, page) {
12
14
  flat: "true",
13
15
  });
14
16
  const res = await apiClient.get(`${ADMIN_ENDPOINTS.CATEGORIES}?${params}`);
15
- const items = (res.items ?? []).map((c) => ({
17
+ const items = (res.data ?? []).map((c) => ({
16
18
  value: String(c.id ?? ""),
17
19
  label: String(c.name ?? ""),
18
20
  }));
19
21
  const total = res.total ?? items.length;
20
22
  return { items, hasMore: page * 20 < total, nextPage: page + 1 };
21
23
  }
24
+ // Seller: fetch all flat categories from the public unauthenticated endpoint,
25
+ // filter client-side by query. Public endpoint returns { success, data: CategoryItem[] }.
26
+ async function loadPublicCategoryOptions(query, _page) {
27
+ const params = new URLSearchParams({ flat: "true", pageSize: "200" });
28
+ const res = await fetch(`/api/categories?${params}`, { credentials: "include" });
29
+ const json = await res.json().catch(() => ({}));
30
+ const all = (json.data ?? []).map((c) => ({
31
+ value: String(c.id ?? ""),
32
+ label: String(c.name ?? ""),
33
+ }));
34
+ const q = query.trim().toLowerCase();
35
+ const items = q ? all.filter((c) => c.label.toLowerCase().includes(q)) : all;
36
+ return { items, hasMore: false, nextPage: 2 };
37
+ }
22
38
  export function CategoryInlineSelect({ value, onChange, placeholder = "Search categories…", disabled, allowCreate = false, }) {
23
- return (_jsx(InlineCreateSelect, { value: value || null, onChange: (v) => onChange(v ?? ""), loadOptions: loadCategoryOptions, placeholder: placeholder, disabled: disabled, createLabel: "category", drawerTitle: "Create Category", renderCreateForm: allowCreate
39
+ const loadOptions = allowCreate ? loadAdminCategoryOptions : loadPublicCategoryOptions;
40
+ return (_jsx(InlineCreateSelect, { value: value || null, onChange: (v) => onChange(v ?? ""), loadOptions: loadOptions, placeholder: placeholder, disabled: disabled, createLabel: "category", drawerTitle: "Create Category", renderCreateForm: allowCreate
24
41
  ? ({ onCreated, onCancel }) => (_jsx(CategoryQuickCreateForm, { onSaved: (id, name) => onCreated({ value: id, label: name }), onCancel: onCancel }))
25
42
  : undefined }));
26
43
  }
@@ -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
  }
@@ -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;