@mohasinac/appkit 2.7.56 → 2.7.57
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/shared/actions/action-registry.js +31 -5
- package/dist/client.d.ts +1 -3
- package/dist/client.js +1 -2
- package/dist/constants/field-names.d.ts +3 -0
- package/dist/constants/field-names.js +3 -0
- package/dist/features/about/components/FAQPageView.js +2 -2
- package/dist/features/about/components/FeesView.js +1 -1
- package/dist/features/about/components/PolicyPageView.js +1 -1
- package/dist/features/about/components/PublicProfileView.js +1 -1
- package/dist/features/account/components/AddressesIndexListing.js +1 -1
- package/dist/features/account/components/MessagesView.js +1 -1
- package/dist/features/account/components/NotificationPreferencesPanel.js +12 -3
- package/dist/features/account/components/UserOffersPanel.js +2 -2
- package/dist/features/account/components/UserSidebar.js +3 -3
- package/dist/features/account/components/UserSupportView.js +1 -1
- package/dist/features/admin/components/AdminAllEventEntriesView.js +1 -1
- package/dist/features/admin/components/AdminBidsView.js +5 -1
- package/dist/features/admin/components/AdminBlogView.js +6 -1
- package/dist/features/admin/components/AdminBrandsView.js +6 -1
- package/dist/features/admin/components/AdminBundleEditorView.js +13 -9
- package/dist/features/admin/components/AdminBundlesView.js +3 -1
- package/dist/features/admin/components/AdminCarouselEditorView.js +7 -8
- package/dist/features/admin/components/AdminCarouselView.js +4 -2
- package/dist/features/admin/components/AdminCartsView.js +1 -1
- package/dist/features/admin/components/AdminCategoriesView.js +6 -1
- package/dist/features/admin/components/AdminContactView.js +3 -1
- package/dist/features/admin/components/AdminCouponsView.js +4 -2
- package/dist/features/admin/components/AdminEmployeeEditorView.js +1 -1
- package/dist/features/admin/components/AdminFaqsView.js +3 -1
- package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -1
- package/dist/features/admin/components/AdminFeaturesView.js +4 -0
- package/dist/features/admin/components/AdminMediaView.js +10 -5
- package/dist/features/admin/components/AdminNavigationView.js +1 -1
- package/dist/features/admin/components/AdminNewsletterView.js +3 -1
- package/dist/features/admin/components/AdminNotificationsView.js +6 -1
- package/dist/features/admin/components/AdminOrdersView.js +10 -1
- package/dist/features/admin/components/AdminPayoutsView.js +5 -1
- package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -0
- package/dist/features/admin/components/AdminProductsView.js +13 -1
- package/dist/features/admin/components/AdminReviewsView.js +6 -1
- package/dist/features/admin/components/AdminScammersView.js +1 -1
- package/dist/features/admin/components/AdminSectionsView.js +1 -1
- package/dist/features/admin/components/AdminSessionsView.js +5 -1
- package/dist/features/admin/components/AdminSidebar.js +3 -3
- package/dist/features/admin/components/AdminSiteSettingsView.js +1 -1
- package/dist/features/admin/components/AdminStoreEditorView.js +1 -1
- package/dist/features/admin/components/AdminStoresView.js +6 -1
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +1 -1
- package/dist/features/admin/components/AdminTeamView.js +3 -1
- package/dist/features/admin/components/AdminUserEditorView.js +1 -1
- package/dist/features/admin/components/AdminUsersView.js +5 -1
- package/dist/features/admin/components/AdminViewCards.js +2 -2
- package/dist/features/admin/constants/filter-tabs.d.ts +9 -0
- package/dist/features/admin/constants/filter-tabs.js +3 -0
- package/dist/features/auctions/components/AuctionBottomActions.d.ts +7 -0
- package/dist/features/auctions/components/AuctionBottomActions.js +24 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +7 -7
- package/dist/features/auctions/components/AuctionsListView.js +1 -1
- package/dist/features/auctions/components/MarketplaceAuctionGrid.js +1 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +1 -1
- package/dist/features/auth/components/LoginForm.js +1 -1
- package/dist/features/auth/components/VerifyEmailView.js +2 -2
- package/dist/features/before-after/components/BeforeAfterGallery.js +1 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +1 -1
- package/dist/features/blog/components/BlogIndexListing.js +1 -1
- package/dist/features/blog/components/BlogListView.js +2 -2
- package/dist/features/blog/components/BlogPostView.js +8 -8
- package/dist/features/cart/components/CartDrawer.js +1 -1
- package/dist/features/cart/repository/cart.repository.js +11 -4
- package/dist/features/cart/schemas/firestore.d.ts +3 -0
- package/dist/features/categories/components/BrandDetailPageView.js +1 -1
- package/dist/features/categories/components/CategoriesIndexListing.js +1 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +39 -3
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +4 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +7 -2
- package/dist/features/categories/components/CategoryGrid.js +1 -1
- package/dist/features/categories/components/CategoryProductsView.js +1 -1
- package/dist/features/categories/components/CategoryStoresListing.d.ts +5 -0
- package/dist/features/categories/components/CategoryStoresListing.js +11 -0
- package/dist/features/categories/components/CategoryTableColumns.js +2 -2
- package/dist/features/categories/components/CategoryTree.js +1 -1
- package/dist/features/categories/components/index.d.ts +2 -0
- package/dist/features/categories/components/index.js +1 -0
- package/dist/features/checkout/components/CheckoutStepper.js +1 -1
- package/dist/features/classified/components/ClassifiedIndexListing.js +2 -0
- package/dist/features/classified/components/ClassifiedListView.js +1 -1
- package/dist/features/contact/components/ContactInfoSidebar.js +1 -1
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +2 -0
- package/dist/features/digital-codes/components/DigitalCodesListView.js +1 -1
- package/dist/features/events/components/AdminEventsView.js +5 -1
- package/dist/features/events/components/EventCard.js +1 -1
- package/dist/features/events/components/EventPollWidget.js +1 -1
- package/dist/features/events/components/EventStatusBadge.js +5 -5
- package/dist/features/events/components/EventsIndexListing.js +1 -1
- package/dist/features/events/components/EventsListView.js +2 -2
- package/dist/features/events/components/SpinWheelView.js +4 -2
- package/dist/features/filters/AsyncFacetSection.js +3 -3
- package/dist/features/filters/FilterFacetSection.js +5 -5
- package/dist/features/filters/RangeFilter.js +1 -1
- package/dist/features/forms/Input.js +2 -2
- package/dist/features/forms/Select.js +1 -1
- package/dist/features/forms/Slider.js +1 -1
- package/dist/features/forms/Textarea.js +1 -1
- package/dist/features/forms/utils.d.ts +1 -1
- package/dist/features/forms/utils.js +1 -1
- package/dist/features/history/hooks/useHistory.js +23 -10
- package/dist/features/homepage/components/AdvertisementBanner.js +2 -2
- package/dist/features/homepage/components/FeaturedResultsSection.js +1 -1
- package/dist/features/homepage/components/HeroSection.js +1 -1
- package/dist/features/homepage/components/NewsletterSection.js +1 -1
- package/dist/features/homepage/components/SectionCarousel.js +1 -1
- package/dist/features/live/components/LiveItemsIndexListing.js +2 -0
- package/dist/features/live/components/LiveItemsListView.js +1 -1
- package/dist/features/orders/components/OrdersList.js +12 -12
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
- package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
- package/dist/features/pre-orders/components/PreOrderBadge.js +5 -5
- package/dist/features/pre-orders/components/PreOrderBottomActions.d.ts +5 -0
- package/dist/features/pre-orders/components/PreOrderBottomActions.js +22 -0
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +5 -5
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +47 -0
- package/dist/features/pre-orders/components/PreOrdersListView.js +1 -1
- package/dist/features/pre-orders/components/PreorderCard.js +3 -3
- package/dist/features/products/components/AuctionsIndexListing.js +23 -0
- package/dist/features/products/components/BidHistory.js +2 -2
- package/dist/features/products/components/CompareOverlay.js +1 -1
- package/dist/features/products/components/CustomFieldsEditor.js +1 -1
- package/dist/features/products/components/CustomSectionTabContent.js +1 -1
- package/dist/features/products/components/CustomSectionsEditor.js +1 -1
- package/dist/features/products/components/GroupSettingsPanel.js +2 -2
- package/dist/features/products/components/MakeOfferButton.js +1 -1
- package/dist/features/products/components/PrizeDrawBottomActions.d.ts +6 -0
- package/dist/features/products/components/PrizeDrawBottomActions.js +24 -0
- package/dist/features/products/components/PrizeDrawDetailPageView.js +3 -5
- package/dist/features/products/components/PrizeDrawEntryActions.js +5 -18
- package/dist/features/products/components/PrizeRevealModal.js +5 -2
- package/dist/features/products/components/ProductDetailActions.d.ts +6 -1
- package/dist/features/products/components/ProductDetailActions.js +36 -6
- package/dist/features/products/components/ProductDetailPageView.js +17 -16
- package/dist/features/products/components/ProductGalleryClient.js +1 -1
- package/dist/features/products/components/ProductGrid.js +6 -6
- package/dist/features/products/components/ProductTabs.js +1 -1
- package/dist/features/products/components/ProductTabsShell.js +1 -1
- package/dist/features/products/components/ProductsIndexListing.js +47 -0
- package/dist/features/products/components/RelatedProducts.js +1 -1
- package/dist/features/products/components/ShowGroupSection.js +1 -1
- package/dist/features/products/components/SublistingCarouselSection.js +1 -1
- package/dist/features/products/components/SublistingCategorySelect.js +1 -1
- package/dist/features/products/components/index.d.ts +0 -2
- package/dist/features/products/components/index.js +0 -1
- package/dist/features/products/constants/action-defs.d.ts +7 -0
- package/dist/features/products/constants/action-defs.js +14 -0
- package/dist/features/products/constants/listing-tabs.d.ts +6 -0
- package/dist/features/products/constants/listing-tabs.js +1 -0
- package/dist/features/promotions/components/CouponCard.js +2 -2
- package/dist/features/promotions/components/CouponsIndexListing.js +2 -2
- package/dist/features/reviews/components/ReviewDetailPageView.js +1 -1
- package/dist/features/reviews/components/ReviewDetailShell.js +3 -3
- package/dist/features/reviews/components/ReviewModal.js +2 -2
- package/dist/features/reviews/components/ReviewSummary.js +1 -1
- package/dist/features/reviews/components/ReviewsIndexListing.js +1 -1
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +4 -3
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +1 -1
- package/dist/features/seller/components/SellerAuctionsView.js +10 -3
- package/dist/features/seller/components/SellerBidsView.js +22 -7
- package/dist/features/seller/components/SellerBundlesView.js +10 -3
- package/dist/features/seller/components/SellerClassifiedView.js +10 -3
- package/dist/features/seller/components/SellerCouponsView.js +13 -4
- package/dist/features/seller/components/SellerDigitalCodesView.js +10 -3
- package/dist/features/seller/components/SellerGoogleReviewsView.js +10 -5
- package/dist/features/seller/components/SellerGroupedListingsView.js +2 -0
- package/dist/features/seller/components/SellerLiveView.js +10 -3
- package/dist/features/seller/components/SellerOffersPanel.js +2 -2
- package/dist/features/seller/components/SellerOffersView.js +1 -1
- package/dist/features/seller/components/SellerOrdersView.js +40 -17
- package/dist/features/seller/components/SellerPayoutMethodsView.js +13 -4
- package/dist/features/seller/components/SellerPayoutsView.js +1 -1
- package/dist/features/seller/components/SellerPreOrdersView.js +7 -2
- package/dist/features/seller/components/SellerPrizeDrawsView.js +7 -2
- package/dist/features/seller/components/SellerProductShell.js +27 -9
- package/dist/features/seller/components/SellerProductsCards.js +2 -1
- package/dist/features/seller/components/SellerProductsView.js +21 -8
- package/dist/features/seller/components/SellerReviewsView.js +8 -0
- package/dist/features/seller/components/SellerShippingConfigsView.js +13 -4
- package/dist/features/seller/components/SellerSidebar.js +2 -2
- package/dist/features/seller/components/SellerStoreCategoriesView.js +10 -3
- package/dist/features/seller/components/SellerTemplatesView.js +19 -6
- package/dist/features/shell/StepForm.js +9 -3
- package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
- package/dist/features/site-settings/components/NavPermissionsManager.js +1 -1
- package/dist/features/stores/components/InteractiveStoreCard.js +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +1 -1
- package/dist/features/stores/components/StoreReviewsListing.js +1 -1
- package/dist/features/stores/components/StoresIndexListing.js +17 -1
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +1 -1
- package/dist/features/wishlist/components/WishlistPage.js +2 -2
- package/dist/features/wishlist/hooks/useWishlistToggle.js +6 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +6 -3
- package/dist/react/hooks/use-action-dispatch.d.ts +5 -0
- package/dist/react/hooks/use-action-dispatch.js +10 -0
- package/dist/seed/cart-seed-data.js +50 -3
- package/dist/seed/categories-seed-data.js +80 -16
- package/dist/seed/index.d.ts +3 -0
- package/dist/seed/index.js +3 -0
- package/dist/seed/manifest.js +15 -1
- package/dist/seed/products-classifieds-seed-data.d.ts +10 -0
- package/dist/seed/products-classifieds-seed-data.js +197 -0
- package/dist/seed/products-digital-codes-seed-data.d.ts +10 -0
- package/dist/seed/products-digital-codes-seed-data.js +204 -0
- package/dist/seed/products-live-items-seed-data.d.ts +13 -0
- package/dist/seed/products-live-items-seed-data.js +190 -0
- package/dist/styles.css +16 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/index.d.ts +3 -3
- package/dist/tokens/index.js +3 -3
- package/dist/ui/DataTable.js +3 -3
- package/dist/ui/components/Button.style.css +15 -0
- package/dist/ui/components/Input.js +1 -1
- package/dist/ui/components/ListingToolbar.js +1 -1
- package/dist/ui/components/Select.js +1 -1
- package/dist/ui/components/StatsGrid.js +1 -1
- package/dist/ui/index.d.ts +0 -4
- package/dist/ui/index.js +0 -2
- package/package.json +1 -1
- package/dist/_internal/client/features/seller/print-center/InventoryLabel.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/InventoryLabel.js +0 -49
- package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.d.ts +0 -7
- package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.js +0 -32
- package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.d.ts +0 -28
- package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.js +0 -33
- package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.js +0 -30
- package/dist/_internal/client/features/seller/print-center/PrintCenterView.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/PrintCenterView.js +0 -102
- package/dist/_internal/client/features/seller/print-center/PrintGrid.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/PrintGrid.js +0 -13
- package/dist/_internal/client/features/seller/print-center/StoreCard.d.ts +0 -19
- package/dist/_internal/client/features/seller/print-center/StoreCard.js +0 -30
- package/dist/_internal/client/features/seller/print-center/WebsiteCard.d.ts +0 -11
- package/dist/_internal/client/features/seller/print-center/WebsiteCard.js +0 -25
- package/dist/_internal/client/features/seller/print-center/index.d.ts +0 -11
- package/dist/_internal/client/features/seller/print-center/index.js +0 -10
- package/dist/_internal/client/features/seller/print-center/types.d.ts +0 -20
- package/dist/_internal/client/features/seller/print-center/types.js +0 -20
- package/dist/_internal/client/features/seller/print-center/useInventoryPdf.d.ts +0 -18
- package/dist/_internal/client/features/seller/print-center/useInventoryPdf.js +0 -76
- package/dist/features/account/repository/address.repository.d.ts +0 -17
- package/dist/features/account/repository/address.repository.js +0 -184
- package/dist/features/auctions/components/AuctionCard.d.ts +0 -29
- package/dist/features/auctions/components/AuctionCard.js +0 -38
- package/dist/features/categories/components/BundleAddToCartCta.d.ts +0 -16
- package/dist/features/categories/components/BundleAddToCartCta.js +0 -54
- package/dist/features/categories/components/CategoriesListView.d.ts +0 -31
- package/dist/features/categories/components/CategoriesListView.js +0 -27
- package/dist/features/products/components/AuctionsView.d.ts +0 -6
- package/dist/features/products/components/AuctionsView.js +0 -5
- package/dist/features/products/components/BuyBar.d.ts +0 -21
- package/dist/features/products/components/BuyBar.js +0 -19
- package/dist/features/products/components/PreOrdersView.d.ts +0 -6
- package/dist/features/products/components/PreOrdersView.js +0 -5
- package/dist/features/reviews/components/ReviewsListView.d.ts +0 -31
- package/dist/features/reviews/components/ReviewsListView.js +0 -16
- package/dist/features/stores/components/StoresListView.d.ts +0 -19
- package/dist/features/stores/components/StoresListView.js +0 -25
- package/dist/features/stores/repository/store-address.repository.d.ts +0 -55
- package/dist/features/stores/repository/store-address.repository.js +0 -187
- package/dist/ui/components/BulkActionsBar.d.ts +0 -18
- package/dist/ui/components/BulkActionsBar.js +0 -25
- package/dist/ui/components/CollageLightbox.d.ts +0 -18
- package/dist/ui/components/CollageLightbox.js +0 -41
- package/dist/ui/components/StickyBottomBar.d.ts +0 -22
- package/dist/ui/components/StickyBottomBar.js +0 -20
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useEffect, useState } from "react";
|
|
4
4
|
import { RefreshCw } from "lucide-react";
|
|
5
|
-
import { Alert, Button, Div, FormField, Heading, Section, Stack, Text, Toggle, } from "../../../ui";
|
|
5
|
+
import { Alert, Button, Div, FormField, Heading, Section, Stack, Text, Toggle, useToast, } from "../../../ui";
|
|
6
6
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
7
7
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
8
8
|
const EMPTY_DRAFT = {
|
|
@@ -16,6 +16,7 @@ export function SellerGoogleReviewsView({ onSave, onSync, }) {
|
|
|
16
16
|
const [saving, setSaving] = useState(false);
|
|
17
17
|
const [syncing, setSyncing] = useState(false);
|
|
18
18
|
const [saveMessage, setSaveMessage] = useState(null);
|
|
19
|
+
const { showToast } = useToast();
|
|
19
20
|
useEffect(() => {
|
|
20
21
|
fetch(SELLER_ENDPOINTS.GOOGLE_REVIEWS, { credentials: "include" })
|
|
21
22
|
.then((r) => r.json())
|
|
@@ -53,14 +54,16 @@ export function SellerGoogleReviewsView({ onSave, onSync, }) {
|
|
|
53
54
|
});
|
|
54
55
|
setSaveMessage({ text: res.ok ? "Settings saved." : "Save failed.", ok: res.ok });
|
|
55
56
|
}
|
|
57
|
+
showToast("Google Reviews settings saved.", "success");
|
|
56
58
|
}
|
|
57
|
-
catch {
|
|
59
|
+
catch (err) {
|
|
58
60
|
setSaveMessage({ text: "Save failed. Please try again.", ok: false });
|
|
61
|
+
showToast(err instanceof Error ? err.message : "Failed to save settings.", "error");
|
|
59
62
|
}
|
|
60
63
|
finally {
|
|
61
64
|
setSaving(false);
|
|
62
65
|
}
|
|
63
|
-
}, [draft, onSave]);
|
|
66
|
+
}, [draft, onSave, showToast]);
|
|
64
67
|
const handleSync = useCallback(async () => {
|
|
65
68
|
setSyncing(true);
|
|
66
69
|
setSaveMessage(null);
|
|
@@ -75,14 +78,16 @@ export function SellerGoogleReviewsView({ onSave, onSync, }) {
|
|
|
75
78
|
});
|
|
76
79
|
setSaveMessage({ text: res.ok ? "Sync queued." : "Sync failed.", ok: res.ok });
|
|
77
80
|
}
|
|
81
|
+
showToast("Reviews synced.", "success");
|
|
78
82
|
}
|
|
79
|
-
catch {
|
|
83
|
+
catch (err) {
|
|
80
84
|
setSaveMessage({ text: "Sync failed. Please try again.", ok: false });
|
|
85
|
+
showToast(err instanceof Error ? err.message : "Failed to sync reviews.", "error");
|
|
81
86
|
}
|
|
82
87
|
finally {
|
|
83
88
|
setSyncing(false);
|
|
84
89
|
}
|
|
85
|
-
}, [onSync]);
|
|
90
|
+
}, [onSync, showToast]);
|
|
86
91
|
if (loading) {
|
|
87
92
|
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))) }));
|
|
88
93
|
}
|
|
@@ -4,6 +4,7 @@ import { useState, useCallback } from "react";
|
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
6
|
import { Badge, BulkActionBar, Button, ListingToolbar, Pagination } from "../../../ui";
|
|
7
|
+
import { useBottomActions } from "../../layout";
|
|
7
8
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
9
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
9
10
|
import { toRecordArray, toRelativeDate, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
|
|
@@ -109,5 +110,6 @@ export function SellerGroupedListingsView({ onCreateClick, onEditClick, onDelete
|
|
|
109
110
|
},
|
|
110
111
|
},
|
|
111
112
|
];
|
|
113
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
112
114
|
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: ACTIONS.STORE["edit-listing"].label }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => onDeleteClick?.(row.id), children: ACTIONS.STORE["delete-listing"].label })] })) })] }));
|
|
113
115
|
}
|
|
@@ -4,7 +4,8 @@ import { useState, useCallback } from "react";
|
|
|
4
4
|
import { Plus } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
-
import { BulkActionBar, Button, ConfirmDeleteModal, DataTable, Div, ListingToolbar, Pagination, RowActionMenu, Text, } from "../../../ui";
|
|
7
|
+
import { BulkActionBar, Button, ConfirmDeleteModal, DataTable, Div, ListingToolbar, Pagination, RowActionMenu, Text, useToast, } from "../../../ui";
|
|
8
|
+
import { useBottomActions } from "../../layout";
|
|
8
9
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
10
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
11
|
import { ROUTES } from "../../..";
|
|
@@ -61,6 +62,7 @@ export function SellerLiveView({ onCreateClick, onEditClick, onDelete, onBulkDel
|
|
|
61
62
|
const [view] = useState("table");
|
|
62
63
|
const [deleteTargetId, setDeleteTargetId] = useState(null);
|
|
63
64
|
const [deletingId, setDeletingId] = useState(null);
|
|
65
|
+
const { showToast } = useToast();
|
|
64
66
|
const commitSearch = useCallback(() => {
|
|
65
67
|
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
66
68
|
}, [searchInput, table]);
|
|
@@ -111,12 +113,16 @@ export function SellerLiveView({ onCreateClick, onEditClick, onDelete, onBulkDel
|
|
|
111
113
|
await onDelete(id);
|
|
112
114
|
else
|
|
113
115
|
await fetch(`/api/store/products/${id}`, { method: "DELETE", credentials: "include" });
|
|
116
|
+
showToast("Listing deleted.", "success");
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
showToast(err instanceof Error ? err.message : "Failed to delete listing.", "error");
|
|
114
120
|
}
|
|
115
121
|
finally {
|
|
116
122
|
setDeletingId(null);
|
|
117
123
|
setDeleteTargetId(null);
|
|
118
124
|
}
|
|
119
|
-
}, [onDelete]);
|
|
125
|
+
}, [onDelete, showToast]);
|
|
120
126
|
const handleEdit = useCallback((id) => {
|
|
121
127
|
if (onEditClick)
|
|
122
128
|
onEditClick(id);
|
|
@@ -131,7 +137,8 @@ export function SellerLiveView({ onCreateClick, onEditClick, onDelete, onBulkDel
|
|
|
131
137
|
window.location.href = String(ROUTES.STORE.LIVE_ITEMS_NEW);
|
|
132
138
|
}
|
|
133
139
|
}, [onCreateClick]);
|
|
134
|
-
|
|
140
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
141
|
+
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-400", 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), actions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
135
142
|
{
|
|
136
143
|
label: ACTIONS.STORE["edit-listing"].label,
|
|
137
144
|
onClick: () => handleEdit(row.id),
|
|
@@ -106,7 +106,7 @@ function OfferCard({ offer, onRespond, onUpdate, onNeedsLogin }) {
|
|
|
106
106
|
}
|
|
107
107
|
submit({ action: "counter", offerId: offer.id, counterAmount: amount, sellerNote: sellerNote || undefined });
|
|
108
108
|
}
|
|
109
|
-
return (_jsxs(Div, { className: "rounded-xl border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 p-4 space-y-3", children: [_jsxs(Div, { className: "flex items-start justify-between gap-2 flex-wrap", children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: "font-semibold text-sm text-zinc-900 dark:text-zinc-100 truncate", children: offer.productTitle ?? "Unknown Product" }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 mt-0.5", children: [relativeTime(offer.createdAt), " \u00B7 ", isPendingOffer ? expiresIn(offer.expiresAt) : ""] })] }), _jsx(Badge, { variant: statusVariant(offer.status), className: "shrink-0 capitalize text-xs", children: offer.status })] }), _jsxs(Div, { className: "flex gap-4 flex-wrap", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-
|
|
109
|
+
return (_jsxs(Div, { className: "rounded-xl border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 p-4 space-y-3", children: [_jsxs(Div, { className: "flex items-start justify-between gap-2 flex-wrap", children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: "font-semibold text-sm text-zinc-900 dark:text-zinc-100 truncate", children: offer.productTitle ?? "Unknown Product" }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 mt-0.5", children: [relativeTime(offer.createdAt), " \u00B7 ", isPendingOffer ? expiresIn(offer.expiresAt) : ""] })] }), _jsx(Badge, { variant: statusVariant(offer.status), className: "shrink-0 capitalize text-xs", children: offer.status })] }), _jsxs(Div, { className: "flex gap-4 flex-wrap", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400 uppercase tracking-wide", children: "Listed" }), _jsx(Text, { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: formatRupees(offer.listedPrice) })] }), _jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400 uppercase tracking-wide", children: "Offered" }), _jsx(Text, { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: formatRupees(offer.offerAmount) })] }), offer.counterAmount != null && (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400 uppercase tracking-wide", children: "Your Counter" }), _jsx(Text, { className: "text-sm font-medium text-blue-600 dark:text-blue-400", children: formatRupees(offer.counterAmount) })] }))] }), offer.buyerNote && (_jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 italic", children: ["\"", offer.buyerNote, "\""] })), isPendingOffer && uiState === "idle" && (_jsxs(Div, { className: "flex gap-2 flex-wrap", children: [_jsx(Button, { size: "sm", variant: "primary", onClick: toAccepting, disabled: isPending, children: "Accept" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: toCountering, disabled: isPending, className: "border border-zinc-300 dark:border-zinc-600", children: "Counter" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: toDeclining, disabled: isPending, className: "text-red-600 dark:text-red-400 border border-red-200 dark:border-red-800", children: "Decline" })] })), uiState === "accepting" && (_jsxs(Div, { className: "space-y-2", children: [_jsx(Input, { placeholder: PLACEHOLDER_SELLER_NOTE, value: sellerNote, onChange: (e) => setSellerNote(e.target.value), disabled: isPending }), _jsxs(Div, { className: "flex gap-2", children: [_jsx(Button, { size: "sm", variant: "primary", onClick: handleAccept, disabled: isPending, children: isPending ? "Accepting…" : "Confirm Accept" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: toIdle, disabled: isPending, children: ACTIONS.STORE["cancel-form"].label })] })] })), uiState === "declining" && (_jsxs(Div, { className: "space-y-2", children: [_jsx(Input, { placeholder: PLACEHOLDER_SELLER_NOTE, value: sellerNote, onChange: (e) => setSellerNote(e.target.value), disabled: isPending }), _jsxs(Div, { className: "flex gap-2", children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: handleDecline, disabled: isPending, className: "text-red-600 dark:text-red-400 border border-red-200 dark:border-red-800", children: isPending ? "Declining…" : "Confirm Decline" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: toIdle, disabled: isPending, children: ACTIONS.STORE["cancel-form"].label })] })] })), uiState === "countering" && (_jsxs(Div, { className: "space-y-2", children: [_jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["Suggest a price between the buyer's offer (", formatRupees(offer.offerAmount), ") and your listed price (", formatRupees(offer.listedPrice), ")."] }), _jsx(Input, { type: "number", placeholder: "Your counter amount (\u20B9)", value: counterInput, onChange: (e) => setCounterInput(e.target.value), disabled: isPending }), _jsx(Input, { placeholder: PLACEHOLDER_SELLER_NOTE, value: sellerNote, onChange: (e) => setSellerNote(e.target.value), disabled: isPending }), _jsxs(Div, { className: "flex gap-2", children: [_jsx(Button, { size: "sm", variant: "secondary", onClick: handleCounter, disabled: isPending, children: isPending ? "Sending…" : "Send Counter" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: toIdle, disabled: isPending, children: ACTIONS.STORE["cancel-form"].label })] })] })), error && _jsx(Alert, { variant: "error", children: _jsx(Text, { className: "text-xs", children: error }) })] }));
|
|
110
110
|
}
|
|
111
111
|
export function SellerOffersPanel({ fetchEndpoint = "/api/store/offers", onRespond, className = "", }) {
|
|
112
112
|
const [offers, setOffers] = useState([]);
|
|
@@ -152,5 +152,5 @@ export function SellerOffersPanel({ fetchEndpoint = "/api/store/offers", onRespo
|
|
|
152
152
|
{ value: "expired", label: "Expired" },
|
|
153
153
|
];
|
|
154
154
|
const pending = offers.filter((o) => o.status === "pending").length;
|
|
155
|
-
return (_jsxs(Div, { className: `space-y-4 ${className}`, children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to manage offers. Please log in or create an account to continue." }), _jsxs(Div, { className: "flex items-center justify-between flex-wrap gap-2", children: [_jsxs(Div, { children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold text-zinc-900 dark:text-zinc-100", children: "Offers Received" }), pending > 0 && (_jsxs(Text, { className: "text-xs text-amber-600 dark:text-amber-400 mt-0.5", children: [pending, " pending offer", pending > 1 ? "s" : "", " awaiting your response"] }))] }), _jsx(Button, { size: "sm", variant: "ghost", onClick: loadOffers, disabled: loading, className: "border border-zinc-300 dark:border-zinc-600 text-xs", children: loading ? "Refreshing…" : ACTIONS.STORE["refresh-offers"].label })] }), _jsx(Div, { className: "flex gap-1 flex-wrap", children: STATUS_FILTERS.map((f) => (_jsx(Button, { size: "sm", variant: statusFilter === f.value ? "primary" : "ghost", onClick: () => setStatusFilter(f.value), className: "text-xs px-2 py-1", children: f.label }, f.value))) }), fetchError && _jsx(Alert, { variant: "error", children: _jsx(Text, { className: "text-sm", children: fetchError }) }), loading && (_jsx(Div, { className: "flex justify-center py-12", children: _jsx(Spinner, { size: "lg" }) })), !loading && offers.length === 0 && (_jsx(Div, { className: "text-center py-12", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-
|
|
155
|
+
return (_jsxs(Div, { className: `space-y-4 ${className}`, children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to manage offers. Please log in or create an account to continue." }), _jsxs(Div, { className: "flex items-center justify-between flex-wrap gap-2", children: [_jsxs(Div, { children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold text-zinc-900 dark:text-zinc-100", children: "Offers Received" }), pending > 0 && (_jsxs(Text, { className: "text-xs text-amber-600 dark:text-amber-400 mt-0.5", children: [pending, " pending offer", pending > 1 ? "s" : "", " awaiting your response"] }))] }), _jsx(Button, { size: "sm", variant: "ghost", onClick: loadOffers, disabled: loading, className: "border border-zinc-300 dark:border-zinc-600 text-xs", children: loading ? "Refreshing…" : ACTIONS.STORE["refresh-offers"].label })] }), _jsx(Div, { className: "flex gap-1 flex-wrap", children: STATUS_FILTERS.map((f) => (_jsx(Button, { size: "sm", variant: statusFilter === f.value ? "primary" : "ghost", onClick: () => setStatusFilter(f.value), className: "text-xs px-2 py-1", children: f.label }, f.value))) }), fetchError && _jsx(Alert, { variant: "error", children: _jsx(Text, { className: "text-sm", children: fetchError }) }), loading && (_jsx(Div, { className: "flex justify-center py-12", children: _jsx(Spinner, { size: "lg" }) })), !loading && offers.length === 0 && (_jsx(Div, { className: "text-center py-12", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-400 text-sm", children: "No offers found" }) })), !loading && offers.length > 0 && (_jsx(Div, { className: "space-y-3", children: offers.map((offer) => (_jsx(OfferCard, { offer: offer, onRespond: onRespond, onUpdate: handleUpdate, onNeedsLogin: () => setShowLoginModal(true) }, offer.id))) }))] }));
|
|
156
156
|
}
|
|
@@ -84,5 +84,5 @@ export function SellerOffersView({ children, onAcceptOffer, onRejectOffer, onCou
|
|
|
84
84
|
...(onAcceptOffer ? [{ label: ACTIONS.STORE["accept-offer"].label, onClick: () => void onAcceptOffer(row.id) }] : []),
|
|
85
85
|
...(onCounterOffer ? [{ label: ACTIONS.STORE["counter-offer"].label, onClick: () => onCounterOffer(row.id) }] : []),
|
|
86
86
|
...(onRejectOffer ? [{ label: ACTIONS.STORE["reject-offer"].label, destructive: true, onClick: () => void onRejectOffer(row.id) }] : []),
|
|
87
|
-
] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No offers received", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-
|
|
87
|
+
] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No offers received", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
88
88
|
}
|
|
@@ -5,7 +5,7 @@ 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";
|
|
8
|
-
import { BulkActionBar, Badge, Button, Div, FilterChipGroup, Heading, Input, ListingToolbar, Pagination, ListingViewShell, Select, SideDrawer, Stack, Text } from "../../../ui";
|
|
8
|
+
import { BulkActionBar, Badge, Button, Div, FilterChipGroup, Heading, Input, ListingToolbar, Pagination, ListingViewShell, Select, SideDrawer, Stack, Text, useToast } from "../../../ui";
|
|
9
9
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
10
|
import { SELLER_ORDER_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
11
11
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
@@ -13,6 +13,7 @@ import { PhysicalLocationModal } from "./PhysicalLocationModal";
|
|
|
13
13
|
import { ROUTES } from "../../../constants";
|
|
14
14
|
import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
|
|
15
15
|
import { DataTable } from "../../admin/components/DataTable";
|
|
16
|
+
import { useBottomActions } from "../../layout";
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
17
18
|
// Config
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
@@ -120,6 +121,7 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
120
121
|
const [selectedOrderId, setSelectedOrderId] = useState(null);
|
|
121
122
|
const [setLocationOpen, setSetLocationOpen] = useState(false);
|
|
122
123
|
const dispatch = useActionDispatch();
|
|
124
|
+
const { showToast } = useToast();
|
|
123
125
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
124
126
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
125
127
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -201,7 +203,7 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
201
203
|
key: "physicalLocation",
|
|
202
204
|
header: "Staging",
|
|
203
205
|
className: "w-28",
|
|
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-
|
|
206
|
+
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-400", children: "\u2014" })),
|
|
205
207
|
},
|
|
206
208
|
{
|
|
207
209
|
key: "shipping",
|
|
@@ -251,24 +253,44 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
251
253
|
});
|
|
252
254
|
}, [selection.selectedIds, dispatch]);
|
|
253
255
|
const handleSetLocation = useCallback(async (loc) => {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
256
|
+
try {
|
|
257
|
+
const res = await fetch(SELLER_ENDPOINTS.ORDERS_BULK_LOCATION, {
|
|
258
|
+
method: "PATCH",
|
|
259
|
+
headers: { "Content-Type": "application/json" },
|
|
260
|
+
body: JSON.stringify({ orderIds: selection.selectedIds, physicalLocation: loc }),
|
|
261
|
+
});
|
|
262
|
+
if (!res.ok) {
|
|
263
|
+
const body = await res.json().catch(() => null);
|
|
264
|
+
throw new Error(body?.error ?? "Failed to update location");
|
|
265
|
+
}
|
|
266
|
+
showToast("Location updated.", "success");
|
|
267
|
+
setSetLocationOpen(false);
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
showToast(err instanceof Error ? err.message : "Failed to update location.", "error");
|
|
271
|
+
}
|
|
272
|
+
}, [selection.selectedIds, showToast]);
|
|
261
273
|
// S-STORE-5-A — bulk order selection → single payout request.
|
|
262
274
|
const requestPayoutForSelection = useCallback(async () => {
|
|
263
275
|
if (!selection.selectedIds.length)
|
|
264
276
|
return;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
277
|
+
try {
|
|
278
|
+
const res = await fetch("/api/store/payouts/request", {
|
|
279
|
+
method: "POST",
|
|
280
|
+
headers: { "Content-Type": "application/json" },
|
|
281
|
+
body: JSON.stringify({ orderIds: selection.selectedIds }),
|
|
282
|
+
});
|
|
283
|
+
if (!res.ok) {
|
|
284
|
+
const body = await res.json().catch(() => null);
|
|
285
|
+
throw new Error(body?.error ?? "Failed to request payout");
|
|
286
|
+
}
|
|
287
|
+
showToast("Payout requested.", "success");
|
|
288
|
+
selection.clearSelection();
|
|
289
|
+
}
|
|
290
|
+
catch (err) {
|
|
291
|
+
showToast(err instanceof Error ? err.message : "Failed to request payout.", "error");
|
|
292
|
+
}
|
|
293
|
+
}, [selection, showToast]);
|
|
272
294
|
const bulkActions = [
|
|
273
295
|
{
|
|
274
296
|
id: ACTIONS.STORE["print-packing-slips"].id,
|
|
@@ -292,5 +314,6 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
292
314
|
if (hasChildren) {
|
|
293
315
|
return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
|
|
294
316
|
}
|
|
295
|
-
|
|
317
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
318
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by order ID or buyer name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedIds.length > 0 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+88px)] z-20 px-3 sm:px-4 py-2 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700", children: _jsx(BulkActionBar, { selectedCount: selection.selectedIds.length, onClearSelection: selection.clearSelection, actions: bulkActions }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No orders yet", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: () => selection.toggleAll(), renderRowActions: renderRowActions })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 dark:text-zinc-400 hover:text-rose-500 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), selectedOrderId && (_jsx(OrderDetailDrawer, { orderId: selectedOrderId, apiBase: orderDetailApiBase, onClose: () => setSelectedOrderId(null) })), setLocationOpen && (_jsx(PhysicalLocationModal, { count: selection.selectedIds.length, onSave: handleSetLocation, onClose: () => setSetLocationOpen(false) }))] }));
|
|
296
319
|
}
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useCallback } from "react";
|
|
4
4
|
import { Plus } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
-
import { Button, ConfirmDeleteModal, Div, ListingToolbar, Text, } from "../../../ui";
|
|
6
|
+
import { Button, ConfirmDeleteModal, Div, ListingToolbar, Text, useToast, } from "../../../ui";
|
|
7
7
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
8
|
import { ROUTES } from "../../..";
|
|
9
9
|
import { toRecordArray, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
|
|
@@ -34,6 +34,7 @@ export function SellerPayoutMethodsView({ onCreateClick, onEditClick, onDelete,
|
|
|
34
34
|
const [deleteTargetId, setDeleteTargetId] = useState(null);
|
|
35
35
|
const [deletingId, setDeletingId] = useState(null);
|
|
36
36
|
const [settingDefaultId, setSettingDefaultId] = useState(null);
|
|
37
|
+
const { showToast } = useToast();
|
|
37
38
|
const commitSearch = useCallback(() => {
|
|
38
39
|
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
39
40
|
}, [searchInput, table]);
|
|
@@ -72,12 +73,16 @@ export function SellerPayoutMethodsView({ onCreateClick, onEditClick, onDelete,
|
|
|
72
73
|
});
|
|
73
74
|
}
|
|
74
75
|
refetch?.();
|
|
76
|
+
showToast("Payout method deleted.", "success");
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
showToast(err instanceof Error ? err.message : "Failed to delete payout method.", "error");
|
|
75
80
|
}
|
|
76
81
|
finally {
|
|
77
82
|
setDeletingId(null);
|
|
78
83
|
setDeleteTargetId(null);
|
|
79
84
|
}
|
|
80
|
-
}, [onDelete, refetch]);
|
|
85
|
+
}, [onDelete, refetch, showToast]);
|
|
81
86
|
const handleSetDefault = useCallback(async (id) => {
|
|
82
87
|
setSettingDefaultId(id);
|
|
83
88
|
try {
|
|
@@ -93,11 +98,15 @@ export function SellerPayoutMethodsView({ onCreateClick, onEditClick, onDelete,
|
|
|
93
98
|
});
|
|
94
99
|
}
|
|
95
100
|
refetch?.();
|
|
101
|
+
showToast("Default payout method updated.", "success");
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
showToast(err instanceof Error ? err.message : "Failed to update default.", "error");
|
|
96
105
|
}
|
|
97
106
|
finally {
|
|
98
107
|
setSettingDefaultId(null);
|
|
99
108
|
}
|
|
100
|
-
}, [onSetDefault, refetch]);
|
|
109
|
+
}, [onSetDefault, refetch, showToast]);
|
|
101
110
|
const handleCreate = useCallback(() => {
|
|
102
111
|
if (onCreateClick) {
|
|
103
112
|
onCreateClick();
|
|
@@ -114,7 +123,7 @@ export function SellerPayoutMethodsView({ onCreateClick, onEditClick, onDelete,
|
|
|
114
123
|
window.location.href = String(ROUTES.STORE.PAYOUT_METHODS_EDIT(id));
|
|
115
124
|
}
|
|
116
125
|
}, [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-
|
|
126
|
+
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-400", 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
127
|
? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"
|
|
119
128
|
: "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
129
|
}
|
|
@@ -83,5 +83,5 @@ export function SellerPayoutsView({ children, onViewClick, onExportClick, ...pro
|
|
|
83
83
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search payouts by payout # or amount", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No payouts found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
84
84
|
...(onViewClick ? [{ label: ACTIONS.STORE["view-payout"].label, onClick: () => onViewClick(row.id) }] : []),
|
|
85
85
|
...(onExportClick ? [{ label: ACTIONS.STORE["export-payout"].label, onClick: () => onExportClick(row.id) }] : []),
|
|
86
|
-
] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No payouts found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-
|
|
86
|
+
] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No payouts found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
87
87
|
}
|
|
@@ -4,7 +4,7 @@ import React, { useState, useCallback } from "react";
|
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
-
import { Alert, Badge, Button, ConfirmDeleteModal, Div, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, Row, RowActionMenu, Text } from "../../../ui";
|
|
7
|
+
import { Alert, Badge, Button, ConfirmDeleteModal, Div, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, Row, RowActionMenu, Text, useToast } from "../../../ui";
|
|
8
8
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
9
|
import { SELLER_PRE_ORDER_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
10
10
|
import { ROUTES } from "../../../constants";
|
|
@@ -76,6 +76,7 @@ export function SellerPreOrdersView({ children, onDelete, ...props }) {
|
|
|
76
76
|
const dispatch = useActionDispatch();
|
|
77
77
|
const [deleteTargetId, setDeleteTargetId] = useState(null);
|
|
78
78
|
const [deletingId, setDeletingId] = useState(null);
|
|
79
|
+
const { showToast } = useToast();
|
|
79
80
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
80
81
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
81
82
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -144,12 +145,16 @@ export function SellerPreOrdersView({ children, onDelete, ...props }) {
|
|
|
144
145
|
await onDelete(id);
|
|
145
146
|
else
|
|
146
147
|
await fetch(`/api/store/products/${id}`, { method: "DELETE", credentials: "include" });
|
|
148
|
+
showToast("Pre-order deleted.", "success");
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
showToast(err instanceof Error ? err.message : "Failed to delete pre-order.", "error");
|
|
147
152
|
}
|
|
148
153
|
finally {
|
|
149
154
|
setDeletingId(null);
|
|
150
155
|
setDeleteTargetId(null);
|
|
151
156
|
}
|
|
152
|
-
}, [onDelete]);
|
|
157
|
+
}, [onDelete, showToast]);
|
|
153
158
|
if (hasChildren) {
|
|
154
159
|
return (_jsx(ListingViewShell, { portal: "seller", ...props, children: children }));
|
|
155
160
|
}
|
|
@@ -4,7 +4,7 @@ import React, { useState, useCallback } from "react";
|
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
-
import { Alert, Badge, Button, ConfirmDeleteModal, Div, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, Row, RowActionMenu, Text } from "../../../ui";
|
|
7
|
+
import { Alert, Badge, Button, ConfirmDeleteModal, Div, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, Row, RowActionMenu, Text, useToast } from "../../../ui";
|
|
8
8
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
9
|
import { SELLER_PRIZE_DRAW_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
10
10
|
import { ROUTES } from "../../../constants";
|
|
@@ -73,6 +73,7 @@ export function SellerPrizeDrawsView({ children, onDelete, ...props }) {
|
|
|
73
73
|
const dispatch = useActionDispatch();
|
|
74
74
|
const [deleteTargetId, setDeleteTargetId] = useState(null);
|
|
75
75
|
const [deletingId, setDeletingId] = useState(null);
|
|
76
|
+
const { showToast } = useToast();
|
|
76
77
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
77
78
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
78
79
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -141,12 +142,16 @@ export function SellerPrizeDrawsView({ children, onDelete, ...props }) {
|
|
|
141
142
|
await onDelete(id);
|
|
142
143
|
else
|
|
143
144
|
await fetch(`/api/store/products/${id}`, { method: "DELETE", credentials: "include" });
|
|
145
|
+
showToast("Prize draw deleted.", "success");
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
showToast(err instanceof Error ? err.message : "Failed to delete prize draw.", "error");
|
|
144
149
|
}
|
|
145
150
|
finally {
|
|
146
151
|
setDeletingId(null);
|
|
147
152
|
setDeleteTargetId(null);
|
|
148
153
|
}
|
|
149
|
-
}, [onDelete]);
|
|
154
|
+
}, [onDelete, showToast]);
|
|
150
155
|
if (hasChildren) {
|
|
151
156
|
return (_jsx(ListingViewShell, { portal: "seller", ...props, children: children }));
|
|
152
157
|
}
|
|
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
5
|
import { FormShell, StepForm, StepFormActions, useFormShell } from "../../shell";
|
|
6
6
|
import { FormShellProvider } from "../../../ui/forms";
|
|
7
|
-
import { Alert, Button, Div, FormField, FormGroup, Heading, Section, Stack, Text, Toggle } from "../../../ui";
|
|
7
|
+
import { Alert, Button, Div, FormField, FormGroup, Heading, Section, Stack, Text, Toggle, useToast } from "../../../ui";
|
|
8
8
|
import { ImageUpload, MediaUploadField, MediaUploadList, useMediaUpload } from "../../media";
|
|
9
9
|
import { StoreAddressSelectorCreate } from "../../stores/components/StoreAddressSelectorCreate";
|
|
10
10
|
import { QuickProductForm } from "./QuickProductForm";
|
|
@@ -149,6 +149,7 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
149
149
|
const [stepError, setStepError] = useState(null);
|
|
150
150
|
const { isDirty, markDirty, markClean } = useFormShell();
|
|
151
151
|
const router = useRouter();
|
|
152
|
+
const { showToast } = useToast();
|
|
152
153
|
const { upload: shellUpload } = useMediaUpload();
|
|
153
154
|
// Auto-save in create mode — debounce 2s on any draft change
|
|
154
155
|
const autoSaveTimer = useRef(null);
|
|
@@ -202,13 +203,25 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
202
203
|
router.back();
|
|
203
204
|
}, [onDiscard, router]);
|
|
204
205
|
const handleSave = useCallback(async () => {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
try {
|
|
207
|
+
await onSave(draft);
|
|
208
|
+
markClean();
|
|
209
|
+
showToast("Saved.", "success");
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
showToast(err instanceof Error ? err.message : "Failed to save.", "error");
|
|
213
|
+
}
|
|
214
|
+
}, [draft, onSave, markClean, showToast]);
|
|
208
215
|
const handlePublish = useCallback(async () => {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
216
|
+
try {
|
|
217
|
+
await onPublish({ ...draft, status: "published" });
|
|
218
|
+
markClean();
|
|
219
|
+
showToast("Published.", "success");
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
showToast(err instanceof Error ? err.message : "Failed to publish.", "error");
|
|
223
|
+
}
|
|
224
|
+
}, [draft, onPublish, markClean, showToast]);
|
|
212
225
|
const listingTypeLabel = listingType === "auction"
|
|
213
226
|
? "Auction"
|
|
214
227
|
: listingType === "pre-order"
|
|
@@ -291,9 +304,14 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
291
304
|
setCurrentStep((c) => c + 1);
|
|
292
305
|
}
|
|
293
306
|
else {
|
|
294
|
-
|
|
307
|
+
try {
|
|
308
|
+
await handlePublish();
|
|
309
|
+
}
|
|
310
|
+
catch (err) {
|
|
311
|
+
showToast(err instanceof Error ? err.message : "Something went wrong.", "error");
|
|
312
|
+
}
|
|
295
313
|
}
|
|
296
|
-
}, [currentStep, steps, draft, handlePublish]);
|
|
314
|
+
}, [currentStep, steps, draft, handlePublish, showToast]);
|
|
297
315
|
// Step error badges — run each step's validate against current draft
|
|
298
316
|
const stepValidationErrors = useMemo(() => steps.map((s) => (s.validate ? Boolean(s.validate(draft)) : false)),
|
|
299
317
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Badge, Button, Div, Row, Text } from "../../../ui";
|
|
4
|
+
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
|
|
4
5
|
import { CARD_BORDER, CARD_BORDER_ACTIVE, CARD_GRID_CLS, CARD_LIST_CLS, KIND_BADGE_VARIANT } from "./seller-products-styles";
|
|
5
6
|
function buildHref(row) {
|
|
6
7
|
if (row.listingKind === "auction")
|
|
@@ -14,6 +15,6 @@ export function SellerProductsCards({ view, rows, isLoading, listingKind, select
|
|
|
14
15
|
const isSelected = selectedIds.has(row.id);
|
|
15
16
|
const href = buildHref(row);
|
|
16
17
|
const borderCls = isSelected ? CARD_BORDER_ACTIVE : CARD_BORDER;
|
|
17
|
-
return view === "grid" ? (_jsxs(Div, { className: `group relative rounded-xl border bg-[var(--appkit-color-surface)] overflow-hidden hover:shadow-md transition-shadow ${borderCls}`, children: [_jsx(Div, { className: "absolute top-2 left-2 z-10", children: _jsx("input", { type: "checkbox", checked: isSelected, onChange: () => toggle(row.id), className: "h-4 w-4 rounded border-[var(--appkit-color-border)]", onClick: (e) => e.stopPropagation(), "aria-label": "Select" }) }), _jsxs("a", { href: href, className: "block", children: [row.imageUrl ? (_jsx("img", { src: row.imageUrl, alt: "", className: "w-full aspect-square object-cover" })) : (_jsx(Div, { className: "w-full aspect-square bg-[var(--appkit-color-surface-raised)] flex items-center justify-center text-[var(--appkit-color-text-faint)]", children: "\u2014" })), _jsxs(Div, { className: "p-3 space-y-1", children: [_jsx(Text, { className: "font-medium text-sm line-clamp-1", children: row.primary }), _jsxs(Row, { className: "gap-2 items-center", children: [_jsx(Badge, { variant: KIND_BADGE_VARIANT[row.listingKind] ?? "default", children: row.listingKind }), _jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] line-clamp-1", children: row.secondary })] })] })] }), _jsxs(Div, { className: "border-t border-[var(--appkit-color-border)] flex justify-end gap-1 p-2", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); onEdit(row); }, "aria-label": "Edit", children:
|
|
18
|
+
return view === "grid" ? (_jsxs(Div, { className: `group relative rounded-xl border bg-[var(--appkit-color-surface)] overflow-hidden hover:shadow-md transition-shadow ${borderCls}`, children: [_jsx(Div, { className: "absolute top-2 left-2 z-10", children: _jsx("input", { type: "checkbox", checked: isSelected, onChange: () => toggle(row.id), className: "h-4 w-4 rounded border-[var(--appkit-color-border)]", onClick: (e) => e.stopPropagation(), "aria-label": "Select" }) }), _jsxs("a", { href: href, className: "block", children: [row.imageUrl ? (_jsx("img", { src: row.imageUrl, alt: "", className: "w-full aspect-square object-cover" })) : (_jsx(Div, { className: "w-full aspect-square bg-[var(--appkit-color-surface-raised)] flex items-center justify-center text-[var(--appkit-color-text-faint)]", children: "\u2014" })), _jsxs(Div, { className: "p-3 space-y-1", children: [_jsx(Text, { className: "font-medium text-sm line-clamp-1", children: row.primary }), _jsxs(Row, { className: "gap-2 items-center", children: [_jsx(Badge, { variant: KIND_BADGE_VARIANT[row.listingKind] ?? "default", children: row.listingKind }), _jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] line-clamp-1", children: row.secondary })] })] })] }), _jsxs(Div, { className: "border-t border-[var(--appkit-color-border)] flex justify-end gap-1 p-2", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); onEdit(row); }, "aria-label": "Edit", children: ROW_ACTION_META[ROW_ACTION_ID.EDIT].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); onDuplicate(row); }, "aria-label": "Duplicate", children: ROW_ACTION_META[ROW_ACTION_ID.DUPLICATE].label }), onDelete && (_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); onDelete(row); }, "aria-label": "Delete", children: ROW_ACTION_META[ROW_ACTION_ID.DELETE].label }))] })] }, row.id)) : (_jsxs(Div, { className: `flex items-center gap-3 rounded-lg border bg-[var(--appkit-color-surface)] px-3 py-2 hover:bg-[var(--appkit-color-surface-raised)] ${borderCls}`, children: [_jsx("input", { type: "checkbox", checked: isSelected, onChange: () => toggle(row.id), className: "h-4 w-4 rounded border-[var(--appkit-color-border)]", "aria-label": "Select" }), row.imageUrl ? (_jsx("img", { src: row.imageUrl, alt: "", className: "w-12 h-12 rounded object-cover" })) : (_jsx(Div, { className: "w-12 h-12 rounded bg-[var(--appkit-color-surface-raised)]" })), _jsxs("a", { href: href, className: "flex-1 min-w-0", children: [_jsx(Text, { className: "font-medium text-sm line-clamp-1", children: row.primary }), _jsxs(Row, { className: "gap-2 items-center", children: [_jsx(Badge, { variant: KIND_BADGE_VARIANT[row.listingKind] ?? "default", children: row.listingKind }), _jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] line-clamp-1", children: row.secondary })] })] }), _jsxs(Row, { className: "gap-1 flex-shrink-0", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => onEdit(row), "aria-label": "Edit", children: ROW_ACTION_META[ROW_ACTION_ID.EDIT].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => onDuplicate(row), "aria-label": "Duplicate", children: ROW_ACTION_META[ROW_ACTION_ID.DUPLICATE].label }), onDelete && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => onDelete(row), "aria-label": "Delete", children: ROW_ACTION_META[ROW_ACTION_ID.DELETE].label }))] })] }, row.id));
|
|
18
19
|
})] }));
|
|
19
20
|
}
|