@mohasinac/appkit 2.7.50 → 2.7.53

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,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: "Item",
25
+ render: (row) => (_jsxs(Div, { children: [_jsx(Text, { className: "text-sm font-medium", children: row.title }), row.species && (_jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] italic", children: row.species }))] })),
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: "ageMonths",
34
+ header: "Age",
35
+ render: (row) => (_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.ageMonths !== null ? `${row.ageMonths}mo` : "—" })),
36
+ },
37
+ {
38
+ key: "vendorVerified",
39
+ header: "Verified",
40
+ render: (row) => (_jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${row.vendorVerified
41
+ ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"
42
+ : "bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300"}`, children: row.vendorVerified ? "Verified" : "Pending" })),
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: "Listed",
54
+ render: (row) => _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.createdAt }),
55
+ },
56
+ ];
57
+ export function SellerLiveView({ 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", "live-items"],
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==live",
76
+ q: table.get(TABLE_KEYS.QUERY) || undefined,
77
+ mapRows: (response) => toRecordArray(response.products).map((item, index) => {
78
+ const live = (item.liveItem ?? {});
79
+ return {
80
+ id: toStringValue(item.id, `live-${index}`),
81
+ raw: item,
82
+ title: toStringValue(item.productTitle ?? item.title, "Untitled"),
83
+ price: toRupees(item.price),
84
+ species: toStringValue(live.species, ""),
85
+ ageMonths: typeof live.ageMonths === "number" ? live.ageMonths : null,
86
+ vendorVerified: Boolean(live.vendorVerified),
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.LIVE_ITEMS_NEW);
110
+ }
111
+ }, [onCreateClick]);
112
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search live items by name or species...", 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 Live Item" })] }) }), 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 live items yet \u2014 list animals, plants, or other living specimens" }) })) : (_jsx(DataTable, { columns: COLUMNS, data: rows, keyExtractor: (r) => r.id, selectable: bulkActions.length > 0, selectedIds: selection.selectedIds, onSelectionChange: (ids) => selection.setSelectedIds(ids) }))] })] }));
113
+ }
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import React, { useState, useCallback } from "react";
4
- import { X, Eye, Printer, MapPin } from "lucide-react";
4
+ import { X, Eye, Printer, MapPin, Truck } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
7
  import { useActionDispatch } from "../../../react/hooks/use-action-dispatch";
@@ -34,8 +34,11 @@ const STATUS_BADGE_VARIANT = {
34
34
  };
35
35
  const UPDATE_STATUS_OPTIONS = [
36
36
  { value: "", label: "— keep current —" },
37
+ { value: "confirmed", label: "Confirmed" },
37
38
  { value: "processing", label: "Processing" },
38
39
  { value: "shipped", label: "Shipped" },
40
+ { value: "delivered", label: "Delivered" },
41
+ { value: "cancelled", label: "Cancelled" },
39
42
  ];
40
43
  // ---------------------------------------------------------------------------
41
44
  // Order Detail Drawer
@@ -225,7 +228,20 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
225
228
  render: (row) => _jsx("span", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: row.updatedAt }),
226
229
  },
227
230
  ];
228
- const renderRowActions = useCallback((row) => (_jsx("button", { type: "button", onClick: () => setSelectedOrderId(row.id), title: "View order details", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 hover:text-zinc-700 dark:hover:text-zinc-300 transition-colors", children: _jsx(Eye, { className: "h-4 w-4" }) })), []);
231
+ const handleQuickShip = useCallback(async (row, e) => {
232
+ e.stopPropagation();
233
+ const res = await fetch(`${orderDetailApiBase}/${row.id}`, {
234
+ method: "PATCH",
235
+ headers: { "Content-Type": "application/json" },
236
+ body: JSON.stringify({ status: "shipped" }),
237
+ }).catch(() => null);
238
+ if (res?.ok)
239
+ setSelectedOrderId(null);
240
+ }, [orderDetailApiBase]);
241
+ const renderRowActions = useCallback((row) => {
242
+ const isShippable = ["PENDING", "PROCESSING", "CONFIRMED"].includes(row.status?.toUpperCase() ?? "");
243
+ return (_jsxs("div", { className: "flex items-center gap-1", children: [isShippable && (_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => void handleQuickShip(row, e), "aria-label": "Mark as shipped", title: "Mark shipped", children: _jsx(Truck, { className: "h-4 w-4" }) })), _jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); setSelectedOrderId(row.id); }, title: "View order details", "aria-label": "View order details", children: _jsx(Eye, { className: "h-4 w-4" }) })] }));
244
+ }, [handleQuickShip]);
229
245
  const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
230
246
  const handlePrintPackingSlips = useCallback(() => {
231
247
  const ids = selection.selectedIds.join(",");
@@ -0,0 +1,7 @@
1
+ export interface SellerPayoutMethodsViewProps {
2
+ onCreateClick?: () => void;
3
+ onEditClick?: (id: string) => void;
4
+ onDelete?: (id: string) => Promise<void>;
5
+ onSetDefault?: (id: string) => Promise<void>;
6
+ }
7
+ export declare function SellerPayoutMethodsView({ onCreateClick, onEditClick, onDelete, onSetDefault, }: SellerPayoutMethodsViewProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,120 @@
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 { Button, ConfirmDeleteModal, Div, ListingToolbar, Text, } from "../../../ui";
7
+ import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
8
+ import { ROUTES } from "../../..";
9
+ import { toRecordArray, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
10
+ import { TABLE_KEYS } from "../../../constants/table-keys";
11
+ const PAGE_SIZE = 50;
12
+ const DEFAULT_SORT = "-createdAt";
13
+ const SORT_OPTIONS = [
14
+ { value: "-createdAt", label: "Newest" },
15
+ { value: "createdAt", label: "Oldest" },
16
+ { value: "label", label: "Label A–Z" },
17
+ ];
18
+ function getMaskedIdentifier(item) {
19
+ if (item.upiVpa && typeof item.upiVpa === "string") {
20
+ return item.upiVpa;
21
+ }
22
+ if (item.accountNumber && typeof item.accountNumber === "string") {
23
+ const acc = item.accountNumber;
24
+ return `••••${acc.slice(-4)}`;
25
+ }
26
+ return "—";
27
+ }
28
+ function TypeBadge({ type }) {
29
+ return (_jsx("span", { className: "inline-flex items-center rounded-full px-2 py-0.5 text-xs font-semibold uppercase tracking-wide bg-violet-100 text-violet-700 dark:bg-violet-900/40 dark:text-violet-300", children: type === "upi" ? "UPI" : type === "bank" ? "Bank" : type.toUpperCase() }));
30
+ }
31
+ export function SellerPayoutMethodsView({ onCreateClick, onEditClick, onDelete, onSetDefault, }) {
32
+ const table = useUrlTable({ defaults: { sort: DEFAULT_SORT } });
33
+ const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
34
+ const [deleteTargetId, setDeleteTargetId] = useState(null);
35
+ const [deletingId, setDeletingId] = useState(null);
36
+ const [settingDefaultId, setSettingDefaultId] = useState(null);
37
+ const commitSearch = useCallback(() => {
38
+ table.set(TABLE_KEYS.QUERY, searchInput.trim());
39
+ }, [searchInput, table]);
40
+ const resetAll = useCallback(() => {
41
+ table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
42
+ setSearchInput("");
43
+ }, [table]);
44
+ const hasActiveState = !!table.get(TABLE_KEYS.QUERY) || table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT;
45
+ const { rows, isLoading, errorMessage, refetch } = useSellerListingData({
46
+ queryKey: ["seller", "payout-methods"],
47
+ endpoint: SELLER_ENDPOINTS.PAYOUT_METHODS,
48
+ page: 1,
49
+ pageSize: PAGE_SIZE,
50
+ sorts: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
51
+ q: table.get(TABLE_KEYS.QUERY) || undefined,
52
+ mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
53
+ id: toStringValue(item.id, `pm-${index}`),
54
+ raw: item,
55
+ label: String(item.label ?? ""),
56
+ type: String(item.type ?? ""),
57
+ isDefault: Boolean(item.isDefault),
58
+ isActive: Boolean(item.isActive),
59
+ maskedIdentifier: getMaskedIdentifier(item),
60
+ })),
61
+ });
62
+ const handleDelete = useCallback(async (id) => {
63
+ setDeletingId(id);
64
+ try {
65
+ if (onDelete) {
66
+ await onDelete(id);
67
+ }
68
+ else {
69
+ await fetch(SELLER_ENDPOINTS.PAYOUT_METHOD_BY_ID(id), {
70
+ method: "DELETE",
71
+ credentials: "include",
72
+ });
73
+ }
74
+ refetch?.();
75
+ }
76
+ finally {
77
+ setDeletingId(null);
78
+ setDeleteTargetId(null);
79
+ }
80
+ }, [onDelete, refetch]);
81
+ const handleSetDefault = useCallback(async (id) => {
82
+ setSettingDefaultId(id);
83
+ try {
84
+ if (onSetDefault) {
85
+ await onSetDefault(id);
86
+ }
87
+ else {
88
+ await fetch(SELLER_ENDPOINTS.PAYOUT_METHOD_BY_ID(id), {
89
+ method: "PATCH",
90
+ headers: { "Content-Type": "application/json" },
91
+ credentials: "include",
92
+ body: JSON.stringify({ isDefault: true }),
93
+ });
94
+ }
95
+ refetch?.();
96
+ }
97
+ finally {
98
+ setSettingDefaultId(null);
99
+ }
100
+ }, [onSetDefault, refetch]);
101
+ const handleCreate = useCallback(() => {
102
+ if (onCreateClick) {
103
+ onCreateClick();
104
+ }
105
+ else {
106
+ window.location.href = String(ROUTES.STORE.PAYOUT_METHODS_NEW);
107
+ }
108
+ }, [onCreateClick]);
109
+ const handleEdit = useCallback((id) => {
110
+ if (onEditClick) {
111
+ onEditClick(id);
112
+ }
113
+ else {
114
+ window.location.href = String(ROUTES.STORE.PAYOUT_METHODS_EDIT(id));
115
+ }
116
+ }, [onEditClick]);
117
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search payout methods...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, 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 Method" })] }) }), _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-3", children: Array.from({ length: 3 }).map((_, i) => (_jsx(Div, { className: "h-20 animate-pulse rounded-xl border border-zinc-100 dark:border-slate-700 bg-zinc-50 dark:bg-slate-800" }, i))) })) : rows.length === 0 ? (_jsxs(Div, { className: "py-16 text-center", children: [_jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No payout methods yet \u2014 add a UPI VPA or bank account to receive payouts" }), _jsx("div", { className: "mt-4", children: _jsx(Button, { size: "sm", onClick: handleCreate, children: "Add payout method" }) })] })) : (_jsx("div", { className: "space-y-3", children: rows.map((row) => (_jsx("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 bg-white dark:bg-slate-900 px-4 py-3", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsx("div", { className: "flex items-start gap-3 min-w-0", children: _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(Text, { className: "text-sm font-medium", children: row.label }), _jsx(TypeBadge, { type: row.type }), row.isDefault && (_jsx("span", { className: "inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300", children: "Default" })), _jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${row.isActive
118
+ ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"
119
+ : "bg-zinc-100 text-zinc-600 dark:bg-slate-800 dark:text-slate-400"}`, children: row.isActive ? "Active" : "Inactive" })] }), _jsx(Text, { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400 font-mono", children: row.maskedIdentifier })] }) }), _jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [!row.isDefault && (_jsx(Button, { size: "sm", variant: "ghost", isLoading: settingDefaultId === row.id, onClick: () => handleSetDefault(row.id), children: "Set Default" })), _jsx(Button, { size: "sm", variant: "outline", onClick: () => handleEdit(row.id), children: "Edit" }), _jsx(Button, { size: "sm", variant: "danger", disabled: deletingId === row.id, onClick: () => setDeleteTargetId(row.id), children: "Delete" })] })] }) }, row.id))) }))] }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Payout Method", message: "Are you sure you want to delete this payout method? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
120
+ }
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useCallback, useEffect, useState } from "react";
4
- import { Alert, Badge, Button, Div, FormField, FormGroup, Heading, Section, Stack, Text } from "../../../ui";
4
+ import { Alert, Badge, Div, FormField, FormGroup, Heading, Stack, Text, Toggle } from "../../../ui";
5
5
  import { StackedViewShell } from "../../../ui";
