@mohasinac/appkit 2.7.49 → 2.7.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/SidebarCollapseToggle.d.ts +19 -0
- package/dist/_internal/client/features/layout/SidebarCollapseToggle.js +15 -0
- package/dist/_internal/client/features/layout/index.d.ts +2 -0
- package/dist/_internal/client/features/layout/index.js +1 -0
- package/dist/_internal/server/features/checkout/actions.js +25 -2
- package/dist/_internal/server/features/promotions/actions.js +3 -1
- package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.js +65 -0
- package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.js +163 -0
- package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.js +115 -0
- package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.js +96 -0
- package/dist/_internal/shared/actions/action-registry.js +149 -0
- package/dist/client.d.ts +45 -6
- package/dist/client.js +27 -4
- package/dist/constants/api-endpoints.d.ts +42 -0
- package/dist/constants/api-endpoints.js +14 -0
- package/dist/features/_guide-cls.d.ts +13 -0
- package/dist/features/_guide-cls.js +14 -0
- package/dist/features/about/components/HelpPageView.js +29 -26
- package/dist/features/account/components/BuyerAccountGuideView.d.ts +1 -0
- package/dist/features/account/components/BuyerAccountGuideView.js +39 -0
- package/dist/features/account/components/BuyerAuctionsGuideView.d.ts +1 -0
- package/dist/features/account/components/BuyerAuctionsGuideView.js +38 -0
- package/dist/features/account/components/BuyerOrdersGuideView.d.ts +1 -0
- package/dist/features/account/components/BuyerOrdersGuideView.js +48 -0
- package/dist/features/account/components/BuyerShoppingGuideView.d.ts +1 -0
- package/dist/features/account/components/BuyerShoppingGuideView.js +38 -0
- package/dist/features/account/components/UserSidebar.js +2 -1
- package/dist/features/admin/components/AdminAnalyticsGuideView.d.ts +1 -0
- package/dist/features/admin/components/AdminAnalyticsGuideView.js +24 -0
- package/dist/features/admin/components/AdminBlogEditorView.js +102 -75
- package/dist/features/admin/components/AdminBundleEditorView.js +20 -14
- package/dist/features/admin/components/AdminBundlesView.d.ts +1 -3
- package/dist/features/admin/components/AdminBundlesView.js +173 -52
- package/dist/features/admin/components/AdminCatalogGuideView.d.ts +1 -0
- package/dist/features/admin/components/AdminCatalogGuideView.js +28 -0
- package/dist/features/admin/components/AdminContentGuideView.d.ts +1 -0
- package/dist/features/admin/components/AdminContentGuideView.js +36 -0
- package/dist/features/admin/components/AdminCouponEditorView.js +1 -1
- package/dist/features/admin/components/AdminGuideHubView.d.ts +5 -0
- package/dist/features/admin/components/AdminGuideHubView.js +79 -0
- package/dist/features/admin/components/AdminOrdersGuideView.d.ts +1 -0
- package/dist/features/admin/components/AdminOrdersGuideView.js +44 -0
- package/dist/features/admin/components/AdminProductsView.js +8 -3
- package/dist/features/admin/components/AdminSidebar.js +2 -1
- package/dist/features/admin/components/AdminSiteConfigGuideView.d.ts +1 -0
- package/dist/features/admin/components/AdminSiteConfigGuideView.js +21 -0
- package/dist/features/admin/components/AdminStoresGuideView.d.ts +1 -0
- package/dist/features/admin/components/AdminStoresGuideView.js +32 -0
- package/dist/features/admin/components/AdminTeamGuideView.d.ts +1 -0
- package/dist/features/admin/components/AdminTeamGuideView.js +33 -0
- package/dist/features/admin/components/AdminTrustGuideView.d.ts +1 -0
- package/dist/features/admin/components/AdminTrustGuideView.js +40 -0
- package/dist/features/admin/components/AdminUsersGuideView.d.ts +1 -0
- package/dist/features/admin/components/AdminUsersGuideView.js +38 -0
- package/dist/features/auth/repository/session.repository.js +16 -1
- package/dist/features/cart/hooks/useAddToCart.d.ts +2 -0
- package/dist/features/cart/hooks/useAddToCart.js +2 -0
- package/dist/features/cart/hooks/useGuestCart.d.ts +2 -0
- package/dist/features/cart/utils/guest-cart.d.ts +7 -0
- package/dist/features/cart/utils/pending-ops.d.ts +6 -0
- package/dist/features/categories/components/CategoryBundlesListing.js +3 -25
- package/dist/features/categories/components/CategoryProductsListing.js +6 -10
- package/dist/features/categories/components/index.d.ts +0 -2
- package/dist/features/categories/components/index.js +0 -1
- package/dist/features/categories/schemas/firestore.d.ts +9 -0
- package/dist/features/events/components/AdminEventEditorView.js +223 -269
- package/dist/features/events/components/EventRaffleWinnerView.d.ts +9 -1
- package/dist/features/events/components/EventRaffleWinnerView.js +3 -3
- package/dist/features/events/components/SpinWheelView.js +3 -3
- package/dist/features/grouped/repository/grouped-listings.repository.d.ts +9 -0
- package/dist/features/grouped/repository/grouped-listings.repository.js +12 -0
- package/dist/features/layout/BottomNavLayout.d.ts +5 -0
- package/dist/features/layout/BottomNavLayout.js +21 -0
- package/dist/features/layout/TitleBar.js +7 -1
- package/dist/features/layout/TitleBarLayout.d.ts +8 -2
- package/dist/features/layout/TitleBarLayout.js +11 -7
- package/dist/features/media/upload/MediaUploadField.js +1 -1
- package/dist/features/orders/schemas/index.d.ts +2 -2
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +12 -6
- package/dist/features/products/components/AuctionsIndexListing.js +3 -1
- package/dist/features/products/components/MarketplaceBundleCard.d.ts +21 -0
- package/dist/features/products/components/MarketplaceBundleCard.js +56 -0
- package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
- package/dist/features/products/components/PrizeDrawEntryActions.d.ts +3 -1
- package/dist/features/products/components/PrizeDrawEntryActions.js +8 -12
- package/dist/features/products/components/ProductDetailActions.d.ts +5 -1
- package/dist/features/products/components/ProductDetailActions.js +3 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +2 -0
- package/dist/features/products/components/ProductDetailPageView.js +4 -0
- package/dist/features/products/components/ProductGrid.js +3 -3
- package/dist/features/products/components/ProductsIndexListing.js +18 -9
- package/dist/features/products/components/index.d.ts +2 -4
- package/dist/features/products/components/index.js +1 -2
- package/dist/features/promotions/components/CouponCard.d.ts +11 -1
- package/dist/features/promotions/components/CouponCard.js +36 -2
- package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +27 -0
- package/dist/features/promotions/repository/claimed-coupons.repository.js +115 -0
- package/dist/features/promotions/schemas/firestore.d.ts +34 -0
- package/dist/features/promotions/schemas/firestore.js +14 -1
- package/dist/features/reviews/components/index.d.ts +0 -2
- package/dist/features/reviews/components/index.js +0 -1
- package/dist/features/seller/components/BrandInlineSelect.js +23 -3
- package/dist/features/seller/components/CategoryInlineSelect.js +20 -3
- package/dist/features/seller/components/SellerAddressesView.js +3 -2
- package/dist/features/seller/components/SellerAnalyticsAlertsView.d.ts +6 -0
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +124 -0
- package/dist/features/seller/components/SellerAnalyticsView.d.ts +2 -0
- package/dist/features/seller/components/SellerAnalyticsView.js +17 -9
- package/dist/features/seller/components/SellerBidsView.js +33 -3
- package/dist/features/seller/components/SellerBundlesView.d.ts +5 -0
- package/dist/features/seller/components/SellerBundlesView.js +104 -0
- package/dist/features/seller/components/SellerClassifiedView.d.ts +5 -0
- package/dist/features/seller/components/SellerClassifiedView.js +113 -0
- package/dist/features/seller/components/SellerDashboardView.js +19 -7
- package/dist/features/seller/components/SellerDigitalCodesView.d.ts +5 -0
- package/dist/features/seller/components/SellerDigitalCodesView.js +115 -0
- package/dist/features/seller/components/SellerGoogleReviewsView.d.ts +14 -0
- package/dist/features/seller/components/SellerGoogleReviewsView.js +95 -0
- package/dist/features/seller/components/SellerGroupedListingsView.d.ts +6 -0
- package/dist/features/seller/components/SellerGroupedListingsView.js +112 -0
- package/dist/features/seller/components/SellerLiveView.d.ts +5 -0
- package/dist/features/seller/components/SellerLiveView.js +113 -0
- package/dist/features/seller/components/SellerOrdersView.js +53 -2
- package/dist/features/seller/components/SellerPayoutMethodsView.d.ts +7 -0
- package/dist/features/seller/components/SellerPayoutMethodsView.js +120 -0
- package/dist/features/seller/components/SellerPayoutSettingsView.js +50 -12
- package/dist/features/seller/components/SellerProductShell.d.ts +3 -1
- package/dist/features/seller/components/SellerProductShell.js +35 -11
- package/dist/features/seller/components/SellerProductsCards.d.ts +19 -0
- package/dist/features/seller/components/SellerProductsCards.js +19 -0
- package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +14 -0
- package/dist/features/seller/components/SellerProductsFilterDrawer.js +22 -0
- package/dist/features/seller/components/SellerProductsView.d.ts +3 -1
- package/dist/features/seller/components/SellerProductsView.js +92 -49
- package/dist/features/seller/components/SellerReviewsView.js +67 -4
- package/dist/features/seller/components/SellerShippingConfigsView.d.ts +7 -0
- package/dist/features/seller/components/SellerShippingConfigsView.js +149 -0
- package/dist/features/seller/components/SellerShippingView.js +37 -10
- package/dist/features/seller/components/SellerSidebar.js +2 -1
- package/dist/features/seller/components/SellerStoreCategoriesView.d.ts +9 -0
- package/dist/features/seller/components/SellerStoreCategoriesView.js +122 -0
- package/dist/features/seller/components/SellerStorefrontView.d.ts +0 -7
- package/dist/features/seller/components/SellerStorefrontView.js +26 -29
- package/dist/features/seller/components/SellerTemplatesView.d.ts +10 -0
- package/dist/features/seller/components/SellerTemplatesView.js +265 -0
- package/dist/features/seller/components/index.d.ts +26 -0
- package/dist/features/seller/components/index.js +13 -0
- package/dist/features/seller/components/seller-products-styles.d.ts +7 -0
- package/dist/features/seller/components/seller-products-styles.js +14 -0
- package/dist/features/shell/FormShell.d.ts +7 -1
- package/dist/features/shell/FormShell.js +5 -2
- package/dist/features/store-extensions/index.d.ts +4 -0
- package/dist/features/store-extensions/index.js +4 -0
- package/dist/features/store-extensions/repository/rbac.repositories.d.ts +23 -0
- package/dist/features/store-extensions/repository/rbac.repositories.js +32 -0
- package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +91 -0
- package/dist/features/store-extensions/repository/store-extensions.repositories.js +127 -0
- package/dist/features/store-extensions/schemas/firestore.d.ts +244 -0
- package/dist/features/store-extensions/schemas/firestore.js +158 -0
- package/dist/features/store-extensions/schemas/rbac.d.ts +65 -0
- package/dist/features/store-extensions/schemas/rbac.js +43 -0
- package/dist/features/stores/api/[storeSlug]/reviews/route.js +49 -7
- package/dist/features/stores/components/InteractiveStoreCard.js +7 -3
- package/dist/features/stores/components/StoreCapabilitiesGuideView.d.ts +3 -0
- package/dist/features/stores/components/StoreCapabilitiesGuideView.js +101 -0
- package/dist/features/stores/components/StoreFinanceGuideView.d.ts +3 -0
- package/dist/features/stores/components/StoreFinanceGuideView.js +79 -0
- package/dist/features/stores/components/StoreGuideHubView.d.ts +5 -0
- package/dist/features/stores/components/StoreGuideHubView.js +89 -0
- package/dist/features/stores/components/StoreListingsGuideView.d.ts +3 -0
- package/dist/features/stores/components/StoreListingsGuideView.js +151 -0
- package/dist/features/stores/components/StoreOrdersGuideView.d.ts +3 -0
- package/dist/features/stores/components/StoreOrdersGuideView.js +122 -0
- package/dist/features/stores/components/StoreProductsListing.js +6 -10
- package/dist/features/stores/components/StoreReviewsListing.js +100 -23
- package/dist/features/stores/components/StoreSettingsGuideView.d.ts +3 -0
- package/dist/features/stores/components/StoreSettingsGuideView.js +56 -0
- package/dist/features/stores/components/index.d.ts +12 -1
- package/dist/features/stores/components/index.js +6 -1
- package/dist/features/stores/hooks/useStores.d.ts +5 -0
- package/dist/features/stores/hooks/useStores.js +10 -0
- package/dist/features/stores/manifest.js +1 -1
- package/dist/features/stores/schemas/firestore.d.ts +2 -0
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +13 -2
- package/dist/index.d.ts +60 -9
- package/dist/index.js +66 -15
- package/dist/next/routing/route-map.d.ts +134 -0
- package/dist/next/routing/route-map.js +59 -0
- package/dist/providers/db-firebase/__tests__/filter-aliases.test.d.ts +1 -0
- package/dist/providers/db-firebase/__tests__/filter-aliases.test.js +93 -0
- package/dist/providers/db-firebase/sieve.d.ts +49 -0
- package/dist/providers/db-firebase/sieve.js +61 -8
- package/dist/react/hooks/useFormStatePreservation.d.ts +17 -0
- package/dist/react/hooks/useFormStatePreservation.js +62 -0
- package/dist/react/hooks/useInlineRowEdit.d.ts +24 -0
- package/dist/react/hooks/useInlineRowEdit.js +68 -0
- package/dist/react/index.d.ts +4 -0
- package/dist/react/index.js +4 -0
- package/dist/repositories/index.d.ts +5 -0
- package/dist/repositories/index.js +7 -0
- package/dist/seed/actions/demo-seed-actions.d.ts +1 -1
- package/dist/seed/claimed-coupons-seed-data.d.ts +13 -0
- package/dist/seed/claimed-coupons-seed-data.js +79 -0
- package/dist/seed/index.d.ts +2 -0
- package/dist/seed/index.js +3 -0
- package/dist/seed/manifest.js +17 -0
- package/dist/seed/store-extensions-seed-data.d.ts +19 -0
- package/dist/seed/store-extensions-seed-data.js +421 -0
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Button.style.css +20 -12
- package/dist/ui/components/ClaimCouponButton.d.ts +28 -0
- package/dist/ui/components/ClaimCouponButton.js +89 -0
- package/dist/ui/components/ListingToolbar.d.ts +8 -1
- package/dist/ui/components/ListingToolbar.js +4 -2
- package/dist/ui/components/QuickCreateModal.d.ts +15 -0
- package/dist/ui/components/QuickCreateModal.js +48 -0
- package/dist/ui/components/QuickCreateModal.style.css +84 -0
- package/dist/ui/components/StickyBottomBar.d.ts +22 -0
- package/dist/ui/components/StickyBottomBar.js +20 -0
- package/dist/ui/components/VacationBanner.d.ts +11 -0
- package/dist/ui/components/VacationBanner.js +16 -0
- package/dist/ui/components/__tests__/BulkActionBar.test.d.ts +1 -0
- package/dist/ui/components/__tests__/BulkActionBar.test.js +96 -0
- package/dist/ui/components/__tests__/ListingToolbar.test.d.ts +1 -0
- package/dist/ui/components/__tests__/ListingToolbar.test.js +125 -0
- package/dist/ui/index.d.ts +9 -1
- package/dist/ui/index.js +4 -0
- package/package.json +8 -2
|
@@ -0,0 +1,115 @@
|
|
|
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
|
+
];
|
|
20
|
+
const COLUMNS = [
|
|
21
|
+
{
|
|
22
|
+
key: "title",
|
|
23
|
+
header: "Product",
|
|
24
|
+
render: (row) => _jsx(Text, { className: "text-sm font-medium", children: row.title }),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: "price",
|
|
28
|
+
header: "Price",
|
|
29
|
+
render: (row) => _jsx(Text, { className: "text-sm tabular-nums", children: row.price }),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
key: "codePoolSize",
|
|
33
|
+
header: "Total Codes",
|
|
34
|
+
render: (row) => (_jsx(Text, { className: "text-sm tabular-nums", children: row.codePoolSize !== null ? row.codePoolSize : "—" })),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
key: "codesAvailable",
|
|
38
|
+
header: "Available",
|
|
39
|
+
render: (row) => (_jsx(Text, { className: "text-sm tabular-nums", children: row.codesAvailable !== null ? row.codesAvailable : "—" })),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: "deliveryMethod",
|
|
43
|
+
header: "Delivery",
|
|
44
|
+
render: (row) => (_jsx("span", { className: "inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium bg-zinc-100 text-zinc-600 dark:bg-slate-800 dark:text-slate-400 capitalize", children: row.deliveryMethod.replace(/-/g, " ") })),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
key: "status",
|
|
48
|
+
header: "Status",
|
|
49
|
+
render: (row) => (_jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize ${row.status === "active"
|
|
50
|
+
? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"
|
|
51
|
+
: "bg-zinc-100 text-zinc-600 dark:bg-slate-800 dark:text-slate-400"}`, children: row.status })),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
key: "createdAt",
|
|
55
|
+
header: "Created",
|
|
56
|
+
render: (row) => _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.createdAt }),
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
export function SellerDigitalCodesView({ onCreateClick, onBulkDelete, }) {
|
|
60
|
+
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
61
|
+
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
62
|
+
const [view] = useState("table");
|
|
63
|
+
const commitSearch = useCallback(() => {
|
|
64
|
+
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
65
|
+
}, [searchInput, table]);
|
|
66
|
+
const resetAll = useCallback(() => {
|
|
67
|
+
table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
|
|
68
|
+
setSearchInput("");
|
|
69
|
+
}, [table]);
|
|
70
|
+
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) || table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT;
|
|
71
|
+
const { rows, total, isLoading, errorMessage } = useSellerListingData({
|
|
72
|
+
queryKey: ["seller", "digital-codes"],
|
|
73
|
+
endpoint: SELLER_ENDPOINTS.PRODUCTS,
|
|
74
|
+
page: table.getNumber(TABLE_KEYS.PAGE, 1),
|
|
75
|
+
pageSize: PAGE_SIZE,
|
|
76
|
+
sorts: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
|
|
77
|
+
filters: "listingType==digital-code",
|
|
78
|
+
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
79
|
+
mapRows: (response) => toRecordArray(response.products).map((item, index) => {
|
|
80
|
+
const dc = (item.digitalCode ?? {});
|
|
81
|
+
return {
|
|
82
|
+
id: toStringValue(item.id, `dc-${index}`),
|
|
83
|
+
raw: item,
|
|
84
|
+
title: toStringValue(item.productTitle ?? item.title, "Untitled"),
|
|
85
|
+
price: toRupees(item.price),
|
|
86
|
+
codePoolSize: typeof dc.codePoolSize === "number" ? dc.codePoolSize : null,
|
|
87
|
+
codesAvailable: typeof dc.codesAvailable === "number" ? dc.codesAvailable : null,
|
|
88
|
+
deliveryMethod: toStringValue(dc.codeDeliveryMethod, "auto-claim"),
|
|
89
|
+
status: toStringValue(item.status, "draft"),
|
|
90
|
+
createdAt: toRelativeDate(item.createdAt),
|
|
91
|
+
};
|
|
92
|
+
}),
|
|
93
|
+
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
94
|
+
});
|
|
95
|
+
const currentPage = table.getNumber(TABLE_KEYS.PAGE, 1);
|
|
96
|
+
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
97
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
98
|
+
const bulkActions = onBulkDelete
|
|
99
|
+
? [{
|
|
100
|
+
id: "bulk-delete",
|
|
101
|
+
label: "Delete selected",
|
|
102
|
+
variant: "danger",
|
|
103
|
+
onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
|
|
104
|
+
}]
|
|
105
|
+
: [];
|
|
106
|
+
const handleCreate = useCallback(() => {
|
|
107
|
+
if (onCreateClick) {
|
|
108
|
+
onCreateClick();
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
window.location.href = String(ROUTES.STORE.DIGITAL_CODES_NEW);
|
|
112
|
+
}
|
|
113
|
+
}, [onCreateClick]);
|
|
114
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search digital code products...", 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 Digital Code" })] }) }), 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 digital code products yet \u2014 sell Steam keys, gift cards, and more" }) })) : (_jsx(DataTable, { columns: COLUMNS, data: rows, keyExtractor: (r) => r.id, selectable: bulkActions.length > 0, selectedIds: selection.selectedIds, onSelectionChange: (ids) => selection.setSelectedIds(ids) }))] })] }));
|
|
115
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface GoogleConfigDraft {
|
|
2
|
+
placeId: string;
|
|
3
|
+
businessName: string;
|
|
4
|
+
isConnected: boolean;
|
|
5
|
+
averageRating?: number;
|
|
6
|
+
totalReviews?: number;
|
|
7
|
+
lastSyncedAt?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface SellerGoogleReviewsViewProps {
|
|
10
|
+
onSave?: (data: Partial<GoogleConfigDraft>) => Promise<void>;
|
|
11
|
+
onSync?: () => Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export declare function SellerGoogleReviewsView({ onSave, onSync, }: SellerGoogleReviewsViewProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useState } from "react";
|
|
4
|
+
import { RefreshCw } from "lucide-react";
|
|
5
|
+
import { Alert, Button, Div, FormField, Heading, Section, Stack, Text, Toggle, } from "../../../ui";
|
|
6
|
+
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
7
|
+
const EMPTY_DRAFT = {
|
|
8
|
+
placeId: "",
|
|
9
|
+
businessName: "",
|
|
10
|
+
isConnected: false,
|
|
11
|
+
};
|
|
12
|
+
export function SellerGoogleReviewsView({ onSave, onSync, }) {
|
|
13
|
+
const [draft, setDraft] = useState(EMPTY_DRAFT);
|
|
14
|
+
const [loading, setLoading] = useState(true);
|
|
15
|
+
const [saving, setSaving] = useState(false);
|
|
16
|
+
const [syncing, setSyncing] = useState(false);
|
|
17
|
+
const [saveMessage, setSaveMessage] = useState(null);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
fetch(SELLER_ENDPOINTS.GOOGLE_REVIEWS, { credentials: "include" })
|
|
20
|
+
.then((r) => r.json())
|
|
21
|
+
.then((j) => {
|
|
22
|
+
const data = j?.data ?? j ?? {};
|
|
23
|
+
setDraft({
|
|
24
|
+
placeId: String(data.placeId ?? ""),
|
|
25
|
+
businessName: String(data.businessName ?? ""),
|
|
26
|
+
isConnected: Boolean(data.isConnected),
|
|
27
|
+
averageRating: typeof data.averageRating === "number" ? data.averageRating : undefined,
|
|
28
|
+
totalReviews: typeof data.totalReviews === "number" ? data.totalReviews : undefined,
|
|
29
|
+
lastSyncedAt: data.lastSyncedAt ? String(data.lastSyncedAt) : undefined,
|
|
30
|
+
});
|
|
31
|
+
})
|
|
32
|
+
.catch(() => { })
|
|
33
|
+
.finally(() => setLoading(false));
|
|
34
|
+
}, []);
|
|
35
|
+
const handleSave = useCallback(async () => {
|
|
36
|
+
setSaving(true);
|
|
37
|
+
setSaveMessage(null);
|
|
38
|
+
try {
|
|
39
|
+
if (onSave) {
|
|
40
|
+
await onSave(draft);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const res = await fetch(SELLER_ENDPOINTS.GOOGLE_REVIEWS, {
|
|
44
|
+
method: "PUT",
|
|
45
|
+
headers: { "Content-Type": "application/json" },
|
|
46
|
+
credentials: "include",
|
|
47
|
+
body: JSON.stringify({
|
|
48
|
+
placeId: draft.placeId.trim() || undefined,
|
|
49
|
+
businessName: draft.businessName.trim() || undefined,
|
|
50
|
+
isConnected: draft.isConnected,
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
setSaveMessage({ text: res.ok ? "Settings saved." : "Save failed.", ok: res.ok });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
setSaveMessage({ text: "Save failed. Please try again.", ok: false });
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
setSaving(false);
|
|
61
|
+
}
|
|
62
|
+
}, [draft, onSave]);
|
|
63
|
+
const handleSync = useCallback(async () => {
|
|
64
|
+
setSyncing(true);
|
|
65
|
+
setSaveMessage(null);
|
|
66
|
+
try {
|
|
67
|
+
if (onSync) {
|
|
68
|
+
await onSync();
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const res = await fetch(SELLER_ENDPOINTS.GOOGLE_REVIEWS_SYNC, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
credentials: "include",
|
|
74
|
+
});
|
|
75
|
+
setSaveMessage({ text: res.ok ? "Sync queued." : "Sync failed.", ok: res.ok });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
setSaveMessage({ text: "Sync failed. Please try again.", ok: false });
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
setSyncing(false);
|
|
83
|
+
}
|
|
84
|
+
}, [onSync]);
|
|
85
|
+
if (loading) {
|
|
86
|
+
return (_jsx("div", { className: "space-y-4 py-6 px-4", children: Array.from({ length: 3 }).map((_, i) => (_jsx("div", { className: "h-12 animate-pulse rounded-xl border border-zinc-100 dark:border-slate-700 bg-zinc-50 dark:bg-slate-800" }, i))) }));
|
|
87
|
+
}
|
|
88
|
+
const formattedLastSync = draft.lastSyncedAt
|
|
89
|
+
? new Date(draft.lastSyncedAt).toLocaleString("en-IN", {
|
|
90
|
+
dateStyle: "medium",
|
|
91
|
+
timeStyle: "short",
|
|
92
|
+
})
|
|
93
|
+
: "Never";
|
|
94
|
+
return (_jsx("div", { className: "py-4 px-3 sm:px-4 max-w-2xl", children: _jsxs(Stack, { gap: "lg", children: [_jsxs(Section, { children: [_jsx(Heading, { level: 3, className: "mb-1", children: "Google Business Settings" }), _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mb-4", children: "Connect your Google Business profile to sync reviews to your storefront." }), _jsxs(Stack, { gap: "md", children: [_jsx(Toggle, { checked: draft.isConnected, onChange: (v) => setDraft((d) => ({ ...d, isConnected: v })), label: "Show Google reviews on my store page" }), _jsx(FormField, { name: "placeId", label: "Google Place ID", type: "text", value: draft.placeId, onChange: (v) => setDraft((d) => ({ ...d, placeId: v })), placeholder: "ChIJ\u2026", helpText: "Find your Place ID at developers.google.com/maps/documentation/places/web-service/place-id" }), _jsx(FormField, { name: "businessName", label: "Business name", type: "text", value: draft.businessName, onChange: (v) => setDraft((d) => ({ ...d, businessName: v })), placeholder: "Pok\u00E9mon Palace" })] })] }), (draft.averageRating !== undefined || draft.totalReviews !== undefined) && (_jsxs(Section, { children: [_jsx(Heading, { level: 3, className: "mb-3", children: "Review Stats" }), _jsxs("div", { className: "flex flex-wrap gap-6", children: [draft.averageRating !== undefined && (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] uppercase tracking-wide", children: "Avg rating" }), _jsx(Text, { className: "text-2xl font-bold tabular-nums", children: draft.averageRating.toFixed(1) })] })), draft.totalReviews !== undefined && (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] uppercase tracking-wide", children: "Total reviews" }), _jsx(Text, { className: "text-2xl font-bold tabular-nums", children: draft.totalReviews })] })), _jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] uppercase tracking-wide", children: "Last synced" }), _jsx(Text, { className: "text-sm", children: formattedLastSync })] })] })] })), saveMessage && (_jsx(Alert, { variant: saveMessage.ok ? "success" : "error", children: saveMessage.text })), _jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-[var(--appkit-color-border)] pt-4", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: handleSync, isLoading: syncing, disabled: saving || !draft.placeId.trim(), className: "flex items-center gap-1.5", children: [_jsx(RefreshCw, { className: "h-3.5 w-3.5" }), "Sync now"] }), _jsx(Button, { variant: "primary", size: "sm", onClick: handleSave, isLoading: saving, disabled: syncing, children: "Save Settings" })] })] }) }));
|
|
95
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface SellerGroupedListingsViewProps {
|
|
2
|
+
onCreateClick?: () => void;
|
|
3
|
+
onEditClick?: (id: string) => void;
|
|
4
|
+
onDeleteClick?: (id: string) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare function SellerGroupedListingsView({ onCreateClick, onEditClick, onDeleteClick, }: SellerGroupedListingsViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
4
|
+
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
|
+
import { Badge, BulkActionBar, Button, ListingToolbar, Pagination } from "../../../ui";
|
|
7
|
+
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
|
+
import { toRecordArray, toRelativeDate, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
|
|
9
|
+
import { DataTable } from "../../admin/components/DataTable";
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Config
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
const PAGE_SIZE = 25;
|
|
14
|
+
const DEFAULT_SORT = "-createdAt";
|
|
15
|
+
const SORT_OPTIONS = [
|
|
16
|
+
{ value: "-createdAt", label: "Newest" },
|
|
17
|
+
{ value: "createdAt", label: "Oldest" },
|
|
18
|
+
{ value: "title", label: "Title A–Z" },
|
|
19
|
+
{ value: "-title", label: "Title Z–A" },
|
|
20
|
+
];
|
|
21
|
+
const THEME_LABELS = {
|
|
22
|
+
related: "Related",
|
|
23
|
+
character: "Character",
|
|
24
|
+
lineage: "Lineage",
|
|
25
|
+
set: "Set",
|
|
26
|
+
generic: "Generic",
|
|
27
|
+
};
|
|
28
|
+
const VISIBILITY_BADGE = {
|
|
29
|
+
visible: "success",
|
|
30
|
+
hidden: "warning",
|
|
31
|
+
};
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Columns
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
const COLUMNS = [
|
|
36
|
+
{
|
|
37
|
+
key: "title",
|
|
38
|
+
header: "Title",
|
|
39
|
+
render: (r) => _jsx("span", { className: "font-medium text-zinc-900 dark:text-zinc-100", children: r.title }),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: "groupTheme",
|
|
43
|
+
header: "Theme",
|
|
44
|
+
render: (r) => (_jsx(Badge, { variant: "default", children: THEME_LABELS[r.groupTheme] ?? r.groupTheme })),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
key: "productCount",
|
|
48
|
+
header: "Products",
|
|
49
|
+
render: (r) => _jsx("span", { className: "text-sm text-zinc-600 dark:text-zinc-400", children: r.productCount }),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
key: "visibilityStatus",
|
|
53
|
+
header: "Visibility",
|
|
54
|
+
render: (r) => (_jsx(Badge, { variant: VISIBILITY_BADGE[r.visibilityStatus] ?? "default", children: r.visibilityStatus === "visible" ? "Visible" : "Hidden" })),
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
key: "isActive",
|
|
58
|
+
header: "Status",
|
|
59
|
+
render: (r) => (_jsx(Badge, { variant: r.isActive ? "success" : "default", children: r.isActive ? "Active" : "Inactive" })),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
key: "createdAt",
|
|
63
|
+
header: "Created",
|
|
64
|
+
render: (r) => _jsx("span", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: r.createdAt }),
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
export function SellerGroupedListingsView({ onCreateClick, onEditClick, onDeleteClick, }) {
|
|
68
|
+
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
69
|
+
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
70
|
+
const resetAll = useCallback(() => {
|
|
71
|
+
table.setMany({ q: "", sort: "" });
|
|
72
|
+
setSearchInput("");
|
|
73
|
+
}, [table]);
|
|
74
|
+
const commitSearch = useCallback(() => {
|
|
75
|
+
table.set("q", searchInput.trim());
|
|
76
|
+
}, [searchInput, table]);
|
|
77
|
+
const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT;
|
|
78
|
+
const { rows, total, isLoading, errorMessage } = useSellerListingData({
|
|
79
|
+
queryKey: ["seller", "grouped-listings"],
|
|
80
|
+
endpoint: SELLER_ENDPOINTS.GROUPED_LISTINGS,
|
|
81
|
+
page: table.getNumber("page", 1),
|
|
82
|
+
pageSize: PAGE_SIZE,
|
|
83
|
+
sorts: table.get("sort") || DEFAULT_SORT,
|
|
84
|
+
q: table.get("q") || undefined,
|
|
85
|
+
mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
|
|
86
|
+
id: toStringValue(item.id, `group-${index}`),
|
|
87
|
+
title: toStringValue(item.title, "Untitled group"),
|
|
88
|
+
groupTheme: toStringValue(item.groupTheme, "generic"),
|
|
89
|
+
productCount: Array.isArray(item.productIds) ? item.productIds.length : 0,
|
|
90
|
+
isActive: item.isActive === true,
|
|
91
|
+
visibilityStatus: toStringValue(item.visibilityStatus, "hidden"),
|
|
92
|
+
createdAt: toRelativeDate(item.createdAt),
|
|
93
|
+
})),
|
|
94
|
+
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
95
|
+
});
|
|
96
|
+
const currentPage = table.getNumber("page", 1);
|
|
97
|
+
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
98
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
99
|
+
const bulkActions = [
|
|
100
|
+
{
|
|
101
|
+
id: "delete",
|
|
102
|
+
label: "Delete selected",
|
|
103
|
+
variant: "danger",
|
|
104
|
+
onClick: () => {
|
|
105
|
+
for (const id of selection.selectedIds)
|
|
106
|
+
onDeleteClick?.(id);
|
|
107
|
+
selection.clearSelection();
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { searchValue: searchInput, searchPlaceholder: "Search grouped listings", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: onCreateClick ? (_jsx(Button, { size: "sm", onClick: onCreateClick, children: "+ New Group" })) : null }), selection.selectedCount > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), 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) }) })), _jsx(DataTable, { columns: COLUMNS, rows: rows, isLoading: isLoading, emptyLabel: errorMessage ?? "No grouped listings yet", selectedIds: selection.selectedIdSet, onToggleSelect: (id, _selected) => selection.toggle(id), onToggleSelectAll: (_next) => selection.toggleAll(), renderRowActions: (row) => (_jsxs("div", { className: "flex gap-1", children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: () => onEditClick?.(row.id), children: "Edit" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => onDeleteClick?.(row.id), children: "Delete" })] })) })] }));
|
|
112
|
+
}
|
|
@@ -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
|
|
@@ -200,6 +203,24 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
200
203
|
className: "w-28",
|
|
201
204
|
render: (row) => row.physicalLocation ? (_jsxs("span", { className: "text-xs font-mono text-zinc-500 dark:text-zinc-400", children: [row.physicalLocation.zone, "/", row.physicalLocation.shelf, "/", row.physicalLocation.bin] })) : (_jsx("span", { className: "text-xs text-zinc-400 dark:text-zinc-600", children: "\u2014" })),
|
|
202
205
|
},
|
|
206
|
+
{
|
|
207
|
+
key: "shipping",
|
|
208
|
+
header: "Shipping",
|
|
209
|
+
className: "w-32",
|
|
210
|
+
render: (row) => {
|
|
211
|
+
const r = row;
|
|
212
|
+
return (_jsx("span", { className: "text-xs text-zinc-600 dark:text-zinc-300", children: r.shippingMethod ?? r.carrier ?? "—" }));
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
key: "weight",
|
|
217
|
+
header: "Weight",
|
|
218
|
+
className: "w-20 text-right",
|
|
219
|
+
render: (row) => {
|
|
220
|
+
const r = row;
|
|
221
|
+
return (_jsx("span", { className: "text-xs text-zinc-500 dark:text-zinc-400 tabular-nums", children: r.weightGrams ? `${r.weightGrams} g` : "—" }));
|
|
222
|
+
},
|
|
223
|
+
},
|
|
203
224
|
{
|
|
204
225
|
key: "updatedAt",
|
|
205
226
|
header: "Date",
|
|
@@ -207,7 +228,20 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
207
228
|
render: (row) => _jsx("span", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: row.updatedAt }),
|
|
208
229
|
},
|
|
209
230
|
];
|
|
210
|
-
const
|
|
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]);
|
|
211
245
|
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
212
246
|
const handlePrintPackingSlips = useCallback(() => {
|
|
213
247
|
const ids = selection.selectedIds.join(",");
|
|
@@ -224,6 +258,17 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
224
258
|
});
|
|
225
259
|
setSetLocationOpen(false);
|
|
226
260
|
}, [selection.selectedIds]);
|
|
261
|
+
// S-STORE-5-A — bulk order selection → single payout request.
|
|
262
|
+
const requestPayoutForSelection = useCallback(async () => {
|
|
263
|
+
if (!selection.selectedIds.length)
|
|
264
|
+
return;
|
|
265
|
+
await fetch("/api/store/payouts/request", {
|
|
266
|
+
method: "POST",
|
|
267
|
+
headers: { "Content-Type": "application/json" },
|
|
268
|
+
body: JSON.stringify({ orderIds: selection.selectedIds }),
|
|
269
|
+
}).catch(() => null);
|
|
270
|
+
selection.clearSelection();
|
|
271
|
+
}, [selection]);
|
|
227
272
|
const bulkActions = [
|
|
228
273
|
{
|
|
229
274
|
id: ACTIONS.STORE["print-packing-slips"].id,
|
|
@@ -237,6 +282,12 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
237
282
|
icon: _jsx(MapPin, { className: "w-4 h-4" }),
|
|
238
283
|
onClick: () => setSetLocationOpen(true),
|
|
239
284
|
},
|
|
285
|
+
{
|
|
286
|
+
id: ACTIONS.STORE["request-payout"].id,
|
|
287
|
+
label: ACTIONS.STORE["request-payout"].label,
|
|
288
|
+
onClick: () => void requestPayoutForSelection(),
|
|
289
|
+
variant: "primary",
|
|
290
|
+
},
|
|
240
291
|
];
|
|
241
292
|
if (hasChildren) {
|
|
242
293
|
return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
|
|
@@ -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;
|