@mohasinac/appkit 2.7.55 → 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
|
@@ -6,7 +6,7 @@ import { Eye, EyeOff, Pencil, Trash2, Printer, MapPin } from "lucide-react";
|
|
|
6
6
|
import { PhysicalLocationModal } from "./PhysicalLocationModal";
|
|
7
7
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
8
8
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
9
|
-
import { Alert, Badge, BulkActionBar, Button, Div, ListingToolbar, ListingViewShell, Pagination, Row, Span, Text } from "../../../ui";
|
|
9
|
+
import { Alert, Badge, BulkActionBar, Button, Div, ListingToolbar, ListingViewShell, Pagination, Row, Span, Text, useToast } from "../../../ui";
|
|
10
10
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
11
|
import { SELLER_PRODUCT_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
12
12
|
import { ROUTES } from "../../../constants";
|
|
@@ -17,6 +17,7 @@ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
|
17
17
|
import { SellerProductsCards } from "./SellerProductsCards";
|
|
18
18
|
import { SellerProductsFilterDrawer } from "./SellerProductsFilterDrawer";
|
|
19
19
|
import { KIND_BADGE_VARIANT } from "./seller-products-styles";
|
|
20
|
+
import { useBottomActions } from "../../layout";
|
|
20
21
|
const PAGE_SIZE = 25;
|
|
21
22
|
const FILTER_KEYS = [
|
|
22
23
|
"status",
|
|
@@ -104,6 +105,7 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, children, .
|
|
|
104
105
|
const hasChildren = React.Children.count(children) > 0;
|
|
105
106
|
const [view, setView] = useState("table");
|
|
106
107
|
const dispatch = useActionDispatch();
|
|
108
|
+
const { showToast } = useToast();
|
|
107
109
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
108
110
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
109
111
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -263,13 +265,23 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, children, .
|
|
|
263
265
|
});
|
|
264
266
|
}, [selection.selectedIds, dispatch]);
|
|
265
267
|
const handleSetLocation = useCallback(async (loc) => {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
try {
|
|
269
|
+
const res = await fetch(SELLER_ENDPOINTS.PRODUCTS_BULK_LOCATION, {
|
|
270
|
+
method: "PATCH",
|
|
271
|
+
headers: { "Content-Type": "application/json" },
|
|
272
|
+
body: JSON.stringify({ productIds: selection.selectedIds, physicalLocation: loc }),
|
|
273
|
+
});
|
|
274
|
+
if (!res.ok) {
|
|
275
|
+
const body = await res.json().catch(() => null);
|
|
276
|
+
throw new Error(body?.error ?? "Failed to update location");
|
|
277
|
+
}
|
|
278
|
+
showToast("Location updated.", "success");
|
|
279
|
+
setSetLocationOpen(false);
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
showToast(err instanceof Error ? err.message : "Failed to update location.", "error");
|
|
283
|
+
}
|
|
284
|
+
}, [selection.selectedIds, showToast]);
|
|
273
285
|
const bulkActions = [
|
|
274
286
|
{
|
|
275
287
|
id: ACTIONS.STORE["print-labels"].id,
|
|
@@ -284,6 +296,7 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, children, .
|
|
|
284
296
|
onClick: () => setSetLocationOpen(true),
|
|
285
297
|
},
|
|
286
298
|
];
|
|
299
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
287
300
|
return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search products by name\u2026", 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, toggles: [
|
|
288
301
|
{ label: "Show sold", active: showSold, onChange: (next) => table.set("showSold", next ? "true" : "") },
|
|
289
302
|
], extra: onCreateClick ? (_jsx(Button, { variant: "primary", size: "sm", onClick: onCreateClick, children: "+ New Listing" })) : null }), _jsx(TypeDropdown, { active: listingKind, onChange: handleKindChange }), totalPages > 1 && (_jsx(Div, { className: "sticky z-10 flex justify-center bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] px-3 py-1.5", style: { top: "calc(var(--header-height, 0px) + 44px)" }, children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedIds.length > 0 && (_jsx(Div, { className: "sticky z-20 px-3 lg:px-4 py-2 bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)]", style: { top: "calc(var(--header-height, 0px) + 88px)" }, children: _jsx(BulkActionBar, { selectedCount: selection.selectedIds.length, onClearSelection: selection.clearSelection, actions: bulkActions }) })), _jsxs(Div, { className: "py-4 px-3 lg:px-4", children: [errorMessage && (_jsx(Alert, { variant: "error", className: "mb-4", children: errorMessage })), view !== "table" && (_jsx(SellerProductsCards, { view: view, rows: visibleRows, isLoading: isLoading, listingKind: listingKind, selectedIds: selection.selectedIdSet, toggle: selection.toggle, onEdit: handleEdit, onDuplicate: (row) => void handleDuplicate(row), onDelete: onDeleteProduct ? (row) => void handleDelete(row) : undefined })), view === "table" && (_jsx(DataTable, { columns: PRODUCT_COLUMNS, rows: visibleRows, isLoading: isLoading, emptyLabel: listingKind !== "all"
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
4
|
import { Alert, Badge, BulkActionBar, Button, Checkbox, Div, Modal, Row, Select, SideDrawer, Stack, Text, Textarea, } from "../../../ui";
|
|
5
5
|
import { StackedViewShell } from "../../../ui";
|
|
6
|
+
import { useBottomActions } from "../../layout";
|
|
6
7
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
7
8
|
const STAR = "★";
|
|
8
9
|
const EMPTY_STAR = "☆";
|
|
@@ -141,6 +142,13 @@ export function SellerReviewsView({ reviewsApiBase = "/api/store/reviews", reply
|
|
|
141
142
|
setReplySaving(false);
|
|
142
143
|
}
|
|
143
144
|
};
|
|
145
|
+
useBottomActions(selectedIds.size > 0 ? { bulk: { selectedCount: selectedIds.size, onClearSelection: clearSelection, actions: [
|
|
146
|
+
{
|
|
147
|
+
id: "bulk-reply",
|
|
148
|
+
label: "Reply to selected",
|
|
149
|
+
onClick: () => setBulkReplyOpen(true),
|
|
150
|
+
},
|
|
151
|
+
] } } : {});
|
|
144
152
|
return (_jsxs(_Fragment, { children: [_jsx(StackedViewShell, { portal: "seller", title: "Reviews", sections: [
|
|
145
153
|
_jsxs(Stack, { gap: "lg", children: [_jsxs(Div, { className: "flex flex-wrap items-center gap-3", children: [_jsx(Select, { value: rating, onChange: (e) => { setRating(e.target.value); setPage(1); }, "aria-label": "Filter by rating", options: [
|
|
146
154
|
{ value: "", label: "All ratings" },
|
|
@@ -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, DataTable, Div, ListingToolbar, RowActionMenu, Text, } from "../../../ui";
|
|
6
|
+
import { Button, ConfirmDeleteModal, DataTable, Div, ListingToolbar, RowActionMenu, Text, useToast, } from "../../../ui";
|
|
7
7
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
8
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
9
9
|
import { ROUTES } from "../../..";
|
|
@@ -46,6 +46,7 @@ export function SellerShippingConfigsView({ onCreateClick, onEditClick, onDelete
|
|
|
46
46
|
const [deleteTargetId, setDeleteTargetId] = useState(null);
|
|
47
47
|
const [deletingId, setDeletingId] = useState(null);
|
|
48
48
|
const [settingDefaultId, setSettingDefaultId] = useState(null);
|
|
49
|
+
const { showToast } = useToast();
|
|
49
50
|
const commitSearch = useCallback(() => {
|
|
50
51
|
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
51
52
|
}, [searchInput, table]);
|
|
@@ -84,12 +85,16 @@ export function SellerShippingConfigsView({ onCreateClick, onEditClick, onDelete
|
|
|
84
85
|
});
|
|
85
86
|
}
|
|
86
87
|
refetch?.();
|
|
88
|
+
showToast("Shipping config deleted.", "success");
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
showToast(err instanceof Error ? err.message : "Failed to delete config.", "error");
|
|
87
92
|
}
|
|
88
93
|
finally {
|
|
89
94
|
setDeletingId(null);
|
|
90
95
|
setDeleteTargetId(null);
|
|
91
96
|
}
|
|
92
|
-
}, [onDelete, refetch]);
|
|
97
|
+
}, [onDelete, refetch, showToast]);
|
|
93
98
|
const handleSetDefault = useCallback(async (id) => {
|
|
94
99
|
setSettingDefaultId(id);
|
|
95
100
|
try {
|
|
@@ -105,11 +110,15 @@ export function SellerShippingConfigsView({ onCreateClick, onEditClick, onDelete
|
|
|
105
110
|
});
|
|
106
111
|
}
|
|
107
112
|
refetch?.();
|
|
113
|
+
showToast("Default shipping config updated.", "success");
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
showToast(err instanceof Error ? err.message : "Failed to update default.", "error");
|
|
108
117
|
}
|
|
109
118
|
finally {
|
|
110
119
|
setSettingDefaultId(null);
|
|
111
120
|
}
|
|
112
|
-
}, [onSetDefault, refetch]);
|
|
121
|
+
}, [onSetDefault, refetch, showToast]);
|
|
113
122
|
const handleCreate = useCallback(() => {
|
|
114
123
|
if (onCreateClick) {
|
|
115
124
|
onCreateClick();
|
|
@@ -126,7 +135,7 @@ export function SellerShippingConfigsView({ onCreateClick, onEditClick, onDelete
|
|
|
126
135
|
window.location.href = String(ROUTES.STORE.SHIPPING_CONFIGS_EDIT(id));
|
|
127
136
|
}
|
|
128
137
|
}, [onEditClick]);
|
|
129
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search shipping configs...", 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 Config" })] }) }), _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: 3 }).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 ? (_jsxs(Div, { className: "py-16 text-center", children: [_jsx(Text, { className: "text-zinc-400 dark:text-zinc-
|
|
138
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search shipping configs...", 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 Config" })] }) }), _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: 3 }).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 ? (_jsxs(Div, { className: "py-16 text-center", children: [_jsx(Text, { className: "text-zinc-400 dark:text-zinc-400", children: "No shipping configs yet \u2014 define your first shipping rule" }), _jsx("div", { className: "mt-4", children: _jsx(Button, { size: "sm", onClick: handleCreate, children: "Add shipping config" }) })] })) : (_jsx(DataTable, { columns: COLUMNS, data: rows, keyExtractor: (r) => r.id, actions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
130
139
|
{
|
|
131
140
|
label: ACTIONS.STORE["edit-listing"].label,
|
|
132
141
|
onClick: () => handleEdit(row.id),
|
|
@@ -34,11 +34,11 @@ function GroupsContent({ groups, activeHref, storeName, storeLogoURL, onItemClic
|
|
|
34
34
|
const hasActive = group.items.some((i) => activeHref === i.href);
|
|
35
35
|
return (_jsxs("div", { className: "mb-0.5", children: [_jsxs("button", { type: "button", onClick: () => toggle(group.title), className: `flex w-full items-center justify-between px-4 py-2 text-[0.6875rem] font-semibold uppercase tracking-widest transition-colors ${hasActive && !isOpen
|
|
36
36
|
? "text-orange-600 dark:text-orange-400"
|
|
37
|
-
: "text-zinc-400 dark:text-zinc-
|
|
37
|
+
: "text-zinc-400 dark:text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"}`, children: [_jsx("span", { children: group.title }), _jsx("svg", { className: `w-3 h-3 transition-transform duration-150 ${isOpen ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M19 9l-7 7-7-7" }) })] }), isOpen && (_jsx(Ul, { className: "space-y-0.5 px-3 pb-1", children: group.items.map((item) => (_jsx(Li, { children: _jsx(NavLink, { item: item, isActive: isNavItemActive(item, activeHref), onClick: onItemClick }) }, item.href))) }))] }, group.title));
|
|
38
38
|
}) })] }));
|
|
39
39
|
}
|
|
40
40
|
function DrawerPanel({ title, onClose, children }) {
|
|
41
|
-
return (_jsxs("div", { className: "hidden md:block", children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40 backdrop-blur-sm", onClick: onClose, "aria-hidden": "true" }), _jsxs("div", { role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 right-0 z-50 h-full w-64 bg-white dark:bg-slate-900 border-l border-zinc-200 dark:border-slate-700 flex flex-col shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-3.5 border-b border-zinc-100 dark:border-slate-800 shrink-0", children: [_jsx("span", { className: "text-xs font-semibold uppercase tracking-widest text-zinc-400 dark:text-zinc-
|
|
41
|
+
return (_jsxs("div", { className: "hidden md:block", children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40 backdrop-blur-sm", onClick: onClose, "aria-hidden": "true" }), _jsxs("div", { role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 right-0 z-50 h-full w-64 bg-white dark:bg-slate-900 border-l border-zinc-200 dark:border-slate-700 flex flex-col shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-3.5 border-b border-zinc-100 dark:border-slate-800 shrink-0", children: [_jsx("span", { className: "text-xs font-semibold uppercase tracking-widest text-zinc-400 dark:text-zinc-400", children: title }), _jsx("button", { type: "button", onClick: onClose, "aria-label": "Close", className: "flex items-center justify-center w-7 h-7 rounded-md text-zinc-400 hover:bg-zinc-100 dark:hover:bg-slate-800 hover:text-zinc-700 dark:hover:text-zinc-200 transition-colors", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), _jsx("div", { className: "flex-1 overflow-y-auto", children: children })] })] }));
|
|
42
42
|
}
|
|
43
43
|
export function StoreSidebar({ items, groups, activeHref, storeName, storeLogoURL, mobileOpen = false, desktopOpen = false, variant = "overlay", onCloseMobile, onToggle, }) {
|
|
44
44
|
const close = onCloseMobile ?? (() => { });
|
|
@@ -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 "../../../next";
|
|
@@ -42,6 +43,7 @@ export function SellerStoreCategoriesView({ onCreateClick, onEditClick, onDelete
|
|
|
42
43
|
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
43
44
|
const [deleteTargetId, setDeleteTargetId] = useState(null);
|
|
44
45
|
const [deletingId, setDeletingId] = useState(null);
|
|
46
|
+
const { showToast } = useToast();
|
|
45
47
|
const commitSearch = useCallback(() => { table.set(TABLE_KEYS.QUERY, searchInput.trim()); }, [searchInput, table]);
|
|
46
48
|
const resetAll = useCallback(() => {
|
|
47
49
|
table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
|
|
@@ -75,12 +77,16 @@ export function SellerStoreCategoriesView({ onCreateClick, onEditClick, onDelete
|
|
|
75
77
|
try {
|
|
76
78
|
await onDelete(id);
|
|
77
79
|
refetch?.();
|
|
80
|
+
showToast("Category deleted.", "success");
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
showToast(err instanceof Error ? err.message : "Failed to delete category.", "error");
|
|
78
84
|
}
|
|
79
85
|
finally {
|
|
80
86
|
setDeletingId(null);
|
|
81
87
|
setDeleteTargetId(null);
|
|
82
88
|
}
|
|
83
|
-
}, [onDelete, refetch]);
|
|
89
|
+
}, [onDelete, refetch, showToast]);
|
|
84
90
|
const bulkActions = [
|
|
85
91
|
...(onBulkDelete ? [{
|
|
86
92
|
id: "bulk-delete",
|
|
@@ -106,7 +112,8 @@ export function SellerStoreCategoriesView({ onCreateClick, onEditClick, onDelete
|
|
|
106
112
|
}
|
|
107
113
|
window.location.href = String(ROUTES.STORE.STORE_CATEGORIES_NEW);
|
|
108
114
|
};
|
|
109
|
-
|
|
115
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
116
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search categories by label...", 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: "table", onViewChange: () => { }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: handleNavigateNew, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: "New Category" })] }) }), 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 && (_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 categories yet \u2014 add your first storefront category" }) })) : (_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: [
|
|
110
117
|
{
|
|
111
118
|
label: ACTIONS.STORE["edit-listing"].label,
|
|
112
119
|
onClick: () => onEditClick
|
|
@@ -4,7 +4,8 @@ import { useState, useCallback, useMemo } 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, FilterDrawer, Input, ListingToolbar, Pagination, RowActionMenu, Select, SideDrawer, Stack, Text, } from "../../../ui";
|
|
7
|
+
import { BulkActionBar, Button, ConfirmDeleteModal, DataTable, Div, FilterDrawer, Input, ListingToolbar, Pagination, RowActionMenu, Select, SideDrawer, Stack, 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 { toRecordArray, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
|
|
@@ -69,6 +70,7 @@ export function SellerTemplatesView({ onDelete, onBulkDelete, onDuplicate, }) {
|
|
|
69
70
|
const [deleteTargetId, setDeleteTargetId] = useState(null);
|
|
70
71
|
const [deletingId, setDeletingId] = useState(null);
|
|
71
72
|
const [duplicatingId, setDuplicatingId] = useState(null);
|
|
73
|
+
const { showToast } = useToast();
|
|
72
74
|
const commitSearch = useCallback(() => {
|
|
73
75
|
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
74
76
|
}, [searchInput, table]);
|
|
@@ -171,14 +173,16 @@ export function SellerTemplatesView({ onDelete, onBulkDelete, onDuplicate, }) {
|
|
|
171
173
|
});
|
|
172
174
|
closeDrawer();
|
|
173
175
|
refetch?.();
|
|
176
|
+
showToast("Template saved.", "success");
|
|
174
177
|
}
|
|
175
|
-
catch {
|
|
178
|
+
catch (err) {
|
|
176
179
|
setSavingError("Failed to save template. Please try again.");
|
|
180
|
+
showToast(err instanceof Error ? err.message : "Failed to save template.", "error");
|
|
177
181
|
}
|
|
178
182
|
finally {
|
|
179
183
|
setSaving(false);
|
|
180
184
|
}
|
|
181
|
-
}, [draft, drawerMode, editingId, closeDrawer, refetch]);
|
|
185
|
+
}, [draft, drawerMode, editingId, closeDrawer, refetch, showToast]);
|
|
182
186
|
const handleDelete = useCallback(async (id) => {
|
|
183
187
|
setDeletingId(id);
|
|
184
188
|
try {
|
|
@@ -192,12 +196,16 @@ export function SellerTemplatesView({ onDelete, onBulkDelete, onDuplicate, }) {
|
|
|
192
196
|
});
|
|
193
197
|
}
|
|
194
198
|
refetch?.();
|
|
199
|
+
showToast("Template deleted.", "success");
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
showToast(err instanceof Error ? err.message : "Failed to delete template.", "error");
|
|
195
203
|
}
|
|
196
204
|
finally {
|
|
197
205
|
setDeletingId(null);
|
|
198
206
|
setDeleteTargetId(null);
|
|
199
207
|
}
|
|
200
|
-
}, [onDelete, refetch]);
|
|
208
|
+
}, [onDelete, refetch, showToast]);
|
|
201
209
|
const handleDuplicate = useCallback(async (row) => {
|
|
202
210
|
setDuplicatingId(row.id);
|
|
203
211
|
try {
|
|
@@ -219,11 +227,15 @@ export function SellerTemplatesView({ onDelete, onBulkDelete, onDuplicate, }) {
|
|
|
219
227
|
});
|
|
220
228
|
}
|
|
221
229
|
refetch?.();
|
|
230
|
+
showToast("Template duplicated.", "success");
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
showToast(err instanceof Error ? err.message : "Failed to duplicate template.", "error");
|
|
222
234
|
}
|
|
223
235
|
finally {
|
|
224
236
|
setDuplicatingId(null);
|
|
225
237
|
}
|
|
226
|
-
}, [onDuplicate, refetch]);
|
|
238
|
+
}, [onDuplicate, refetch, showToast]);
|
|
227
239
|
const bulkActions = [
|
|
228
240
|
{
|
|
229
241
|
id: "bulk-delete",
|
|
@@ -244,7 +256,8 @@ export function SellerTemplatesView({ onDelete, onBulkDelete, onDuplicate, }) {
|
|
|
244
256
|
},
|
|
245
257
|
},
|
|
246
258
|
];
|
|
247
|
-
|
|
259
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
260
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search templates by name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, showTableView: true, view: "table", onViewChange: () => { }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreate, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: "New Template" })] }) }), 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: safePage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedCount > 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))) })) : pageRows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-400", children: q || conditionFilter
|
|
248
261
|
? "No templates match your search or filters"
|
|
249
262
|
: "No templates yet — add your first product template" }) })) : (_jsx(DataTable, { columns: COLUMNS, data: pageRows, keyExtractor: (r) => r.id, selectable: bulkActions.length > 0, selectedIds: selection.selectedIds, onSelectionChange: (ids) => selection.setSelectedIds(ids), actions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
250
263
|
{
|
|
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useState } from "react";
|
|
|
4
4
|
import { Check } from "lucide-react";
|
|
5
5
|
import { Button } from "../../ui/components/Button";
|
|
6
6
|
import { classNames } from "../../ui/style.helper";
|
|
7
|
-
import { Text } from "../../ui";
|
|
7
|
+
import { Text, useToast } from "../../ui";
|
|
8
8
|
/** Standalone step nav bar — use as `renderBottomBar` in FormShell. */
|
|
9
9
|
export function StepFormActions({ currentStep, totalSteps, onNext, onPrev, completeLabel = "Publish Now", isLoading = false, disabled = false, }) {
|
|
10
10
|
const isFirst = currentStep === 0;
|
|
@@ -34,6 +34,7 @@ export function StepIndicator({ steps, currentStep, onStepClick, stepErrors, })
|
|
|
34
34
|
}) }));
|
|
35
35
|
}
|
|
36
36
|
export function StepForm({ steps, values, onChange, onComplete, completeLabel = "Publish Now", isLoading = false, formId, currentStep, onStepChange, hideActions = false, stepErrors, }) {
|
|
37
|
+
const { showToast } = useToast();
|
|
37
38
|
const [fieldErrors, setFieldErrors] = useState({});
|
|
38
39
|
const [stepError, setStepError] = useState(null);
|
|
39
40
|
// Persist step to localStorage if formId provided
|
|
@@ -67,9 +68,14 @@ export function StepForm({ steps, values, onChange, onComplete, completeLabel =
|
|
|
67
68
|
onStepChange(currentStep + 1);
|
|
68
69
|
}
|
|
69
70
|
else {
|
|
70
|
-
|
|
71
|
+
try {
|
|
72
|
+
await onComplete();
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
showToast(err instanceof Error ? err.message : "Something went wrong.", "error");
|
|
76
|
+
}
|
|
71
77
|
}
|
|
72
|
-
}, [currentStep, steps, values, onComplete, onStepChange]);
|
|
78
|
+
}, [currentStep, steps, values, onComplete, onStepChange, showToast]);
|
|
73
79
|
const goPrev = useCallback(() => {
|
|
74
80
|
if (currentStep > 0) {
|
|
75
81
|
setStepError(null);
|
|
@@ -94,6 +94,6 @@ export function ActionPermissionsManager({ initialConfig, onUpdate }) {
|
|
|
94
94
|
return (_jsxs("tr", { className: [
|
|
95
95
|
idx % 2 === 0 ? "bg-white dark:bg-slate-900" : "bg-zinc-50/50 dark:bg-slate-800/50",
|
|
96
96
|
!enabled ? "opacity-60" : "",
|
|
97
|
-
].join(" "), children: [_jsx("td", { className: "px-4 py-3", children: _jsxs(Div, { children: [_jsx(Text, { className: "font-medium text-zinc-900 dark:text-zinc-100", children: meta.label }), _jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-
|
|
97
|
+
].join(" "), children: [_jsx("td", { className: "px-4 py-3", children: _jsxs(Div, { children: [_jsx(Text, { className: "font-medium text-zinc-900 dark:text-zinc-100", children: meta.label }), _jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: id })] }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("span", { className: `inline-block rounded-full px-2 py-0.5 text-xs font-medium ${categoryColor}`, children: category }) }), _jsx("td", { className: "px-4 py-3", children: meta.requiresAuth ? (_jsx("span", { className: "inline-block rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-800 dark:bg-amber-900/30 dark:text-amber-300", children: "Auth required" })) : (_jsx(Text, { className: "text-xs text-zinc-400", children: "\u2014" })) }), _jsx("td", { className: "px-4 py-3", children: meta.requiredPermission ? (_jsx("code", { className: "rounded bg-zinc-100 px-1.5 py-0.5 text-xs text-zinc-700 dark:bg-slate-700 dark:text-zinc-300", children: meta.requiredPermission })) : (_jsx(Text, { className: "text-xs text-zinc-400", children: "\u2014" })) }), _jsx("td", { className: "px-4 py-3 text-right", children: _jsx(Toggle, { checked: enabled, onChange: (v) => handleToggle(id, v), "aria-label": `Toggle ${meta.label}` }) })] }, id));
|
|
98
98
|
}) })] }) })] }));
|
|
99
99
|
}
|
|
@@ -28,6 +28,6 @@ export function NavPermissionsManager({ initialConfig, navGroups, onUpdate, }) {
|
|
|
28
28
|
return (_jsxs("tr", { className: [
|
|
29
29
|
idx % 2 === 0 ? "bg-white dark:bg-slate-900" : "bg-zinc-50/50 dark:bg-slate-800/50",
|
|
30
30
|
!enabled ? "opacity-60" : "",
|
|
31
|
-
].join(" "), children: [_jsx("td", { className: "px-4 py-3", children: _jsxs(Div, { children: [_jsx(Text, { className: "font-medium text-zinc-900 dark:text-zinc-100", children: item.label }), item.id && (_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-
|
|
31
|
+
].join(" "), children: [_jsx("td", { className: "px-4 py-3", children: _jsxs(Div, { children: [_jsx(Text, { className: "font-medium text-zinc-900 dark:text-zinc-100", children: item.label }), item.id && (_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: item.id }))] }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("code", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: item.href }) }), _jsx("td", { className: "px-4 py-3", children: item.requiredPermission ? (_jsx("code", { className: "rounded bg-zinc-100 px-1.5 py-0.5 text-xs text-zinc-700 dark:bg-slate-700 dark:text-zinc-300", children: item.requiredPermission })) : (_jsx(Text, { className: "text-xs text-zinc-400", children: "\u2014" })) }), _jsx("td", { className: "px-4 py-3 text-right", children: hasId ? (_jsx(Toggle, { checked: enabled, onChange: (v) => handleToggle(item.id, item.label, v), "aria-label": `Toggle ${item.label}` })) : (_jsx(Text, { className: "text-xs text-zinc-400 italic", children: "Legacy \u2014 always visible" })) })] }, item.href));
|
|
32
32
|
}) })] }) })] }, group.groupLabel))) }));
|
|
33
33
|
}
|
|
@@ -23,5 +23,5 @@ export function InteractiveStoreCard({ store, href, selectable, isSelected = fal
|
|
|
23
23
|
].join(" "), onMouseDown: !isSelected ? longPress.onMouseDown : undefined, onMouseUp: !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => {
|
|
24
24
|
e.preventDefault();
|
|
25
25
|
onSelect(store.id, !isSelected);
|
|
26
|
-
}, label: isSelected ? "Deselect store" : "Select store", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsxs(Link, { href: href, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Div, { className: "relative aspect-video w-full overflow-hidden bg-gradient-to-br from-zinc-100 via-zinc-200 to-zinc-300 dark:from-zinc-800 dark:via-zinc-700 dark:to-zinc-900 flex-shrink-0", children: [hasBanner ? (_jsx(MediaImage, { src: store.storeBannerURL, alt: `${store.storeName} banner`, size: "banner", className: "h-full w-full object-cover transition-transform duration-500 group-hover:scale-105" })) : (_jsx(Div, { className: "absolute inset-0 flex items-center justify-center", children: _jsx(Span, { className: "text-5xl opacity-20 select-none", "aria-hidden": "true", children: "\uD83C\uDFEA" }) })), _jsx(Div, { className: "absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-black/40 to-transparent pointer-events-none" }), store.averageRating != null && store.averageRating > 0 && (_jsxs(Div, { className: "absolute top-2 right-2 flex items-center gap-1 rounded-full bg-black/50 backdrop-blur-sm px-2 py-0.5 text-xs font-semibold text-yellow-300", children: ["\u2605 ", store.averageRating.toFixed(1)] }))] }), _jsxs(Div, { className: "flex flex-col flex-1 px-4 pb-4", children: [_jsx(Div, { className: "-mt-5 mb-2 flex items-end justify-between", children: _jsx(Div, { className: "flex-shrink-0", children: hasLogo ? (_jsx("img", { src: store.storeLogoURL, alt: store.storeName, className: "h-10 w-10 rounded-lg border-2 border-white dark:border-zinc-800 shadow-md object-cover bg-white dark:bg-zinc-800", onError: () => setLogoBroken(true), loading: "lazy" })) : (_jsx(Div, { className: "flex h-10 w-10 items-center justify-center rounded-lg border-2 border-white dark:border-zinc-800 bg-primary/10 dark:bg-primary/20 text-base font-bold text-primary shadow-md", children: initial })) }) }), _jsx(Heading, { level: 3, className: `${THEME_CONSTANTS.utilities.textClamp1} text-sm font-bold text-zinc-900 dark:text-zinc-100 group-hover:text-primary transition-colors`, children: store.storeName }), store.storeDescription ? (_jsx(RichText, { html: normalizeRichTextHtml(store.storeDescription), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `mt-1 ${THEME_CONSTANTS.utilities.textClamp2} text-xs text-zinc-500 dark:text-zinc-400 flex-1` })) : (_jsx(Div, { className: "flex-1" })), _jsxs(Row, { gap: "sm", className: "mt-2.5 flex-wrap text-xs text-zinc-500 dark:text-zinc-400", children: [store.totalProducts != null && store.totalProducts > 0 && (_jsxs(Span, { className: "flex items-center gap-0.5", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDCE6" }), " ", store.totalProducts, " ", labels.products ?? "products"] })), store.itemsSold != null && store.itemsSold > 0 && (_jsxs(Span, { className: "flex items-center gap-0.5", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDECD\uFE0F" }), " ", store.itemsSold, " ", labels.sold ?? "sold"] }))] }), _jsxs(Div, { className: "mt-3 pt-2.5 border-t border-zinc-100 dark:border-zinc-800 flex items-center justify-between", children: [_jsxs(Span, { className: "text-xs font-semibold text-primary group-hover:underline transition-colors", children: [labels.visitStore ?? "Visit store", " \u2192"] }), store.totalReviews != null && store.totalReviews > 0 && (_jsxs(Span, { className: "text-xs text-zinc-400 dark:text-zinc-
|
|
26
|
+
}, label: isSelected ? "Deselect store" : "Select store", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsxs(Link, { href: href, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Div, { className: "relative aspect-video w-full overflow-hidden bg-gradient-to-br from-zinc-100 via-zinc-200 to-zinc-300 dark:from-zinc-800 dark:via-zinc-700 dark:to-zinc-900 flex-shrink-0", children: [hasBanner ? (_jsx(MediaImage, { src: store.storeBannerURL, alt: `${store.storeName} banner`, size: "banner", className: "h-full w-full object-cover transition-transform duration-500 group-hover:scale-105" })) : (_jsx(Div, { className: "absolute inset-0 flex items-center justify-center", children: _jsx(Span, { className: "text-5xl opacity-20 select-none", "aria-hidden": "true", children: "\uD83C\uDFEA" }) })), _jsx(Div, { className: "absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-black/40 to-transparent pointer-events-none" }), store.averageRating != null && store.averageRating > 0 && (_jsxs(Div, { className: "absolute top-2 right-2 flex items-center gap-1 rounded-full bg-black/50 backdrop-blur-sm px-2 py-0.5 text-xs font-semibold text-yellow-300", children: ["\u2605 ", store.averageRating.toFixed(1)] }))] }), _jsxs(Div, { className: "flex flex-col flex-1 px-4 pb-4", children: [_jsx(Div, { className: "-mt-5 mb-2 flex items-end justify-between", children: _jsx(Div, { className: "flex-shrink-0", children: hasLogo ? (_jsx("img", { src: store.storeLogoURL, alt: store.storeName, className: "h-10 w-10 rounded-lg border-2 border-white dark:border-zinc-800 shadow-md object-cover bg-white dark:bg-zinc-800", onError: () => setLogoBroken(true), loading: "lazy" })) : (_jsx(Div, { className: "flex h-10 w-10 items-center justify-center rounded-lg border-2 border-white dark:border-zinc-800 bg-primary/10 dark:bg-primary/20 text-base font-bold text-primary shadow-md", children: initial })) }) }), _jsx(Heading, { level: 3, className: `${THEME_CONSTANTS.utilities.textClamp1} text-sm font-bold text-zinc-900 dark:text-zinc-100 group-hover:text-primary transition-colors`, children: store.storeName }), store.storeDescription ? (_jsx(RichText, { html: normalizeRichTextHtml(store.storeDescription), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `mt-1 ${THEME_CONSTANTS.utilities.textClamp2} text-xs text-zinc-500 dark:text-zinc-400 flex-1` })) : (_jsx(Div, { className: "flex-1" })), _jsxs(Row, { gap: "sm", className: "mt-2.5 flex-wrap text-xs text-zinc-500 dark:text-zinc-400", children: [store.totalProducts != null && store.totalProducts > 0 && (_jsxs(Span, { className: "flex items-center gap-0.5", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDCE6" }), " ", store.totalProducts, " ", labels.products ?? "products"] })), store.itemsSold != null && store.itemsSold > 0 && (_jsxs(Span, { className: "flex items-center gap-0.5", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDECD\uFE0F" }), " ", store.itemsSold, " ", labels.sold ?? "sold"] }))] }), _jsxs(Div, { className: "mt-3 pt-2.5 border-t border-zinc-100 dark:border-zinc-800 flex items-center justify-between", children: [_jsxs(Span, { className: "text-xs font-semibold text-primary group-hover:underline transition-colors", children: [labels.visitStore ?? "Visit store", " \u2192"] }), store.totalReviews != null && store.totalReviews > 0 && (_jsxs(Span, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: [store.totalReviews, " ", labels.reviews ?? "reviews"] }))] })] })] })] }));
|
|
27
27
|
}
|
|
@@ -23,7 +23,7 @@ function tabLabel(base, count) {
|
|
|
23
23
|
export async function StoreDetailLayoutView({ storeSlug, activeTab, children, }) {
|
|
24
24
|
const store = await getStoreBySlug(storeSlug);
|
|
25
25
|
if (!store) {
|
|
26
|
-
return (_jsx(Main, { children: _jsx(Section, { className: "py-20", children: _jsx(Container, { size: "md", children: _jsx(Text, { className: "text-center text-zinc-500", children: "Store not found." }) }) }) }));
|
|
26
|
+
return (_jsx(Main, { children: _jsx(Section, { className: "py-20", children: _jsx(Container, { size: "md", children: _jsx(Text, { className: "text-center text-zinc-500 dark:text-zinc-400", children: "Store not found." }) }) }) }));
|
|
27
27
|
}
|
|
28
28
|
const storeId = store?.id;
|
|
29
29
|
const settings = await siteSettingsRepository.findById("global").catch(() => null);
|
|
@@ -102,5 +102,5 @@ export function StoreReviewsListing({ storeSlug }) {
|
|
|
102
102
|
value: opt.value,
|
|
103
103
|
label: SORT_OPTION_LABELS[opt.key] ?? opt.key,
|
|
104
104
|
}));
|
|
105
|
-
return (_jsxs("div", { className: "min-h-screen", children: [totalReviews > 0 && (_jsxs(Row, { align: "center", gap: "sm", className: "px-4 py-3 border-b border-zinc-200 dark:border-slate-700", children: [_jsx(Span, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-100", children: averageRating.toFixed(1) }), _jsxs(Span, { className: "text-zinc-500 text-sm", children: ["/ 5 \u00B7 ", totalReviews, " reviews"] })] })), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, 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) }) })), _jsx("div", { className: "py-6 px-4", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: _jsxs("div", { className: "p-4 space-y-3", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" })] }) }, i))) })) : reviews.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No reviews found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: reviews.map((review) => (_jsx(ReviewCard, { review: review, context: "store" }, review.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: reviews.map((review) => (_jsx(ReviewCard, { review: review, context: "store" }, review.id))) })) }), 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: "flex items-center gap-2 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 filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-
|
|
105
|
+
return (_jsxs("div", { className: "min-h-screen", children: [totalReviews > 0 && (_jsxs(Row, { align: "center", gap: "sm", className: "px-4 py-3 border-b border-zinc-200 dark:border-slate-700", children: [_jsx(Span, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-100", children: averageRating.toFixed(1) }), _jsxs(Span, { className: "text-zinc-500 dark:text-zinc-400 text-sm", children: ["/ 5 \u00B7 ", totalReviews, " reviews"] })] })), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, 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) }) })), _jsx("div", { className: "py-6 px-4", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: _jsxs("div", { className: "p-4 space-y-3", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" })] }) }, i))) })) : reviews.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No reviews found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: reviews.map((review) => (_jsx(ReviewCard, { review: review, context: "store" }, review.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: reviews.map((review) => (_jsx(ReviewCard, { review: review, context: "store" }, review.id))) })) }), 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: "flex items-center gap-2 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 filters", 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", children: _jsx(ReviewFilters, { table: pendingTable, variant: "public" }) }), _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})` : ""] }) })] })] }))] }));
|
|
106
106
|
}
|
|
@@ -13,6 +13,7 @@ import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
|
13
13
|
import { sortBy } from "../../../constants/sort";
|
|
14
14
|
import { STORE_FIELDS } from "../../../constants/field-names";
|
|
15
15
|
import { ACTION_ID } from "../../products/constants/action-defs";
|
|
16
|
+
import { useBottomActions } from "../../layout";
|
|
16
17
|
const DEFAULT_SORT = sortBy(STORE_FIELDS.CREATED_AT);
|
|
17
18
|
const STORE_SORT_OPTIONS = [
|
|
18
19
|
{ value: sortBy(STORE_FIELDS.CREATED_AT), label: "Newest First" },
|
|
@@ -133,6 +134,21 @@ export function StoresIndexListing({ initialData }) {
|
|
|
133
134
|
return (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(storeKey)), selectable: selection.isSelecting, isSelected: selection.isSelected(store.id ?? store.storeSlug), onSelect: (id, sel) => { void sel; selection.toggle(id); } }, storeKey));
|
|
134
135
|
}) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: stores.map((store) => {
|
|
135
136
|
const storeKey = store.storeSlug ?? store.id;
|
|
137
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
138
|
+
{
|
|
139
|
+
id: ACTION_ID.COMPARE,
|
|
140
|
+
label: "Compare",
|
|
141
|
+
variant: "secondary",
|
|
142
|
+
onClick: () => { selection.clearSelection(); },
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "visit",
|
|
146
|
+
label: "Visit Store",
|
|
147
|
+
variant: "primary",
|
|
148
|
+
disabled: selection.selectedCount !== 1,
|
|
149
|
+
onClick: () => { selection.clearSelection(); },
|
|
150
|
+
},
|
|
151
|
+
] } } : {});
|
|
136
152
|
return (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(storeKey)), selectable: selection.isSelecting, isSelected: selection.isSelected(store.id ?? store.storeSlug), onSelect: (id, sel) => { void sel; selection.toggle(id); } }, storeKey));
|
|
137
|
-
}) })) }), 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: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [pendingFilterCount > 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 filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-
|
|
153
|
+
}) })) }), 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: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [pendingFilterCount > 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 filters", 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", children: _jsx(StoreFilters, { table: pendingTable }) }), _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", pendingFilterCount > 0 ? ` (${pendingFilterCount})` : ""] }) })] })] }))] }));
|
|
138
154
|
}
|
|
@@ -157,7 +157,7 @@ export function SellerWhatsAppSettingsView({ hasCapability }) {
|
|
|
157
157
|
}) })] }), _jsxs(Section, { className: CLS_SECTION_CARD, children: [_jsxs(Row, { justify: "between", align: "center", className: "mb-4", children: [_jsx(Heading, { level: 2, className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: "Connection" }), isLoading ? null : cfg?.connected ? (_jsx(Badge, { variant: "success", children: "Connected" })) : (_jsx(Badge, { variant: "default", children: "Not configured" }))] }), _jsxs(Form, { onSubmit: (e) => {
|
|
158
158
|
e.preventDefault();
|
|
159
159
|
saveMutation.mutate();
|
|
160
|
-
}, className: "space-y-4", children: [_jsx(Input, { label: "WhatsApp Business Phone Number", value: phoneNumber, onChange: (e) => setPhoneNumber(e.target.value), placeholder: "919876543210", helperText: "Digits only, include country code (e.g. 91 for India)" }), _jsx(Input, { label: "WABA ID (WhatsApp Business Account ID)", value: wabaId, onChange: (e) => setWabaId(e.target.value), placeholder: "123456789012345", helperText: "From Meta Business Manager \u2192 WhatsApp Accounts" }), _jsx(Input, { label: "Catalog ID", value: catalogId, onChange: (e) => setCatalogId(e.target.value), placeholder: "987654321098765", helperText: "From Meta Commerce Manager \u2192 Catalogs" }), _jsx(MaskedTokenInput, { label: "System User Access Token", value: accessToken, onChange: setAccessToken, placeholder: "EAAxxxxxxxx\u2026", helperText: "Long-lived token with WhatsApp Business permissions" }), _jsx(FormActions, { align: "right", children: _jsx(Button, { type: "submit", isLoading: saveMutation.isPending, disabled: saveMutation.isPending, children: saveMutation.isPending ? "Saving…" : ACTIONS.STORE["whatsapp-connect"].label }) })] })] }), _jsxs(Section, { className: CLS_SECTION_CARD, children: [_jsx(Heading, { level: 2, className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100 mb-4", children: "Catalog Sync" }), _jsxs(Row, { justify: "between", align: "center", className: "mb-4", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-sm text-zinc-700 dark:text-zinc-300", children: "Enable catalog sync" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "When enabled, your published standard products can be synced to WhatsApp." })] }), _jsx(Toggle, { checked: syncEnabled, onChange: setSyncEnabled, disabled: !cfg?.connected })] }), cfg?.lastCatalogSyncAt && (_jsxs(Row, { gap: "sm", align: "center", className: "text-xs text-zinc-500 dark:text-zinc-400 mb-3", children: [_jsxs(Text, { children: ["Last sync: ", new Date(cfg.lastCatalogSyncAt).toLocaleString("en-IN")] }), cfg.lastSyncCount !== undefined && _jsxs(Text, { children: ["\u00B7 ", cfg.lastSyncCount, " products"] }), cfg.lastSyncStatus && (_jsx(Badge, { variant: STATUS_VARIANT[cfg.lastSyncStatus] ?? "default", children: cfg.lastSyncStatus }))] })), _jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-
|
|
160
|
+
}, className: "space-y-4", children: [_jsx(Input, { label: "WhatsApp Business Phone Number", value: phoneNumber, onChange: (e) => setPhoneNumber(e.target.value), placeholder: "919876543210", helperText: "Digits only, include country code (e.g. 91 for India)" }), _jsx(Input, { label: "WABA ID (WhatsApp Business Account ID)", value: wabaId, onChange: (e) => setWabaId(e.target.value), placeholder: "123456789012345", helperText: "From Meta Business Manager \u2192 WhatsApp Accounts" }), _jsx(Input, { label: "Catalog ID", value: catalogId, onChange: (e) => setCatalogId(e.target.value), placeholder: "987654321098765", helperText: "From Meta Commerce Manager \u2192 Catalogs" }), _jsx(MaskedTokenInput, { label: "System User Access Token", value: accessToken, onChange: setAccessToken, placeholder: "EAAxxxxxxxx\u2026", helperText: "Long-lived token with WhatsApp Business permissions" }), _jsx(FormActions, { align: "right", children: _jsx(Button, { type: "submit", isLoading: saveMutation.isPending, disabled: saveMutation.isPending, children: saveMutation.isPending ? "Saving…" : ACTIONS.STORE["whatsapp-connect"].label }) })] })] }), _jsxs(Section, { className: CLS_SECTION_CARD, children: [_jsx(Heading, { level: 2, className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100 mb-4", children: "Catalog Sync" }), _jsxs(Row, { justify: "between", align: "center", className: "mb-4", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-sm text-zinc-700 dark:text-zinc-300", children: "Enable catalog sync" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "When enabled, your published standard products can be synced to WhatsApp." })] }), _jsx(Toggle, { checked: syncEnabled, onChange: setSyncEnabled, disabled: !cfg?.connected })] }), cfg?.lastCatalogSyncAt && (_jsxs(Row, { gap: "sm", align: "center", className: "text-xs text-zinc-500 dark:text-zinc-400 mb-3", children: [_jsxs(Text, { children: ["Last sync: ", new Date(cfg.lastCatalogSyncAt).toLocaleString("en-IN")] }), cfg.lastSyncCount !== undefined && _jsxs(Text, { children: ["\u00B7 ", cfg.lastSyncCount, " products"] }), cfg.lastSyncStatus && (_jsx(Badge, { variant: STATUS_VARIANT[cfg.lastSyncStatus] ?? "default", children: cfg.lastSyncStatus }))] })), _jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400 mb-3", children: "Only published standard products are synced. Auctions and pre-orders are excluded. Batches of up to 50 products per call." }), _jsxs(Div, { className: "flex gap-3", children: [_jsx(Button, { onClick: () => syncMutation.mutate(), isLoading: syncMutation.isPending, disabled: !cfg?.connected || !syncEnabled || syncMutation.isPending, variant: "secondary", children: syncMutation.isPending ? "Syncing…" : ACTIONS.STORE["whatsapp-catalog-sync"].label }), _jsx(Button, { onClick: () => importMutation.mutate(), isLoading: importMutation.isPending, disabled: !cfg?.connected || !syncEnabled || importMutation.isPending, variant: "secondary", children: importMutation.isPending ? "Importing…" : ACTIONS.STORE["whatsapp-catalog-import"].label })] }), _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400 mt-3", children: [_jsx("strong", { children: "Push" }), " sends your published standard products to WhatsApp.", " ", _jsx("strong", { children: "Import" }), " creates draft products from your WhatsApp catalog. Products are matched by slug in the description field."] })] }), _jsxs(Section, { className: CLS_SECTION_CARD, children: [_jsx(Heading, { level: 2, className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100 mb-2", children: "Purchase Announcement Preview" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 mb-3", children: "This message is sent automatically to your phone and the platform admin when a new order is placed." }), _jsx(Div, { className: "bg-zinc-50 dark:bg-zinc-800/60 rounded-lg px-4 py-3 text-sm text-zinc-700 dark:text-zinc-200 font-mono", children: buildPurchaseAnnouncementMessage({
|
|
161
161
|
buyerName: "Ravi K.",
|
|
162
162
|
firstItemName: "Charizard PSA 9",
|
|
163
163
|
additionalItemCount: 2,
|
|
@@ -5,14 +5,14 @@ import { THEME_CONSTANTS } from "../../../tokens";
|
|
|
5
5
|
export function WishlistCard({ item, onRemove, onProductClick, }) {
|
|
6
6
|
return (_jsxs(Div, { className: "flex gap-4 rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-4", children: [_jsx(Div, { role: onProductClick ? "button" : undefined, tabIndex: onProductClick ? 0 : undefined, onClick: onProductClick ? () => onProductClick(item) : undefined, onKeyDown: onProductClick
|
|
7
7
|
? (e) => e.key === "Enter" && onProductClick(item)
|
|
8
|
-
: undefined, className: `h-20 w-20 flex-shrink-0 overflow-hidden rounded-lg bg-neutral-100 dark:bg-slate-800 ${onProductClick ? "cursor-pointer" : ""}`, children: item.productImage && (_jsx(Div, { role: "img", "aria-label": item.productTitle ?? "", className: "h-full w-full bg-center bg-cover", style: { backgroundImage: `url(${item.productImage})` } })) }), _jsxs(Div, { className: "flex flex-1 flex-col justify-between", children: [_jsx(Text, { className: `font-medium text-neutral-900 dark:text-zinc-100 ${THEME_CONSTANTS.utilities.textClamp2}`, children: item.productTitle }), item.productPrice !== undefined && (_jsx(Text, { className: "text-sm font-semibold text-neutral-900 dark:text-zinc-100", children: formatCurrency(item.productPrice, item.productCurrency) }))] }), onRemove && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onRemove(item.id), "aria-label": "Remove from wishlist", className: "self-start text-
|
|
8
|
+
: undefined, className: `h-20 w-20 flex-shrink-0 overflow-hidden rounded-lg bg-neutral-100 dark:bg-slate-800 ${onProductClick ? "cursor-pointer" : ""}`, children: item.productImage && (_jsx(Div, { role: "img", "aria-label": item.productTitle ?? "", className: "h-full w-full bg-center bg-cover", style: { backgroundImage: `url(${item.productImage})` } })) }), _jsxs(Div, { className: "flex flex-1 flex-col justify-between", children: [_jsx(Text, { className: `font-medium text-neutral-900 dark:text-zinc-100 ${THEME_CONSTANTS.utilities.textClamp2}`, children: item.productTitle }), item.productPrice !== undefined && (_jsx(Text, { className: "text-sm font-semibold text-neutral-900 dark:text-zinc-100", children: formatCurrency(item.productPrice, item.productCurrency) }))] }), onRemove && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onRemove(item.id), "aria-label": "Remove from wishlist", className: "self-start text-zinc-400 dark:text-zinc-400 transition hover:text-red-500", children: "\u2715" }))] }));
|
|
9
9
|
}
|
|
10
10
|
export function WishlistPage({ items, isLoading, onRemove, onProductClick, emptyLabel = "Your wishlist is empty", }) {
|
|
11
11
|
if (isLoading) {
|
|
12
12
|
return (_jsx(Stack, { gap: "md", children: Array.from({ length: 4 }).map((_, i) => (_jsxs(Div, { className: "flex animate-pulse gap-4 rounded-xl border border-neutral-200 p-4", children: [_jsx(Div, { className: "h-20 w-20 rounded-lg bg-neutral-200" }), _jsxs(Stack, { gap: "sm", className: "flex-1", children: [_jsx(Div, { className: "h-4 w-3/4 rounded bg-neutral-200 dark:bg-slate-700" }), _jsx(Div, { className: "h-4 w-1/3 rounded bg-neutral-200 dark:bg-slate-700" })] })] }, i))) }));
|
|
13
13
|
}
|
|
14
14
|
if (items.length === 0) {
|
|
15
|
-
return (_jsx(Text, { className: "py-16 text-center text-sm text-
|
|
15
|
+
return (_jsx(Text, { className: "py-16 text-center text-sm text-zinc-500 dark:text-zinc-400", children: emptyLabel }));
|
|
16
16
|
}
|
|
17
17
|
return (_jsx(Stack, { gap: "md", children: items.map((item) => (_jsx(WishlistCard, { item: item, onRemove: onRemove, onProductClick: onProductClick }, item.id))) }));
|
|
18
18
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useCallback, useState } from "react";
|
|
3
|
+
import { useToast } from "../../../ui";
|
|
3
4
|
export function useWishlistToggle(productId, initial = false, actions) {
|
|
4
5
|
const [inWishlist, setInWishlist] = useState(initial);
|
|
5
6
|
const [isLoading, setIsLoading] = useState(false);
|
|
7
|
+
const { showToast } = useToast();
|
|
6
8
|
const toggle = useCallback(async () => {
|
|
7
9
|
if (!productId)
|
|
8
10
|
return;
|
|
@@ -15,18 +17,21 @@ export function useWishlistToggle(productId, initial = false, actions) {
|
|
|
15
17
|
}
|
|
16
18
|
if (prev) {
|
|
17
19
|
await actions.removeFromWishlist(productId);
|
|
20
|
+
showToast("Removed from wishlist.", "success");
|
|
18
21
|
}
|
|
19
22
|
else {
|
|
20
23
|
await actions.addToWishlist(productId);
|
|
24
|
+
showToast("Added to wishlist.", "success");
|
|
21
25
|
}
|
|
22
26
|
}
|
|
23
27
|
catch (err) {
|
|
24
28
|
setInWishlist(prev);
|
|
29
|
+
showToast(err instanceof Error ? err.message : "Something went wrong.", "error");
|
|
25
30
|
throw err;
|
|
26
31
|
}
|
|
27
32
|
finally {
|
|
28
33
|
setIsLoading(false);
|
|
29
34
|
}
|
|
30
|
-
}, [actions, inWishlist, productId]);
|
|
35
|
+
}, [actions, inWishlist, productId, showToast]);
|
|
31
36
|
return { inWishlist, isLoading, toggle };
|
|
32
37
|
}
|