6
+ import { StepForm } from "../../shell";
6
7
  const DEFAULT_DRAFT = {
7
8
  method: "upi",
8
9
  upiId: "",
@@ -11,6 +12,11 @@ const DEFAULT_DRAFT = {
11
12
  ifscCode: "",
12
13
  bankName: "",
13
14
  accountType: "savings",
15
+ gstin: "",
16
+ pan: "",
17
+ businessType: "",
18
+ autoPayout: true,
19
+ minimumThreshold: "",
14
20
  };
15
21
  export function SellerPayoutSettingsView({ apiBase = "/api/store/payout-settings" }) {
16
22
  const [draft, setDraft] = useState(DEFAULT_DRAFT);
@@ -19,6 +25,7 @@ export function SellerPayoutSettingsView({ apiBase = "/api/store/payout-settings
19
25
  const [saving, setSaving] = useState(false);
20
26
  const [error, setError] = useState(null);
21
27
  const [success, setSuccess] = useState(false);
28
+ const [currentStep, setCurrentStep] = useState(0);
22
29
  useEffect(() => {
23
30
  fetch(apiBase)
24
31
  .then((r) => r.json())
@@ -33,13 +40,20 @@ export function SellerPayoutSettingsView({ apiBase = "/api/store/payout-settings
33
40
  ifscCode: details.bankAccount?.ifscCode ?? "",
34
41
  bankName: details.bankAccount?.bankName ?? "",
35
42
  accountType: details.bankAccount?.accountType ?? "savings",
43
+ gstin: res?.data?.taxInfo?.gstin ?? "",
44
+ pan: res?.data?.taxInfo?.pan ?? "",
45
+ businessType: res?.data?.taxInfo?.businessType ?? "",
46
+ autoPayout: res?.data?.preferences?.autoPayout ?? true,
47
+ minimumThreshold: res?.data?.preferences?.minimumThreshold
48
+ ? String(res.data.preferences.minimumThreshold / 100)
49
+ : "",
36
50
  });
37
51
  })
38
52
  .catch(() => { })
39
53
  .finally(() => setLoading(false));
40
54
  }, [apiBase]);
41
- const update = useCallback((key, value) => {
42
- setDraft((prev) => ({ ...prev, [key]: value }));
55
+ const update = useCallback((partial) => {
56
+ setDraft((prev) => ({ ...prev, ...partial }));
43
57
  setSuccess(false);
44
58
  }, []);
45
59
  const handleSave = async () => {
@@ -47,7 +61,7 @@ export function SellerPayoutSettingsView({ apiBase = "/api/store/payout-settings
47
61
  setSuccess(false);
48
62
  setSaving(true);
49
63
  try {
50
- const body = draft.method === "upi"
64
+ const methodFields = draft.method === "upi"
51
65
  ? { method: "upi", upiId: draft.upiId.trim() }
52
66
  : {
53
67
  method: "bank_transfer",
@@ -57,6 +71,18 @@ export function SellerPayoutSettingsView({ apiBase = "/api/store/payout-settings
57
71
  bankName: draft.bankName.trim(),
58
72
  accountType: draft.accountType,
59
73
  };
74
+ const body = {
75
+ ...methodFields,
76
+ taxInfo: {
77
+ gstin: draft.gstin.trim(),
78
+ pan: draft.pan.trim(),
79
+ businessType: draft.businessType.trim(),
80
+ },
81
+ preferences: {
82
+ autoPayout: draft.autoPayout,
83
+ minimumThreshold: Math.round(parseFloat(draft.minimumThreshold || "0") * 100),
84
+ },
85
+ };
60
86
  const res = await fetch(apiBase, {
61
87
  method: "PATCH",
62
88
  headers: { "Content-Type": "application/json" },
@@ -68,7 +94,6 @@ export function SellerPayoutSettingsView({ apiBase = "/api/store/payout-settings
68
94
  setSuccess(true);
69
95
  const updated = json?.data?.payoutDetails ?? { method: draft.method, isConfigured: false };
70
96
  setCurrent(updated);
71
- // clear sensitive field
72
97
  setDraft((prev) => ({ ...prev, accountNumber: "" }));
73
98
  }
74
99
  catch (err) {
@@ -78,14 +103,27 @@ export function SellerPayoutSettingsView({ apiBase = "/api/store/payout-settings
78
103
  setSaving(false);
79
104
  }
80
105
  };
81
- const isUpi = draft.method === "upi";
82
106
  const busy = loading || saving;
83
- return (_jsx(StackedViewShell, { portal: "seller", title: "Payout Settings", sections: [
84
- _jsxs(Stack, { gap: "lg", children: [current && (_jsx(Div, { className: "flex items-center gap-2", children: _jsx(Badge, { variant: current.isConfigured ? "success" : "warning", children: current.isConfigured ? "Payout configured" : "Not configured" }) })), error && _jsx(Alert, { variant: "error", children: error }), success && _jsx(Alert, { variant: "success", children: "Payout details saved." }), current?.isConfigured && (_jsx(Alert, { variant: "info", children: current.method === "upi"
107
+ const steps = [
108
+ {
109
+ label: "Payout Method",
110
+ render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Payout Method" }), current && (_jsx(Div, { className: "flex items-center gap-2 mb-2", children: _jsx(Badge, { variant: current.isConfigured ? "success" : "warning", children: current.isConfigured ? "Payout configured" : "Not configured" }) })), current?.isConfigured && (_jsx(Alert, { variant: "info", children: current.method === "upi"
85
111
  ? `Current UPI: ${current.upiId}`
86
- : `Current bank: ${current.bankAccount?.bankName ?? ""} — ••••${current.bankAccount?.accountNumberMasked?.slice(-4) ?? "••••"} (${current.bankAccount?.accountType})` })), _jsxs(Section, { children: [_jsx(Heading, { level: 3, className: "mb-3", children: "Payout Method" }), _jsx(Stack, { gap: "sm", children: ([
87
- { value: "upi", label: "UPI", desc: "Instant payouts via UPI VPA (e.g. name@upi)." },
88
- { value: "bank_transfer", label: "Bank Transfer", desc: "NEFT/RTGS to your bank account within 2–3 business days." },
89
- ]).map(({ value, label, desc }) => (_jsxs("label", { className: "flex items-center gap-3 p-3 rounded-lg border border-[var(--appkit-color-border)] cursor-pointer has-[:checked]:border-[var(--appkit-color-primary)] has-[:checked]:bg-[var(--appkit-color-primary)]/5", children: [_jsx("input", { type: "radio", name: "payoutMethod", value: value, checked: draft.method === value, onChange: () => update("method", value), className: "accent-[var(--appkit-color-primary)]", disabled: busy }), _jsxs(Div, { children: [_jsx(Text, { className: "font-medium", children: label }), _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: desc })] })] }, value))) })] }), isUpi && (_jsxs(Section, { children: [_jsx(Heading, { level: 3, className: "mb-3", children: "UPI Details" }), _jsx(FormField, { name: "upiId", label: "UPI ID (VPA)", type: "text", value: draft.upiId, onChange: (v) => update("upiId", v), placeholder: "yourname@upi", helpText: "Ensure this VPA is registered and active.", disabled: busy })] })), !isUpi && (_jsxs(Section, { children: [_jsx(Heading, { level: 3, className: "mb-3", children: "Bank Account Details" }), _jsxs(Stack, { gap: "md", children: [_jsx(FormField, { name: "accountHolderName", label: "Account Holder Name", type: "text", value: draft.accountHolderName, onChange: (v) => update("accountHolderName", v), placeholder: "Name as on bank account", disabled: busy }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "accountNumber", label: "Account Number", type: "text", value: draft.accountNumber, onChange: (v) => update("accountNumber", v), placeholder: "Enter full account number", helpText: current?.bankAccount ? `Saved: ••••${current.bankAccount.accountNumberMasked?.slice(-4) ?? "••••"}` : "Stored securely, never displayed in full.", disabled: busy }), _jsx(FormField, { name: "ifscCode", label: "IFSC Code", type: "text", value: draft.ifscCode, onChange: (v) => update("ifscCode", v.toUpperCase()), placeholder: "e.g. SBIN0001234", disabled: busy })] }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "bankName", label: "Bank Name", type: "text", value: draft.bankName, onChange: (v) => update("bankName", v), placeholder: "e.g. State Bank of India", disabled: busy }), _jsxs(Div, { children: [_jsx(Text, { className: "text-sm font-medium mb-1.5", children: "Account Type" }), _jsx(Stack, { gap: "xs", className: "flex-row", children: ["savings", "current"].map((t) => (_jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [_jsx("input", { type: "radio", name: "accountType", value: t, checked: draft.accountType === t, onChange: () => update("accountType", t), className: "accent-[var(--appkit-color-primary)]", disabled: busy }), _jsx(Text, { className: "capitalize text-sm", children: t })] }, t))) })] })] })] })] })), _jsx(Div, { className: "flex justify-end pt-2 border-t border-[var(--appkit-color-border)]", children: _jsx(Button, { variant: "primary", onClick: handleSave, disabled: busy, isLoading: saving, children: "Save Payout Details" }) })] }, "payout"),
112
+ : `Current bank: ${current.bankAccount?.bankName ?? ""} — ••••${current.bankAccount?.accountNumberMasked?.slice(-4) ?? "••••"} (${current.bankAccount?.accountType})` })), _jsx(Stack, { gap: "sm", children: ([
113
+ { value: "upi", label: "UPI", desc: "Instant payouts via UPI VPA (e.g. name@upi)." },
114
+ { value: "bank_transfer", label: "Bank Transfer", desc: "NEFT/RTGS to your bank account within 2–3 business days." },
115
+ ]).map(({ value, label, desc }) => (_jsxs("label", { className: "flex items-center gap-3 p-3 rounded-lg border border-[var(--appkit-color-border)] cursor-pointer has-[:checked]:border-[var(--appkit-color-primary)] has-[:checked]:bg-[var(--appkit-color-primary)]/5", children: [_jsx("input", { type: "radio", name: "payoutMethod", value: value, checked: values.method === value, onChange: () => onChange({ method: value }), className: "accent-[var(--appkit-color-primary)]", disabled: busy }), _jsxs(Div, { children: [_jsx(Text, { className: "font-medium", children: label }), _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: desc })] })] }, value))) }), values.method === "upi" && (_jsx(FormField, { name: "upiId", label: "UPI ID (VPA)", type: "text", value: values.upiId, onChange: (v) => onChange({ upiId: v }), placeholder: "yourname@upi", helpText: "Ensure this VPA is registered and active.", disabled: busy })), values.method === "bank_transfer" && (_jsxs(Stack, { gap: "md", className: "mt-2", children: [_jsx(FormField, { name: "accountHolderName", label: "Account Holder Name", type: "text", value: values.accountHolderName, onChange: (v) => onChange({ accountHolderName: v }), placeholder: "Name as on bank account", disabled: busy }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "accountNumber", label: "Account Number", type: "text", value: values.accountNumber, onChange: (v) => onChange({ accountNumber: v }), placeholder: "Enter full account number", helpText: current?.bankAccount ? `Saved: ••••${current.bankAccount.accountNumberMasked?.slice(-4) ?? "••••"}` : "Stored securely, never displayed in full.", disabled: busy }), _jsx(FormField, { name: "ifscCode", label: "IFSC Code", type: "text", value: values.ifscCode, onChange: (v) => onChange({ ifscCode: v.toUpperCase() }), placeholder: "e.g. SBIN0001234", disabled: busy })] }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "bankName", label: "Bank Name", type: "text", value: values.bankName, onChange: (v) => onChange({ bankName: v }), placeholder: "e.g. State Bank of India", disabled: busy }), _jsxs(Div, { children: [_jsx(Text, { className: "text-sm font-medium mb-1.5", children: "Account Type" }), _jsx(Stack, { gap: "xs", className: "flex-row", children: ["savings", "current"].map((t) => (_jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [_jsx("input", { type: "radio", name: "accountType", value: t, checked: values.accountType === t, onChange: () => onChange({ accountType: t }), className: "accent-[var(--appkit-color-primary)]", disabled: busy }), _jsx(Text, { className: "capitalize text-sm", children: t })] }, t))) })] })] })] }))] })),
116
+ },
117
+ {
118
+ label: "Tax Info",
119
+ render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Tax Information" }), _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "Optional \u2014 required only for GST invoice generation and TDS compliance." }), _jsx(FormField, { name: "gstin", label: "GSTIN (optional)", type: "text", value: values.gstin, onChange: (v) => onChange({ gstin: v.toUpperCase() }), placeholder: "e.g. 22AAAAA0000A1Z5", helpText: "15-character Goods and Services Tax Identification Number.", disabled: busy }), _jsx(FormField, { name: "pan", label: "PAN (optional)", type: "text", value: values.pan, onChange: (v) => onChange({ pan: v.toUpperCase() }), placeholder: "e.g. ABCDE1234F", helpText: "Required for payouts above \u20B950,000 per year (TDS threshold).", disabled: busy }), _jsx(FormField, { name: "businessType", label: "Business Type (optional)", type: "text", value: values.businessType, onChange: (v) => onChange({ businessType: v }), placeholder: "e.g. Individual, Sole Proprietor, Private Limited", disabled: busy })] })),
120
+ },
121
+ {
122
+ label: "Preferences",
123
+ render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Payout Preferences" }), _jsx(Toggle, { checked: values.autoPayout, onChange: (checked) => onChange({ autoPayout: checked }), label: "Enable auto-payout \u2014 automatically transfer earnings on schedule", disabled: busy }), _jsx(FormField, { name: "minimumThreshold", label: "Minimum Payout Threshold (\u20B9)", type: "number", value: values.minimumThreshold, onChange: (v) => onChange({ minimumThreshold: v }), placeholder: "e.g. 500", helpText: "Payouts are held until your balance exceeds this amount. Leave blank for no minimum.", disabled: busy }), _jsxs(Div, { className: "p-3 rounded-lg bg-[var(--appkit-color-surface-alt)] border border-[var(--appkit-color-border)]", children: [_jsx(Text, { className: "text-sm font-medium mb-1", children: "Payout Schedule" }), _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "Auto-payouts run every Monday for the previous week's settled orders. Manual payouts can be requested from the Payouts page at any time." })] })] })),
124
+ },
125
+ ];
126
+ return (_jsx(StackedViewShell, { portal: "seller", title: "Payout Settings", sections: [
127
+ _jsxs("div", { children: [error && _jsx(Alert, { variant: "error", className: "mb-4", children: error }), success && _jsx(Alert, { variant: "success", className: "mb-4", children: "Payout details saved." }), _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: handleSave, formId: "seller-payout-settings", currentStep: currentStep, onStepChange: setCurrentStep, completeLabel: "Save Payout Details", isLoading: busy })] }, "payout"),
90
128
  ] }));
91
129
  }
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- export type ProductListingMode = "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live";
2
+ export type ProductListingMode = "standard" | "auction" | "pre-order" | "prize-draw" | "bundle" | "classified" | "digital-code" | "live";
3
3
  export interface SellerProductDraft {
4
4
  title?: string;
5
5
  slug?: string;
@@ -210,13 +210,15 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
210
210
  ? "Auction"
211
211
  : listingType === "pre-order"
212
212
  ? "Pre-Order"
213
- : listingType === "classified"
214
- ? "Classified"
215
- : listingType === "digital-code"
216
- ? "Digital Code"
217
- : listingType === "live"
218
- ? "Live Item"
219
- : "Product";
213
+ : listingType === "bundle"
214
+ ? "Bundle"
215
+ : listingType === "classified"
216
+ ? "Classified"
217
+ : listingType === "digital-code"
218
+ ? "Digital Code"
219
+ : listingType === "live"
220
+ ? "Live Item"
221
+ : "Product";
220
222
  const typeSpecificStep = listingType === "auction"
221
223
  ? {
222
224
  label: "Auction Settings",
@@ -3,6 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { X } from "lucide-react";
4
4
  import { Button, Div, FilterChipGroup, Row, Text } from "../../../ui";
5
5
  import { INPUT_CLS, FILTER_LABEL_CLS } from "./seller-products-styles";
6
+ import { CategoryInlineSelect } from "./CategoryInlineSelect";
7
+ import { BrandInlineSelect } from "./BrandInlineSelect";
6
8
  const HALF_INPUT_CLS = INPUT_CLS.replace("w-full", "w-1/2");
7
9
  const CONDITION_OPTIONS = [
8
10
  { value: "", label: "Any" },
@@ -16,5 +18,5 @@ export function SellerProductsFilterDrawer({ isOpen, pendingFilters, statusOptio
16
18
  if (!isOpen)
17
19
  return null;
18
20
  const patch = (k, v) => onChange({ ...pendingFilters, [k]: v });
19
- return (_jsxs(_Fragment, { children: [_jsx(Div, { role: "presentation", className: "fixed inset-0 z-40 bg-black/40", onClick: onClose }), _jsxs(Div, { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-[var(--appkit-color-surface)] shadow-2xl", children: [_jsxs(Row, { justify: "between", className: "border-b border-[var(--appkit-color-border)] px-4 py-3.5", children: [_jsx(Text, { className: "text-base font-semibold text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs(Row, { className: "gap-2", children: [activeFilterCount > 0 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: onClear, className: "text-xs text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-error)]", children: "Clear all" })), _jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, "aria-label": "Close filters", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs(Div, { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsx(FilterChipGroup, { label: "Status", tabs: statusOptions, value: pendingFilters.status ?? "", onChange: (id) => patch("status", id) }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Category" }), _jsx("input", { type: "text", value: pendingFilters.category ?? "", onChange: (e) => patch("category", e.target.value), placeholder: "category slug", className: INPUT_CLS })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Brand" }), _jsx("input", { type: "text", value: pendingFilters.brand ?? "", onChange: (e) => patch("brand", e.target.value), placeholder: "brand slug", className: INPUT_CLS })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Condition" }), _jsx("select", { value: pendingFilters.condition ?? "", onChange: (e) => patch("condition", e.target.value), className: INPUT_CLS, children: CONDITION_OPTIONS.map((o) => (_jsx("option", { value: o.value, children: o.label }, o.value))) })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Price (paise)" }), _jsxs(Row, { className: "gap-2", children: [_jsx("input", { type: "number", min: 0, value: pendingFilters.minPrice ?? "", onChange: (e) => patch("minPrice", e.target.value), placeholder: "min", className: HALF_INPUT_CLS }), _jsx("input", { type: "number", min: 0, value: pendingFilters.maxPrice ?? "", onChange: (e) => patch("maxPrice", e.target.value), placeholder: "max", className: HALF_INPUT_CLS })] })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Tags (comma-separated)" }), _jsx("input", { type: "text", value: pendingFilters.tags ?? "", onChange: (e) => patch("tags", e.target.value), placeholder: "pokemon, vintage, holo", className: INPUT_CLS })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Badges (feature slugs)" }), _jsx("input", { type: "text", value: pendingFilters.badges ?? "", onChange: (e) => patch("badges", e.target.value), placeholder: "feature-free-shipping, feature-verified", className: INPUT_CLS })] })] }), _jsx(Div, { className: "border-t border-[var(--appkit-color-border)] px-4 py-3.5", children: _jsxs(Button, { variant: "primary", onClick: onApply, className: "w-full rounded-lg py-2.5 active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
21
+ return (_jsxs(_Fragment, { children: [_jsx(Div, { role: "presentation", className: "fixed inset-0 z-40 bg-black/40", onClick: onClose }), _jsxs(Div, { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-[var(--appkit-color-surface)] shadow-2xl", children: [_jsxs(Row, { justify: "between", className: "border-b border-[var(--appkit-color-border)] px-4 py-3.5", children: [_jsx(Text, { className: "text-base font-semibold text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs(Row, { className: "gap-2", children: [activeFilterCount > 0 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: onClear, className: "text-xs text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-error)]", children: "Clear all" })), _jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, "aria-label": "Close filters", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs(Div, { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsx(FilterChipGroup, { label: "Status", tabs: statusOptions, value: pendingFilters.status ?? "", onChange: (id) => patch("status", id) }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Category" }), _jsx(CategoryInlineSelect, { value: pendingFilters.category ?? "", onChange: (v) => patch("category", v), placeholder: "Search categories\u2026" })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Brand" }), _jsx(BrandInlineSelect, { value: pendingFilters.brand ?? "", onChange: (v) => patch("brand", v), placeholder: "Search brands\u2026", allowCreate: false })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Condition" }), _jsx("select", { value: pendingFilters.condition ?? "", onChange: (e) => patch("condition", e.target.value), className: INPUT_CLS, children: CONDITION_OPTIONS.map((o) => (_jsx("option", { value: o.value, children: o.label }, o.value))) })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Price (\u20B9 Rupees)" }), _jsxs(Row, { className: "gap-2", children: [_jsx("input", { type: "number", min: 0, value: pendingFilters.minPrice ?? "", onChange: (e) => patch("minPrice", e.target.value), placeholder: "min", className: HALF_INPUT_CLS }), _jsx("input", { type: "number", min: 0, value: pendingFilters.maxPrice ?? "", onChange: (e) => patch("maxPrice", e.target.value), placeholder: "max", className: HALF_INPUT_CLS })] })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Tags (comma-separated)" }), _jsx("input", { type: "text", value: pendingFilters.tags ?? "", onChange: (e) => patch("tags", e.target.value), placeholder: "pokemon, vintage, holo", className: INPUT_CLS })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Badges (feature slugs)" }), _jsx("input", { type: "text", value: pendingFilters.badges ?? "", onChange: (e) => patch("badges", e.target.value), placeholder: "feature-free-shipping, feature-verified", className: INPUT_CLS })] })] }), _jsx(Div, { className: "border-t border-[var(--appkit-color-border)] px-4 py-3.5", children: _jsxs(Button, { variant: "primary", onClick: onApply, className: "w-full rounded-lg py-2.5 active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
20
22
  }