@mohasinac/appkit 2.7.59 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/filters/filter-load-options.d.ts +4 -5
- package/dist/_internal/server/features/auctions/og.js +8 -32
- package/dist/_internal/server/features/blog/og.js +10 -49
- package/dist/_internal/server/features/checkout/actions.js +6 -0
- package/dist/_internal/server/features/classified/og.js +9 -51
- package/dist/_internal/server/features/digital-code/actions.d.ts +11 -0
- package/dist/_internal/server/features/digital-code/actions.js +30 -0
- package/dist/_internal/server/features/digital-code/index.d.ts +1 -0
- package/dist/_internal/server/features/digital-code/index.js +1 -0
- package/dist/_internal/server/features/digital-code/og.js +9 -51
- package/dist/_internal/server/features/events/og.js +10 -49
- package/dist/_internal/server/features/item-requests/data.d.ts +17 -0
- package/dist/_internal/server/features/item-requests/data.js +20 -0
- package/dist/_internal/server/features/item-requests/index.d.ts +2 -0
- package/dist/_internal/server/features/item-requests/index.js +2 -0
- package/dist/_internal/server/features/item-requests/og.d.ts +40 -0
- package/dist/_internal/server/features/item-requests/og.js +136 -0
- package/dist/_internal/server/features/live/actions.d.ts +5 -0
- package/dist/_internal/server/features/live/actions.js +24 -0
- package/dist/_internal/server/features/live/index.d.ts +1 -0
- package/dist/_internal/server/features/live/index.js +1 -0
- package/dist/_internal/server/features/live/og.js +9 -51
- package/dist/_internal/server/features/media/contextGuards.js +19 -8
- package/dist/_internal/server/features/pre-orders/og.js +8 -32
- package/dist/_internal/server/features/prize-draws/data.d.ts +17 -0
- package/dist/_internal/server/features/prize-draws/data.js +20 -0
- package/dist/_internal/server/features/prize-draws/index.d.ts +2 -0
- package/dist/_internal/server/features/prize-draws/index.js +2 -0
- package/dist/_internal/server/features/prize-draws/og.d.ts +30 -0
- package/dist/_internal/server/features/prize-draws/og.js +50 -0
- package/dist/_internal/server/features/products/og.js +8 -33
- package/dist/_internal/server/features/seo/og-layout.d.ts +42 -0
- package/dist/_internal/server/features/seo/og-layout.js +69 -0
- package/dist/_internal/server/features/stores/og.js +8 -41
- package/dist/_internal/server/features/sublisting-categories/og.js +11 -42
- package/dist/client.d.ts +10 -6
- package/dist/client.js +7 -3
- package/dist/constants/index.d.ts +4 -0
- package/dist/constants/index.js +2 -0
- package/dist/constants/notification-types.d.ts +20 -0
- package/dist/constants/notification-types.js +37 -0
- package/dist/constants/pagination-presets.d.ts +39 -0
- package/dist/constants/pagination-presets.js +38 -0
- package/dist/errors/messages.d.ts +0 -4
- package/dist/errors/messages.js +0 -4
- package/dist/features/about/components/PublicProfileView.js +8 -8
- package/dist/features/account/components/UserOffersPanel.js +1 -1
- package/dist/features/account/components/UserSupportView.d.ts +1 -2
- package/dist/features/account/components/UserSupportView.js +9 -1
- package/dist/features/admin/actions/admin-actions.js +16 -1
- package/dist/features/admin/components/AdminAllEventEntriesView.d.ts +2 -3
- package/dist/features/admin/components/AdminAllEventEntriesView.js +50 -83
- package/dist/features/admin/components/AdminBidsView.d.ts +2 -3
- package/dist/features/admin/components/AdminBidsView.js +64 -98
- package/dist/features/admin/components/AdminBlogView.d.ts +3 -7
- package/dist/features/admin/components/AdminBlogView.js +53 -83
- package/dist/features/admin/components/AdminBrandsView.d.ts +2 -3
- package/dist/features/admin/components/AdminBrandsView.js +84 -83
- package/dist/features/admin/components/AdminBundlesView.js +82 -125
- package/dist/features/admin/components/AdminCarouselView.d.ts +2 -2
- package/dist/features/admin/components/AdminCarouselView.js +17 -52
- package/dist/features/admin/components/AdminCartsView.d.ts +2 -3
- package/dist/features/admin/components/AdminCartsView.js +39 -88
- package/dist/features/admin/components/AdminCategoriesView.d.ts +3 -3
- package/dist/features/admin/components/AdminCategoriesView.js +98 -92
- package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
- package/dist/features/admin/components/AdminClassifiedView.d.ts +3 -0
- package/dist/features/admin/components/AdminClassifiedView.js +44 -0
- package/dist/features/admin/components/AdminContactView.d.ts +2 -2
- package/dist/features/admin/components/AdminContactView.js +104 -111
- package/dist/features/admin/components/AdminCouponsView.d.ts +3 -6
- package/dist/features/admin/components/AdminCouponsView.js +66 -86
- package/dist/features/admin/components/AdminDigitalCodesView.d.ts +3 -0
- package/dist/features/admin/components/AdminDigitalCodesView.js +51 -0
- package/dist/features/admin/components/AdminFaqsView.d.ts +4 -4
- package/dist/features/admin/components/AdminFaqsView.js +77 -33
- package/dist/features/admin/components/AdminFeaturesView.d.ts +2 -3
- package/dist/features/admin/components/AdminFeaturesView.js +57 -70
- package/dist/features/admin/components/AdminGroupedListingsView.d.ts +3 -0
- package/dist/features/admin/components/AdminGroupedListingsView.js +43 -0
- package/dist/features/admin/components/AdminHistoryView.d.ts +2 -3
- package/dist/features/admin/components/AdminHistoryView.js +35 -52
- package/dist/features/admin/components/AdminListingScaffold.d.ts +2 -2
- package/dist/features/admin/components/AdminListingScaffold.js +3 -3
- package/dist/features/admin/components/AdminLiveView.d.ts +3 -0
- package/dist/features/admin/components/AdminLiveView.js +50 -0
- package/dist/features/admin/components/AdminNewsletterView.d.ts +2 -2
- package/dist/features/admin/components/AdminNewsletterView.js +67 -97
- package/dist/features/admin/components/AdminNotificationsView.d.ts +2 -3
- package/dist/features/admin/components/AdminNotificationsView.js +67 -93
- package/dist/features/admin/components/AdminOrderEditorView.js +10 -10
- package/dist/features/admin/components/AdminOrdersView.d.ts +2 -3
- package/dist/features/admin/components/AdminOrdersView.js +93 -107
- package/dist/features/admin/components/AdminPayoutsView.d.ts +2 -3
- package/dist/features/admin/components/AdminPayoutsView.js +51 -91
- package/dist/features/admin/components/AdminPrizeDrawsView.d.ts +2 -3
- package/dist/features/admin/components/AdminPrizeDrawsView.js +39 -76
- package/dist/features/admin/components/AdminProductEditorView.js +3 -3
- package/dist/features/admin/components/AdminProductsView.d.ts +4 -4
- package/dist/features/admin/components/AdminProductsView.js +167 -163
- package/dist/features/admin/components/AdminReturnRequestsView.d.ts +1 -1
- package/dist/features/admin/components/AdminReturnRequestsView.js +64 -66
- package/dist/features/admin/components/AdminReviewsView.d.ts +3 -7
- package/dist/features/admin/components/AdminReviewsView.js +94 -110
- package/dist/features/admin/components/AdminScammersView.d.ts +2 -3
- package/dist/features/admin/components/AdminScammersView.js +53 -97
- package/dist/features/admin/components/AdminSectionsView.js +1 -1
- package/dist/features/admin/components/AdminSessionsView.d.ts +2 -3
- package/dist/features/admin/components/AdminSessionsView.js +62 -89
- package/dist/features/admin/components/AdminSiteSettingsView.js +4 -5
- package/dist/features/admin/components/AdminStoreAddressesView.d.ts +1 -1
- package/dist/features/admin/components/AdminStoreAddressesView.js +39 -53
- package/dist/features/admin/components/AdminStoreEditorView.js +21 -8
- package/dist/features/admin/components/AdminStoresView.d.ts +2 -3
- package/dist/features/admin/components/AdminStoresView.js +67 -99
- package/dist/features/admin/components/AdminSublistingCategoriesView.d.ts +1 -1
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +28 -44
- package/dist/features/admin/components/AdminSupportTicketDetailView.d.ts +10 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +191 -5
- package/dist/features/admin/components/AdminSupportTicketsView.d.ts +2 -3
- package/dist/features/admin/components/AdminSupportTicketsView.js +65 -96
- package/dist/features/admin/components/AdminTeamView.d.ts +2 -2
- package/dist/features/admin/components/AdminTeamView.js +40 -74
- package/dist/features/admin/components/AdminUserEditorView.d.ts +11 -1
- package/dist/features/admin/components/AdminUserEditorView.js +77 -7
- package/dist/features/admin/components/AdminUsersView.d.ts +2 -3
- package/dist/features/admin/components/AdminUsersView.js +108 -100
- package/dist/features/admin/components/AdminViewCards.d.ts +1 -1
- package/dist/features/admin/components/AdminViewCards.js +15 -15
- package/dist/features/admin/components/AdminWishlistsView.d.ts +2 -3
- package/dist/features/admin/components/AdminWishlistsView.js +36 -62
- package/dist/features/admin/components/DataListingView.d.ts +122 -0
- package/dist/features/admin/components/DataListingView.js +66 -0
- package/dist/features/admin/components/index.d.ts +10 -4
- package/dist/features/admin/components/index.js +6 -2
- package/dist/features/admin/constants/filter-tabs.d.ts +23 -17
- package/dist/features/admin/constants/filter-tabs.js +21 -17
- package/dist/features/admin/hooks/useAdminListing.d.ts +1 -0
- package/dist/features/admin/hooks/useAdminListing.js +1 -1
- package/dist/features/admin/schemas/firestore.js +6 -7
- package/dist/features/auctions/server.d.ts +0 -1
- package/dist/features/auctions/server.js +0 -1
- package/dist/features/auth/actions/realtime-token-actions.js +1 -1
- package/dist/features/auth/repository/user.repository.js +1 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +1 -0
- package/dist/features/cart/repository/cart.repository.d.ts +4 -1
- package/dist/features/cart/repository/cart.repository.js +12 -0
- package/dist/features/checkout/hooks/useCheckoutApi.d.ts +0 -2
- package/dist/features/checkout/index.d.ts +1 -1
- package/dist/features/events/components/AdminEventEditorView.js +10 -13
- package/dist/features/events/components/AdminEventsView.d.ts +3 -3
- package/dist/features/events/components/AdminEventsView.js +50 -86
- package/dist/features/events/components/EventCard.js +1 -0
- package/dist/features/events/components/EventOfferCard.d.ts +19 -0
- package/dist/features/events/components/EventOfferCard.js +29 -0
- package/dist/features/events/components/EventRaffleEntryForm.d.ts +25 -0
- package/dist/features/events/components/EventRaffleEntryForm.js +37 -0
- package/dist/features/events/components/EventRafflesSection.d.ts +1 -4
- package/dist/features/events/components/EventRafflesSection.js +17 -6
- package/dist/features/events/components/EventSaleBanner.d.ts +21 -0
- package/dist/features/events/components/EventSaleBanner.js +18 -0
- package/dist/features/events/components/index.d.ts +6 -0
- package/dist/features/events/components/index.js +4 -0
- package/dist/features/events/repository/event-entry.repository.d.ts +5 -0
- package/dist/features/events/repository/event-entry.repository.js +7 -0
- package/dist/features/faq/components/FAQPageContent.js +1 -0
- package/dist/features/filters/AsyncFacetSection.d.ts +1 -1
- package/dist/features/homepage/components/AdvertisementBanner.js +6 -6
- package/dist/features/homepage/components/FAQSection.d.ts +0 -5
- package/dist/features/homepage/components/HomepageSkeleton.js +2 -2
- package/dist/features/homepage/components/WelcomeSection.js +3 -3
- package/dist/features/homepage/lib/live-stats.js +1 -1
- package/dist/features/homepage/schemas/firestore.d.ts +0 -2
- package/dist/features/layout/TitleBar.js +1 -0
- package/dist/features/layout/index.d.ts +0 -2
- package/dist/features/layout/index.js +0 -1
- package/dist/features/pre-orders/server.d.ts +0 -1
- package/dist/features/pre-orders/server.js +0 -1
- package/dist/features/products/components/GroupSettingsPanel.js +2 -2
- package/dist/features/products/components/PrizeDrawItemsEditor.js +2 -2
- package/dist/features/products/components/PrizeDrawsSection.d.ts +1 -4
- package/dist/features/products/components/PrizeDrawsSection.js +17 -6
- package/dist/features/products/components/PrizeRevealModal.js +1 -1
- package/dist/features/products/components/ProductCardMetadataSection.d.ts +11 -0
- package/dist/features/products/components/ProductCardMetadataSection.js +25 -0
- package/dist/features/products/components/ProductDetailPageView.js +3 -1
- package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
- package/dist/features/products/components/ProductGradingTab.d.ts +13 -0
- package/dist/features/products/components/ProductGradingTab.js +25 -0
- package/dist/features/products/components/ProductGrid.js +15 -2
- package/dist/features/products/components/ProductTabsShell.d.ts +5 -1
- package/dist/features/products/components/ProductTabsShell.js +6 -1
- package/dist/features/products/components/index.d.ts +4 -0
- package/dist/features/products/components/index.js +3 -0
- package/dist/features/products/constants/action-defs.d.ts +5 -0
- package/dist/features/products/constants/action-defs.js +52 -32
- package/dist/features/products/constants/sieve.d.ts +24 -4
- package/dist/features/products/constants/sieve.js +20 -11
- package/dist/features/products/schemas/firestore.d.ts +17 -0
- package/dist/features/products/schemas/product-features.validators.d.ts +2 -2
- package/dist/features/products/schemas/product-templates.d.ts +2 -4
- package/dist/features/products/types/index.d.ts +4 -0
- package/dist/features/promotions/components/CouponsIndexListing.js +4 -4
- package/dist/features/promotions/server.d.ts +0 -1
- package/dist/features/promotions/server.js +0 -1
- package/dist/features/reviews/components/ReviewDetailShell.js +3 -3
- package/dist/features/search/api/route.js +44 -1
- package/dist/features/search/components/Search.js +2 -0
- package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
- package/dist/features/search/hooks/useNavSuggestions.js +31 -4
- package/dist/features/seller/components/BrandInlineSelect.js +2 -2
- package/dist/features/seller/components/CategoryInlineSelect.js +2 -2
- package/dist/features/seller/components/SellerAddressesView.js +4 -5
- package/dist/features/seller/components/SellerAuctionsView.d.ts +3 -3
- package/dist/features/seller/components/SellerAuctionsView.js +60 -90
- package/dist/features/seller/components/SellerBidsView.js +1 -1
- package/dist/features/seller/components/SellerBundlesView.js +64 -89
- package/dist/features/seller/components/SellerClassifiedView.js +67 -90
- package/dist/features/seller/components/SellerCouponEditorView.js +1 -1
- package/dist/features/seller/components/SellerCouponsView.d.ts +2 -2
- package/dist/features/seller/components/SellerCouponsView.js +45 -84
- package/dist/features/seller/components/SellerDigitalCodesView.js +63 -88
- package/dist/features/seller/components/SellerGroupedListingsView.js +33 -49
- package/dist/features/seller/components/SellerLiveView.js +63 -84
- package/dist/features/seller/components/SellerOffersPanel.js +1 -1
- package/dist/features/seller/components/SellerOffersView.d.ts +2 -2
- package/dist/features/seller/components/SellerOffersView.js +37 -69
- package/dist/features/seller/components/SellerOrdersView.d.ts +2 -2
- package/dist/features/seller/components/SellerOrdersView.js +4 -4
- package/dist/features/seller/components/SellerPayoutMethodsView.js +1 -1
- package/dist/features/seller/components/SellerPayoutsView.d.ts +2 -2
- package/dist/features/seller/components/SellerPayoutsView.js +34 -68
- package/dist/features/seller/components/SellerPreOrdersView.d.ts +2 -2
- package/dist/features/seller/components/SellerPreOrdersView.js +49 -82
- package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +2 -2
- package/dist/features/seller/components/SellerPrizeDrawsView.js +49 -82
- package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
- package/dist/features/seller/components/SellerProductsView.d.ts +2 -2
- package/dist/features/seller/components/SellerProductsView.js +15 -10
- package/dist/features/seller/components/SellerShippingConfigsView.js +1 -1
- package/dist/features/seller/components/SellerSidebar.d.ts +0 -4
- package/dist/features/seller/components/SellerSidebar.js +0 -2
- package/dist/features/seller/components/SellerStoreCategoriesView.js +1 -1
- package/dist/features/seller/components/SellerTemplatesView.js +2 -2
- package/dist/features/seller/components/SellersListView.d.ts +1 -2
- package/dist/features/seller/components/analytics/SellerAnalyticsStats.d.ts +5 -0
- package/dist/features/seller/components/analytics/SellerAnalyticsStats.js +2 -8
- package/dist/features/seller/components/index.d.ts +0 -3
- package/dist/features/seller/components/index.js +0 -2
- package/dist/features/seller/server.d.ts +0 -1
- package/dist/features/seller/server.js +0 -1
- package/dist/features/shell/FormShell.d.ts +1 -1
- package/dist/features/shell/FormShell.js +7 -4
- package/dist/features/shell/field-groups/ImageFieldGroup.d.ts +18 -0
- package/dist/features/shell/field-groups/ImageFieldGroup.js +15 -0
- package/dist/features/shell/field-groups/SeoFieldGroup.d.ts +22 -0
- package/dist/features/shell/field-groups/SeoFieldGroup.js +16 -0
- package/dist/features/shell/field-groups/StatusFieldGroup.d.ts +35 -0
- package/dist/features/shell/field-groups/StatusFieldGroup.js +16 -0
- package/dist/features/shell/field-groups/TitleDescriptionGroup.d.ts +40 -0
- package/dist/features/shell/field-groups/TitleDescriptionGroup.js +16 -0
- package/dist/features/shell/field-groups/index.d.ts +13 -0
- package/dist/features/shell/field-groups/index.js +9 -0
- package/dist/features/shell/index.d.ts +2 -0
- package/dist/features/shell/index.js +1 -0
- package/dist/features/stores/components/StoreCapabilitiesGuideView.d.ts +1 -2
- package/dist/features/stores/components/StoreFinanceGuideView.d.ts +1 -2
- package/dist/features/stores/components/StoreHeader.js +2 -1
- package/dist/features/stores/components/StoreListingsGuideView.d.ts +1 -2
- package/dist/features/stores/components/StoreOrdersGuideView.d.ts +1 -2
- package/dist/features/stores/components/StoreScopedSearch.d.ts +16 -0
- package/dist/features/stores/components/StoreScopedSearch.js +27 -0
- package/dist/features/stores/components/StoreSettingsGuideView.d.ts +1 -2
- package/dist/features/stores/server.d.ts +0 -1
- package/dist/features/stores/server.js +0 -1
- package/dist/features/support/schemas/firestore.d.ts +22 -1
- package/dist/features/support/schemas/firestore.js +10 -0
- package/dist/features/wishlist/server.d.ts +0 -1
- package/dist/features/wishlist/server.js +0 -1
- package/dist/index.d.ts +9 -27
- package/dist/index.js +14 -50
- package/dist/next/routing/route-map.d.ts +8 -0
- package/dist/next/routing/route-map.js +4 -0
- package/dist/react/hooks/useListingTypeFlags.d.ts +32 -0
- package/dist/react/hooks/useListingTypeFlags.js +19 -0
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/pii-redact.d.ts +8 -0
- package/dist/security/pii-redact.js +10 -1
- package/dist/seed/grouped-listings-seed-data.d.ts +4 -0
- package/dist/seed/products-preorders-seed-data.d.ts +4 -0
- package/dist/seed/products-prize-draws-seed-data.js +3 -0
- package/dist/seed/site-settings-seed-data.js +5 -5
- package/dist/seed/sub-listings-seed-data.d.ts +4 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +4 -0
- package/dist/styles.css +54 -395
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/BaseListingCard.d.ts +10 -0
- package/dist/ui/components/BaseListingCard.js +17 -1
- package/dist/ui/components/DetailPageGallery.d.ts +32 -0
- package/dist/ui/components/DetailPageGallery.js +47 -0
- package/dist/ui/components/DetailPageHero.d.ts +30 -0
- package/dist/ui/components/DetailPageHero.js +22 -0
- package/dist/ui/components/DetailPageTabs.d.ts +34 -0
- package/dist/ui/components/DetailPageTabs.js +38 -0
- package/dist/ui/components/HorizontalScroller.style.css +15 -2
- package/dist/ui/components/ListingLayout.d.ts +11 -2
- package/dist/ui/components/ListingLayout.js +12 -6
- package/dist/ui/components/PaginatedSelect.d.ts +66 -0
- package/dist/ui/components/PaginatedSelect.js +201 -0
- package/dist/ui/components/PaginatedSelect.style.css +254 -0
- package/dist/ui/components/Semantic.d.ts +0 -4
- package/dist/ui/components/Semantic.js +0 -2
- package/dist/ui/components/SettingsSection.d.ts +47 -0
- package/dist/ui/components/SettingsSection.js +38 -0
- package/dist/ui/components/index.style.css +1 -4
- package/dist/ui/index.d.ts +12 -14
- package/dist/ui/index.js +6 -7
- package/dist/utils/id-generators.d.ts +1 -18
- package/dist/utils/id-generators.js +51 -18
- package/dist/validation/schemas.d.ts +25 -0
- package/dist/validation/schemas.js +28 -0
- package/package.json +1 -1
|
@@ -61,8 +61,6 @@ export interface CarouselCard {
|
|
|
61
61
|
/** Legacy — kept for backward compat; use `zone` instead */
|
|
62
62
|
gridCol?: 1 | 2 | 3;
|
|
63
63
|
}
|
|
64
|
-
/** @deprecated Use CarouselCard. This alias is kept for backward compatibility. */
|
|
65
|
-
export type GridCard = CarouselCard;
|
|
66
64
|
export type GridCardCreateInput = Omit<CarouselCard, "id">;
|
|
67
65
|
/** A named carousel that holds up to MAX_SLIDES_PER_CAROUSEL slide references. */
|
|
68
66
|
export interface CarouselDocument {
|
|
@@ -22,8 +22,6 @@ export { LocaleSwitcher } from "./LocaleSwitcher";
|
|
|
22
22
|
export type { LocaleSwitcherProps, LocaleSwitcherOption, } from "./LocaleSwitcher";
|
|
23
23
|
export { BackToTop, SkipToMain } from "./BackToTop";
|
|
24
24
|
export type { BackToTopProps } from "./BackToTop";
|
|
25
|
-
export { ListingLayout } from "./ListingLayout";
|
|
26
|
-
export type { ListingLayoutProps, ListingLayoutLabels } from "./ListingLayout";
|
|
27
25
|
export { LayoutClient } from "./LayoutClient";
|
|
28
26
|
export type { LayoutClientProps, LayoutProvider } from "./LayoutClient";
|
|
29
27
|
export { AppLayoutShell } from "./AppLayoutShell";
|
|
@@ -12,7 +12,6 @@ export { AutoBreadcrumbs } from "./AutoBreadcrumbs";
|
|
|
12
12
|
export { Breadcrumbs, BreadcrumbItem } from "./Breadcrumbs";
|
|
13
13
|
export { LocaleSwitcher } from "./LocaleSwitcher";
|
|
14
14
|
export { BackToTop, SkipToMain } from "./BackToTop";
|
|
15
|
-
export { ListingLayout } from "./ListingLayout";
|
|
16
15
|
export { LayoutClient } from "./LayoutClient";
|
|
17
16
|
export { AppLayoutShell } from "./AppLayoutShell";
|
|
18
17
|
export { BottomActionsProvider, useBottomActionsContext, } from "./BottomActionsContext";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
|
-
import { Button, ConfirmDeleteModal, Div, FormField, Heading, Modal, Row, Select, SideDrawer, Stack, Text, Tabs, TabsContent, TabsList, TabsTrigger,
|
|
4
|
+
import { Button, ConfirmDeleteModal, Div, FormField, Heading, Modal, Row, Select, SideDrawer, Stack, Text, Tabs, TabsContent, TabsList, TabsTrigger, PaginatedSelect, useToast, } from "../../../ui";
|
|
5
5
|
import { apiClient } from "../../../http";
|
|
6
6
|
const CONDITION_OPTIONS = [
|
|
7
7
|
{ value: "new", label: "New" },
|
|
@@ -204,5 +204,5 @@ export function GroupSettingsPanel({ productId, productSlug, groupId, isGroupPar
|
|
|
204
204
|
_jsx("img", { src: child.images[0], alt: child.title, className: "w-8 h-8 rounded-full object-cover border border-zinc-200 dark:border-zinc-700" })) : (_jsx("div", { className: "w-8 h-8 rounded-full bg-zinc-100 dark:bg-zinc-800" })), _jsx(Text, { className: "text-sm text-zinc-800 dark:text-zinc-200 flex-1 truncate", children: child.title }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => requestUnlinkChild(child.id), isLoading: loading, className: "text-red-500 hover:text-red-600 text-xs", children: "Unlink" })] }, child.id)))] }))] }), _jsxs(Row, { gap: "sm", className: "flex-wrap", children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setShowAddModal(true), children: "Add child listing" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestDissolveGroup, isLoading: loading, className: "text-red-500 hover:text-red-600", children: "Dissolve group" })] })] })), !isGroupParent && groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { className: "text-sm text-zinc-600 dark:text-zinc-400", children: ["Part of:", " ", _jsx("span", { className: "font-medium text-zinc-800 dark:text-zinc-200", children: groupTitle ?? groupParentSlug })] }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["Parent listing: ", _jsx("code", { className: "font-mono", children: groupParentSlug })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestLeaveGroup, isLoading: loading, className: "text-red-500 hover:text-red-600 w-fit", children: "Leave group" })] }))] })), (groupChildSlugs?.length ?? 0) >= 4 ? (_jsx(SideDrawer, { isOpen: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, linkTarget: linkTarget, setLinkTarget: setLinkTarget, loadLinkOptions: loadLinkOptions, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })) : (_jsx(Modal, { open: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", size: "lg", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, linkTarget: linkTarget, setLinkTarget: setLinkTarget, loadLinkOptions: loadLinkOptions, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })), confirmAction && (_jsx(ConfirmDeleteModal, { isOpen: true, title: confirmAction.title, message: confirmAction.message, onConfirm: confirmAction.onConfirm, onClose: () => setConfirmAction(null), isDeleting: loading }))] }));
|
|
205
205
|
}
|
|
206
206
|
function AddChildContent({ addTab, setAddTab, createForm, setCreateForm, productSlug, linkTarget, setLinkTarget, loadLinkOptions, onAddCreate, onAddLink, loading, }) {
|
|
207
|
-
return (_jsx(Stack, { gap: "md", children: _jsxs(Tabs, { value: addTab, onChange: (v) => setAddTab(v), children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "create", children: "Create new child" }), _jsx(TabsTrigger, { value: "link", children: "Link existing" })] }), _jsx(TabsContent, { value: "create", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(FormField, { name: "childTitle", label: "Title *", type: "text", value: createForm.title, onChange: (v) => setCreateForm({ ...createForm, title: v }), placeholder: `${productSlug}-part` }), _jsx(FormField, { name: "childPrice", label: "Price (\u20B9) *", type: "number", value: createForm.price, onChange: (v) => setCreateForm({ ...createForm, price: v }), placeholder: "0" }), _jsx(Select, { label: "Condition", value: createForm.condition, onValueChange: (v) => setCreateForm({ ...createForm, condition: v }), options: CONDITION_OPTIONS }), _jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400/80", children: "Other fields (category, brand, shipping, return policy) are inherited from this parent listing. Need more control? Edit the full listing after saving." }), _jsx(Button, { type: "button", onClick: onAddCreate, isLoading: loading, disabled: !createForm.title || !createForm.price, children: "Create and link child" })] }) }), _jsx(TabsContent, { value: "link", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Search your existing products or pre-orders. Auctions cannot be linked." }), _jsx(
|
|
207
|
+
return (_jsx(Stack, { gap: "md", children: _jsxs(Tabs, { value: addTab, onChange: (v) => setAddTab(v), children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "create", children: "Create new child" }), _jsx(TabsTrigger, { value: "link", children: "Link existing" })] }), _jsx(TabsContent, { value: "create", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(FormField, { name: "childTitle", label: "Title *", type: "text", value: createForm.title, onChange: (v) => setCreateForm({ ...createForm, title: v }), placeholder: `${productSlug}-part` }), _jsx(FormField, { name: "childPrice", label: "Price (\u20B9) *", type: "number", value: createForm.price, onChange: (v) => setCreateForm({ ...createForm, price: v }), placeholder: "0" }), _jsx(Select, { label: "Condition", value: createForm.condition, onValueChange: (v) => setCreateForm({ ...createForm, condition: v }), options: CONDITION_OPTIONS }), _jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400/80", children: "Other fields (category, brand, shipping, return policy) are inherited from this parent listing. Need more control? Edit the full listing after saving." }), _jsx(Button, { type: "button", onClick: onAddCreate, isLoading: loading, disabled: !createForm.title || !createForm.price, children: "Create and link child" })] }) }), _jsx(TabsContent, { value: "link", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Search your existing products or pre-orders. Auctions cannot be linked." }), _jsx(PaginatedSelect, { value: linkTarget, onChange: (v) => setLinkTarget(v), loadOptions: loadLinkOptions, placeholder: "Search listings\u2026", searchPlaceholder: "Type title or slug\u2026", noResultsText: "No matching listings found", ariaLabel: "Listing to link" }), _jsx(Button, { type: "button", onClick: onAddLink, isLoading: loading, disabled: !linkTarget, children: "Link to group" })] }) })] }) }));
|
|
208
208
|
}
|
|
@@ -84,7 +84,7 @@ export function PrizeDrawItemsEditor({ items, onChange, onUploadImage, onUploadV
|
|
|
84
84
|
const nextImgs = (cur.images ?? []).filter((_, i) => i !== slot);
|
|
85
85
|
update(index, { images: nextImgs });
|
|
86
86
|
};
|
|
87
|
-
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Heading, { level: 3, children: ["Prize Items (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "secondary", onClick: addItem, disabled: anyWon || items.length >= MAX_ITEMS, children: ["+ Add prize (", items.length, "/", MAX_ITEMS, ")"] })] }), _jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: ["Add between ", MIN_ITEMS, " and ", MAX_ITEMS, " prizes. Each entry will reveal exactly one of these. Items marked won during a prior reveal cannot be edited or removed."] }), anyWon ? (_jsxs(Div, { className: "rounded border border-
|
|
87
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Heading, { level: 3, children: ["Prize Items (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "secondary", onClick: addItem, disabled: anyWon || items.length >= MAX_ITEMS, children: ["+ Add prize (", items.length, "/", MAX_ITEMS, ")"] })] }), _jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: ["Add between ", MIN_ITEMS, " and ", MAX_ITEMS, " prizes. Each entry will reveal exactly one of these. Items marked won during a prior reveal cannot be edited or removed."] }), anyWon ? (_jsxs(Div, { className: "rounded border border-error/40 bg-error-surface px-3 py-2 text-sm text-error", children: [_jsx(Span, { weight: "bold", children: "Draw locked." }), " At least one prize has been revealed \u2014 this listing can no longer be edited. To run a similar draw, clone it into a new prize-draw listing."] })) : warning ? (_jsx(Div, { className: "rounded border border-yellow-400/40 bg-yellow-50 px-3 py-2 text-sm text-yellow-900 dark:bg-yellow-900/30 dark:text-yellow-100", children: warning })) : null, _jsx(Stack, { gap: "md", children: items.map((it, index) => {
|
|
88
88
|
const locked = it.isWon || anyWon;
|
|
89
89
|
return (_jsxs(Div, { className: `relative rounded-lg border border-[var(--appkit-color-border)] p-4 ${locked ? "opacity-60" : ""}`, children: [locked ? (_jsx(Div, { className: "absolute inset-0 z-10 flex items-center justify-center bg-black/10 dark:bg-black/40 rounded-lg pointer-events-none", children: _jsx(Text, { className: "rounded bg-red-600 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-white", children: "Won \u2014 locked" }) })) : null, _jsxs(Row, { justify: "between", align: "center", className: "mb-3", children: [_jsxs(Heading, { level: 4, children: ["Prize #", it.itemNumber] }), _jsxs(Row, { gap: "xs", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => move(index, -1), disabled: locked || index === 0, "aria-label": "Move up", children: "\u2191" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => move(index, 1), disabled: locked || index === items.length - 1, "aria-label": "Move down", children: "\u2193" }), _jsx(Button, { type: "button", variant: "danger", size: "sm", onClick: () => removeItem(index), disabled: locked || items.length <= MIN_ITEMS, children: "Remove" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(FormField, { name: `item-${index}-title`, label: "Title", type: "text", value: it.title, onChange: (v) => update(index, { title: v }), disabled: locked, placeholder: "e.g. PSA 9 Charizard Base Set Holo" }), _jsx(FormField, { name: `item-${index}-description`, label: "Description (optional)", type: "textarea", value: it.description ?? "", onChange: (v) => update(index, { description: v }), disabled: locked, placeholder: "What makes this prize special?" }), _jsxs(Row, { gap: "md", children: [_jsx(Div, { className: "flex-1", children: _jsx(FormField, { name: `item-${index}-condition`, label: "Condition", type: "select", value: it.condition, onChange: (v) => update(index, { condition: v }), disabled: locked, options: CONDITION_OPTIONS }) }), _jsx(Div, { className: "flex-1", children: _jsx(FormField, { name: `item-${index}-value`, label: "Estimated value (\u20B9)", type: "number", value: it.estimatedValue != null
|
|
90
90
|
? String(Math.round(it.estimatedValue / 100))
|
|
@@ -94,6 +94,6 @@ export function PrizeDrawItemsEditor({ items, onChange, onUploadImage, onUploadV
|
|
|
94
94
|
const existing = (it.images ?? [])[slot];
|
|
95
95
|
return (_jsxs(Div, { className: "w-40", children: [_jsx(ImageUpload, { currentImage: existing, label: existing ? "Replace" : `Image ${slot + 1}`, onUpload: (file) => onUploadImage(file, it.itemNumber), onChange: (url) => setImage(index, slot, url) }), existing && !locked ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeImage(index, slot), children: "Remove" })) : null] }, slot));
|
|
96
96
|
}) })] }), onUploadVideo ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-sm font-medium", children: "Video (optional)" }), _jsx("input", { type: "file", accept: "video/*", disabled: locked, onChange: (e) => handleVideoFileChange(e, index, it.itemNumber) }), it.video?.url ? (_jsxs(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] truncate", children: ["Current: ", it.video.url] })) : null] })) : null] })] }, `prize-item-${it.itemNumber}-${index}`));
|
|
97
|
-
}) }), items.length < MIN_ITEMS ? (_jsxs(Div, { className: "rounded border border-
|
|
97
|
+
}) }), items.length < MIN_ITEMS ? (_jsxs(Div, { className: "rounded border border-error/40 bg-error-surface px-3 py-2 text-sm text-error", children: ["At least ", MIN_ITEMS, " prizes are required."] })) : null] }));
|
|
98
98
|
}
|
|
99
99
|
export default PrizeDrawItemsEditor;
|
|
@@ -4,9 +4,6 @@ export interface PrizeDrawsSectionProps {
|
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
6
|
* Renders a prize-draws strip on the homepage.
|
|
7
|
-
*
|
|
8
|
-
* Reads products with `listingType="prize-draw"` and `prizeRevealStatus` in the
|
|
9
|
-
* configured set. Those fields are added later in the prize-draw block; for now
|
|
10
|
-
* the component renders an empty state and the section ships disabled in seed.
|
|
7
|
+
* W1-38 (2026-05-23): fetches active prize-draw listings from productRepository.
|
|
11
8
|
*/
|
|
12
9
|
export declare function PrizeDrawsSection({ config, }: PrizeDrawsSectionProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
@@ -2,16 +2,27 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import Link from "next/link";
|
|
3
3
|
import { Container, Heading, Section, Stack, Text, } from "../../../ui";
|
|
4
4
|
import { ROUTES } from "../../../next";
|
|
5
|
+
import { productRepository } from "../repository/products.repository";
|
|
6
|
+
import { InteractiveProductCard } from "./InteractiveProductCard";
|
|
5
7
|
/**
|
|
6
8
|
* Renders a prize-draws strip on the homepage.
|
|
7
|
-
*
|
|
8
|
-
* Reads products with `listingType="prize-draw"` and `prizeRevealStatus` in the
|
|
9
|
-
* configured set. Those fields are added later in the prize-draw block; for now
|
|
10
|
-
* the component renders an empty state and the section ships disabled in seed.
|
|
9
|
+
* W1-38 (2026-05-23): fetches active prize-draw listings from productRepository.
|
|
11
10
|
*/
|
|
12
11
|
export async function PrizeDrawsSection({ config, }) {
|
|
13
12
|
const title = config.title ?? "Prize Draws";
|
|
14
13
|
const subtitle = config.subtitle ?? "Enter for a chance to win rare collectibles";
|
|
15
|
-
const
|
|
16
|
-
|
|
14
|
+
const limit = 8;
|
|
15
|
+
let draws = [];
|
|
16
|
+
try {
|
|
17
|
+
const result = await productRepository.list({
|
|
18
|
+
filters: "listingType==prize-draw,status==published",
|
|
19
|
+
sorts: "-createdAt",
|
|
20
|
+
pageSize: limit,
|
|
21
|
+
});
|
|
22
|
+
draws = (result.items ?? []);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
draws = [];
|
|
26
|
+
}
|
|
27
|
+
return (_jsx(Section, { className: "py-10", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, className: "text-2xl font-semibold text-zinc-900 dark:text-zinc-50", children: title }), subtitle ? (_jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: subtitle })) : null] }), draws.length === 0 ? (_jsxs(Stack, { align: "center", gap: "sm", className: "rounded-2xl border border-dashed border-zinc-300 px-6 py-12 text-center dark:border-slate-700", children: [_jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "No active prize draws \u2014 new draws are announced regularly." }), _jsx(Link, { href: String(ROUTES.PUBLIC.AUCTIONS), className: "text-sm font-medium text-primary hover:underline", children: "Browse live auctions \u2192" })] })) : (_jsx("div", { className: "fluid-grid-card gap-3", children: draws.map((draw) => (_jsx(InteractiveProductCard, { product: draw, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(draw.slug ?? draw.id ?? "")) }, draw.id))) }))] }) }) }));
|
|
17
28
|
}
|
|
@@ -143,7 +143,7 @@ export function PrizeRevealModal({ open, onClose, items, orderId, productId, onR
|
|
|
143
143
|
}, REVEAL_DURATION_MS);
|
|
144
144
|
}, [onReveal, orderId, phase, productId, startAnimation]);
|
|
145
145
|
const winnerImg = winner?.images?.[0];
|
|
146
|
-
return (_jsxs(Modal, { isOpen: open, onClose: onClose, title: "Prize Reveal", size: "lg", children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to reveal your prize. Please log in or create an account to continue." }), _jsxs(Stack, { gap: "md", children: [phase === "refunded" ? (_jsxs(Div, { className: "rounded border border-yellow-400/40 bg-yellow-50 px-4 py-3 text-yellow-900 dark:bg-yellow-900/30 dark:text-yellow-100", children: [_jsx(Heading, { level: 3, className: "mb-1", children: "Pool exhausted \u2014 you've been refunded" }), _jsx(Text, { className: "text-sm", children: "Every prize in this draw was already claimed by the time your entry rolled. Your order has been marked refunded automatically." })] })) : null, phase === "error" ? (_jsxs(Div, { className: "rounded border border-
|
|
146
|
+
return (_jsxs(Modal, { isOpen: open, onClose: onClose, title: "Prize Reveal", size: "lg", children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to reveal your prize. Please log in or create an account to continue." }), _jsxs(Stack, { gap: "md", children: [phase === "refunded" ? (_jsxs(Div, { className: "rounded border border-yellow-400/40 bg-yellow-50 px-4 py-3 text-yellow-900 dark:bg-yellow-900/30 dark:text-yellow-100", children: [_jsx(Heading, { level: 3, className: "mb-1", children: "Pool exhausted \u2014 you've been refunded" }), _jsx(Text, { className: "text-sm", children: "Every prize in this draw was already claimed by the time your entry rolled. Your order has been marked refunded automatically." })] })) : null, phase === "error" ? (_jsxs(Div, { className: "rounded border border-error/40 bg-error-surface px-4 py-3 text-error", children: [_jsx(Text, { className: "text-sm font-semibold", children: "Something went wrong" }), _jsx(Text, { className: "text-sm", children: errorMessage })] })) : null, _jsx(PrizeDrawCollage, { items: items, hideWonState: true, highlightItemNumber: highlight }), phase === "idle" ? (_jsx(Button, { type: "button", variant: "primary", size: "lg", onClick: handleRevealClick, children: "\u2728 Reveal my prize" })) : null, phase === "revealing" ? (_jsxs(Div, { className: "rounded bg-[var(--appkit-color-surface-muted)] p-4 text-center", children: [_jsx(Text, { className: "text-lg font-semibold", children: "Rolling\u2026" }), _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "The winner was locked by the server before this animation started. Hang tight \u2014 we're just making it look pretty." })] })) : null, phase === "won" && winner ? (_jsxs(Div, { className: "rounded-lg border-2 border-[var(--appkit-color-primary)] bg-[var(--appkit-color-surface)] p-4 text-center", children: [_jsx(Text, { className: "text-xs uppercase tracking-wider text-[var(--appkit-color-text-muted)]", children: "You won" }), _jsxs(Heading, { level: 2, className: "my-2", children: ["#", winner.itemNumber, " \u2014 ", winner.title] }), winnerImg ? (
|
|
147
147
|
/* eslint-disable-next-line @next/next/no-img-element */
|
|
148
148
|
_jsx("img", { src: winnerImg, alt: winner.title, className: "mx-auto max-h-64 rounded" })) : null, winner.estimatedValue != null ? (_jsxs(Text, { className: "mt-2 text-sm text-[var(--appkit-color-text-muted)]", children: ["Estimated value: \u20B9", (winner.estimatedValue / 100).toLocaleString("en-IN")] })) : null] })) : null, _jsxs(Div, { className: "rounded border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-muted)] px-3 py-2 text-xs text-[var(--appkit-color-text-muted)]", children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Fairness guarantee:" }), " ", "Winners are picked by ", _jsx("code", { children: "crypto.randomInt" }), " running on LetItRip's server before the animation starts. The animation is theatrical \u2014 neither the store nor LetItRip staff can influence the outcome.", " ", effectiveRngUrl ? (_jsx("a", { href: effectiveRngUrl, target: "_blank", rel: "noreferrer noopener", className: "underline", children: "View RNG source code \u2192" })) : null] })] })] }));
|
|
149
149
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProductCardMetadata } from "../schemas/firestore";
|
|
2
|
+
export interface ProductCardMetadataSectionProps {
|
|
3
|
+
card: ProductCardMetadata;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* `ProductCardMetadataSection` — W1-34 — renders single-card aftermarket
|
|
7
|
+
* metadata (set name / year / card number / rarity / language). Designed to
|
|
8
|
+
* be embedded inside the "Specifications" tab content; rendered as a key/value
|
|
9
|
+
* table for predictable line-up on PDPs.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ProductCardMetadataSection({ card }: ProductCardMetadataSectionProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Div, Heading, Stack, Text } from "../../../ui";
|
|
5
|
+
/**
|
|
6
|
+
* `ProductCardMetadataSection` — W1-34 — renders single-card aftermarket
|
|
7
|
+
* metadata (set name / year / card number / rarity / language). Designed to
|
|
8
|
+
* be embedded inside the "Specifications" tab content; rendered as a key/value
|
|
9
|
+
* table for predictable line-up on PDPs.
|
|
10
|
+
*/
|
|
11
|
+
export function ProductCardMetadataSection({ card }) {
|
|
12
|
+
const rows = [];
|
|
13
|
+
rows.push(["Set", card.setName]);
|
|
14
|
+
if (card.setYear)
|
|
15
|
+
rows.push(["Year", String(card.setYear)]);
|
|
16
|
+
if (card.cardNumber)
|
|
17
|
+
rows.push(["Card #", card.cardNumber]);
|
|
18
|
+
if (card.rarity)
|
|
19
|
+
rows.push(["Rarity", card.rarity]);
|
|
20
|
+
if (card.language)
|
|
21
|
+
rows.push(["Language", card.language.toUpperCase()]);
|
|
22
|
+
if (rows.length === 0)
|
|
23
|
+
return null;
|
|
24
|
+
return (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 3, className: "text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "Card details" }), _jsx(Div, { className: "grid grid-cols-2 gap-y-1 text-sm sm:grid-cols-[140px_1fr]", children: rows.map(([label, value]) => (_jsxs(React.Fragment, { children: [_jsx(Text, { className: "text-zinc-500 dark:text-zinc-400", children: label }), _jsx(Text, { className: "font-medium text-zinc-900 dark:text-zinc-100", children: value })] }, label))) })] }));
|
|
25
|
+
}
|
|
@@ -262,7 +262,9 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
|
|
|
262
262
|
codAvailable: "Cash on Delivery",
|
|
263
263
|
wishlistCount: (n) => `${n} wishlisted`,
|
|
264
264
|
categoryProductCount: (n, cat) => `${n} in ${cat}`,
|
|
265
|
-
} }), productFeatures && features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures })), !productFeatures && features.length > 0 && (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 px-4 py-3", children: [_jsx(Text, { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "About this product" }), _jsx("ul", { className: "space-y-1.5", children: features.map((f, i) => (_jsxs("li", { className: "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i))) })] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-sm leading-relaxed text-zinc-600 dark:text-zinc-400 line-clamp-4" })), safeSeller && (_jsx(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-3", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] uppercase tracking-wide text-zinc-400 dark:text-zinc-400 mb-0.5", children: "Sold by" }), _jsx(Text, { className: "text-sm font-semibold text-zinc-800 dark:text-zinc-200", children: safeSeller })
|
|
265
|
+
} }), productFeatures && features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures })), !productFeatures && features.length > 0 && (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 px-4 py-3", children: [_jsx(Text, { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "About this product" }), _jsx("ul", { className: "space-y-1.5", children: features.map((f, i) => (_jsxs("li", { className: "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i))) })] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-sm leading-relaxed text-zinc-600 dark:text-zinc-400 line-clamp-4" })), safeSeller && (_jsx(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-3", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] uppercase tracking-wide text-zinc-400 dark:text-zinc-400 mb-0.5", children: "Sold by" }), _jsx(Text, { className: "text-sm font-semibold text-zinc-800 dark:text-zinc-200", children: safeSeller }), typeof p.storeRating === "number" && p.storeRating > 0 && (_jsx(Row, { gap: "xs", align: "center", className: "mt-0.5", children: _jsxs(Text, { size: "xs", color: "muted", children: [_jsx("span", { className: "text-warning", "aria-hidden": "true", children: "\u2605" }), " ", p.storeRating.toFixed(1), typeof p.storeReviewCount === "number" &&
|
|
266
|
+
p.storeReviewCount > 0 &&
|
|
267
|
+
` · ${p.storeReviewCount} reviews`] }) }))] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-3 py-1.5 text-xs font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderActions: () => (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5 space-y-4", children: [formattedPrice && (_jsxs(Div, { children: [_jsxs(Row, { align: "baseline", gap: "sm", wrap: true, children: [_jsx(Text, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formattedPrice }), formattedOriginal && discount && (_jsxs(_Fragment, { children: [_jsx(Span, { className: "text-sm text-zinc-400 line-through dark:text-zinc-400", children: formattedOriginal }), _jsxs(Span, { className: "rounded-full bg-red-500 px-2 py-0.5 text-xs font-bold text-white", children: ["-", discount, "%"] })] }))] }), inStock && effectiveStock !== null && effectiveStock <= 10 && (_jsxs(Text, { className: "mt-1 text-xs text-warning", children: ["Only ", effectiveStock, " left \u2014 order soon!"] }))] })), _jsxs(Stack, { gap: "sm", children: [renderPrimaryActions ? (renderPrimaryActions({
|
|
266
268
|
productId: product.id,
|
|
267
269
|
productSlug: product.slug ?? slug,
|
|
268
270
|
productTitle: product.title,
|
|
@@ -12,7 +12,7 @@ const ROW_BASE_CLASS = "flex items-start gap-2 rounded-lg border px-3 py-2 text-
|
|
|
12
12
|
const ROW_CHECKED_CLASS = "border-primary bg-primary/5 dark:bg-primary/10";
|
|
13
13
|
const ROW_DISABLED_CLASS = "border-zinc-200 dark:border-slate-700 opacity-50 cursor-not-allowed";
|
|
14
14
|
const ROW_DEFAULT_CLASS = "border-zinc-200 dark:border-slate-700 hover:bg-zinc-50 dark:hover:bg-slate-800";
|
|
15
|
-
const OVER_LIMIT_BANNER_CLASS = "rounded-lg border border-
|
|
15
|
+
const OVER_LIMIT_BANNER_CLASS = "rounded-lg border border-error/20 bg-error-surface px-3 py-2 text-xs text-error";
|
|
16
16
|
const EMPTY_STATE_CLASS = "rounded-lg border border-zinc-200 dark:border-slate-700 bg-white dark:bg-slate-900 px-3 py-4 text-xs text-zinc-500 dark:text-zinc-400 text-center";
|
|
17
17
|
function applyProductType(list, productType) {
|
|
18
18
|
if (productType === "all")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ProductGrading } from "../schemas/firestore";
|
|
2
|
+
export interface ProductGradingTabProps {
|
|
3
|
+
grading: ProductGrading;
|
|
4
|
+
/** Optional resolved media URL for the slab/grade image. */
|
|
5
|
+
slabImageUrl?: string | null;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* `ProductGradingTab` — W1-34 — renders TCG slab grading metadata
|
|
9
|
+
* (`product.grading` ProductGrading). Used as the `gradingContent` slot of
|
|
10
|
+
* `ProductTabsShell`. Always renders the service + grade prominently; cert
|
|
11
|
+
* lookup link is wired for PSA/BGS where the URL pattern is well-known.
|
|
12
|
+
*/
|
|
13
|
+
export declare function ProductGradingTab({ grading, slabImageUrl }: ProductGradingTabProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { Div, Heading, Stack, Text } from "../../../ui";
|
|
4
|
+
import { Badge } from "../../../ui/components/Badge";
|
|
5
|
+
const PSA_CERT_URL = (cert) => `https://www.psacard.com/cert/${cert}`;
|
|
6
|
+
const BGS_CERT_URL = (cert) => `https://www.beckett.com/grading/card-lookup/${cert}`;
|
|
7
|
+
function buildCertLookupUrl(service, certNumber) {
|
|
8
|
+
if (!certNumber)
|
|
9
|
+
return null;
|
|
10
|
+
if (service === "PSA")
|
|
11
|
+
return PSA_CERT_URL(certNumber);
|
|
12
|
+
if (service === "BGS")
|
|
13
|
+
return BGS_CERT_URL(certNumber);
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* `ProductGradingTab` — W1-34 — renders TCG slab grading metadata
|
|
18
|
+
* (`product.grading` ProductGrading). Used as the `gradingContent` slot of
|
|
19
|
+
* `ProductTabsShell`. Always renders the service + grade prominently; cert
|
|
20
|
+
* lookup link is wired for PSA/BGS where the URL pattern is well-known.
|
|
21
|
+
*/
|
|
22
|
+
export function ProductGradingTab({ grading, slabImageUrl }) {
|
|
23
|
+
const lookupUrl = buildCertLookupUrl(grading.service, grading.certNumber);
|
|
24
|
+
return (_jsxs(Stack, { gap: "lg", children: [_jsxs(Div, { className: "flex items-center gap-4", children: [_jsxs(Div, { className: "flex flex-col items-center justify-center rounded-2xl border-2 border-[var(--appkit-color-primary)] px-6 py-4 min-w-[120px]", children: [_jsx(Text, { className: "text-xs font-bold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: grading.service }), _jsx(Text, { className: "text-4xl font-extrabold", children: grading.grade })] }), _jsxs(Stack, { gap: "xs", className: "flex-1", children: [_jsxs(Heading, { level: 3, className: "text-base font-semibold", children: ["Graded by ", grading.service] }), grading.certNumber ? (_jsxs(Text, { className: "text-sm text-zinc-600 dark:text-zinc-400", children: ["Cert #", grading.certNumber, lookupUrl ? (_jsxs(_Fragment, { children: [" — ", _jsx("a", { href: lookupUrl, target: "_blank", rel: "noopener noreferrer", className: "text-[var(--appkit-color-primary)] hover:underline", children: "Verify" })] })) : null] })) : null] })] }), slabImageUrl ? (_jsx(Div, { className: "overflow-hidden rounded-2xl border border-zinc-200 dark:border-zinc-700", children: _jsx("img", { src: slabImageUrl, alt: "Slab certificate image", className: "w-full h-auto" }) })) : null, grading.attributes && Object.keys(grading.attributes).length > 0 ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 3, className: "text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "Subgrades & attributes" }), _jsx(Div, { className: "flex flex-wrap gap-2", children: Object.entries(grading.attributes).map(([key, value]) => (_jsxs(Badge, { variant: "default", children: [key, ": ", value] }, key))) })] })) : null] }));
|
|
25
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
3
|
import React from "react";
|
|
3
4
|
import Link from "next/link";
|
|
@@ -49,7 +50,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
|
|
|
49
50
|
.filter(Boolean)
|
|
50
51
|
.join(" "), children: [_jsxs(Div, { className: "relative overflow-hidden bg-zinc-100 dark:bg-slate-800 aspect-square", children: [product.mainImage ? (_jsx(MediaImage, { src: product.mainImage, alt: product.title, size: "card", className: "transition-transform duration-500 group-hover:scale-105" })) : (_jsx(Div, { className: "h-full w-full flex items-center justify-center bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700", children: _jsx(Span, { className: "text-4xl opacity-30", children: "\uD83D\uDECD\uFE0F" }) })), onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(product.id); }, label: isSelected ? "Deselect" : "Select", position: "top-2 left-2", className: selectionMode || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsx(Div, { className: `absolute top-2 flex flex-col gap-1 ${onSelect ? "left-10" : "left-2"}`, children: !selectionMode && !isSelected && (_jsxs(_Fragment, { children: [discount && (_jsxs(Span, { className: "rounded-full bg-rose-500 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", children: ["-", discount, "%"] })), typeBadge && (_jsx(Span, { className: `rounded-full px-2 py-0.5 text-[10px] font-bold shadow-sm ${typeBadge.cls}`, children: typeBadge.label })), product.partOfBundleIds && product.partOfBundleIds.length > 0 && (_jsx(Span, { className: "rounded-full bg-teal-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", title: product.partOfBundleTitles && product.partOfBundleTitles.length > 0
|
|
51
52
|
? `In bundle: ${product.partOfBundleTitles[0]}`
|
|
52
|
-
: "In a bundle", children: "Bundled" })), product.groupId && (_jsx(Span, { className: "rounded-full bg-indigo-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", title: product.groupTitle ? `Part of set: ${product.groupTitle}` : "Part of a set", children: product.isGroupParent ? "Set Parent" : "In Set" })), product.sublistingCategoryId && (_jsx(Span, { className: "rounded-full bg-sky-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", title: "Has variants or sub-listings", children: "Has Variants" }))] })) }), onAddToWishlist && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => {
|
|
53
|
+
: "In a bundle", children: "Bundled" })), product.groupId && (_jsx(Span, { className: "rounded-full bg-indigo-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", title: product.groupTitle ? `Part of set: ${product.groupTitle}` : "Part of a set", children: product.isGroupParent ? "Set Parent" : "In Set" })), product.sublistingCategoryId && (_jsx(Span, { className: "rounded-full bg-sky-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", title: "Has variants or sub-listings", children: "Has Variants" }))] })) }), _jsx(BaseListingCard.IconOverlay, { groupIcon: product.groupIcon, sublistingIcon: product.sublistingIcon, position: "bottom-2 right-2" }), onAddToWishlist && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => {
|
|
53
54
|
e.stopPropagation();
|
|
54
55
|
e.preventDefault();
|
|
55
56
|
onAddToWishlist(product.id);
|
|
@@ -59,7 +60,19 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
|
|
|
59
60
|
isWishlisted
|
|
60
61
|
? "bg-rose-500 text-white hover:bg-rose-600"
|
|
61
62
|
: "bg-white/90 dark:bg-slate-800/90 text-zinc-500 dark:text-zinc-400 hover:text-rose-500 hover:bg-white dark:hover:bg-slate-800",
|
|
62
|
-
].join(" "), children: _jsx("svg", { className: "h-4 w-4", fill: isWishlisted ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" }) }) }))] }), _jsxs(Div, { className: "flex flex-1 flex-col p-3 pt-2.5", children: [_jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-900 dark:text-white leading-snug`, children: product.title }), ((
|
|
63
|
+
].join(" "), children: _jsx("svg", { className: "h-4 w-4", fill: isWishlisted ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" }) }) }))] }), _jsxs(Div, { className: "flex flex-1 flex-col p-3 pt-2.5", children: [_jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-900 dark:text-white leading-snug`, children: product.title }), (() => {
|
|
64
|
+
const categories = Array.isArray(product.categoryNames)
|
|
65
|
+
? product.categoryNames.filter(Boolean)
|
|
66
|
+
: product.categoryName
|
|
67
|
+
? [product.categoryName]
|
|
68
|
+
: [];
|
|
69
|
+
if (!categories.length && !product.brand)
|
|
70
|
+
return null;
|
|
71
|
+
const MAX_CHIPS = 3;
|
|
72
|
+
const visible = categories.slice(0, MAX_CHIPS);
|
|
73
|
+
const overflow = Math.max(0, categories.length - MAX_CHIPS);
|
|
74
|
+
return (_jsxs(Row, { className: "mt-1 gap-1 flex-wrap", children: [visible.map((name) => (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-600 dark:text-zinc-400 truncate max-w-[100px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 7a2 2 0 012-2h14a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V7z" }) }), name] }, name))), overflow > 0 && (_jsxs(Span, { className: "inline-flex items-center rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] font-medium text-zinc-600 dark:text-zinc-400", title: categories.slice(MAX_CHIPS).join(", "), children: ["+", overflow] })), product.brand && (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-600 dark:text-zinc-400 truncate max-w-[90px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" }) }), product.brand] }))] }));
|
|
75
|
+
})(), (() => {
|
|
63
76
|
const seller = safeDisplayName(product.storeName, "");
|
|
64
77
|
return seller ? (_jsxs(Text, { className: "mt-0.5 text-[11px] text-zinc-400 dark:text-zinc-400 truncate", children: ["by ", seller] })) : null;
|
|
65
78
|
})(), product.rating !== undefined && (_jsxs(Row, { className: "mt-1 gap-1", children: [_jsx(Span, { className: "text-[11px] text-amber-400", children: "\u2605" }), _jsxs(Span, { className: "text-[11px] text-zinc-500 dark:text-zinc-400", children: [product.rating.toFixed(1), product.reviewCount ? ` (${product.reviewCount})` : ""] })] })), _jsxs(Div, { className: "mt-auto pt-2", children: [_jsxs(Row, { align: "baseline", className: "gap-2", children: [_jsx(Span, { className: "text-base font-bold text-primary dark:text-primary-400", children: formatCurrency(product.price, getDefaultCurrency()) }), product.originalPrice && product.originalPrice > product.price && (_jsx(Span, { className: "text-xs text-zinc-400 line-through dark:text-zinc-400", children: formatCurrency(product.originalPrice, getDefaultCurrency()) }))] }), (() => {
|
|
@@ -7,11 +7,15 @@ export interface CustomTabDef {
|
|
|
7
7
|
export interface ProductTabsShellProps {
|
|
8
8
|
descriptionContent?: React.ReactNode;
|
|
9
9
|
specsContent?: React.ReactNode;
|
|
10
|
+
/** W1-34 — grading tab content (PSA/BGS/CGC etc). Pass non-null to surface the tab. */
|
|
11
|
+
gradingContent?: React.ReactNode;
|
|
10
12
|
ingredientsContent?: React.ReactNode;
|
|
11
13
|
howToUseContent?: React.ReactNode;
|
|
12
14
|
reviewsContent?: React.ReactNode;
|
|
15
|
+
/** W1-34 — Q&A tab content. Pass non-null to surface the tab. */
|
|
16
|
+
qaContent?: React.ReactNode;
|
|
13
17
|
/** Additional tabs from custom sections — rendered after standard tabs */
|
|
14
18
|
customTabs?: CustomTabDef[];
|
|
15
19
|
className?: string;
|
|
16
20
|
}
|
|
17
|
-
export declare function ProductTabsShell({ descriptionContent, specsContent, ingredientsContent, howToUseContent, reviewsContent, customTabs, className, }: ProductTabsShellProps): import("react/jsx-runtime").JSX.Element | null;
|
|
21
|
+
export declare function ProductTabsShell({ descriptionContent, specsContent, gradingContent, ingredientsContent, howToUseContent, reviewsContent, qaContent, customTabs, className, }: ProductTabsShellProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -4,17 +4,22 @@ import { useState } from "react";
|
|
|
4
4
|
const STATIC_TABS = [
|
|
5
5
|
{ id: "description", label: "Description" },
|
|
6
6
|
{ id: "specs", label: "Specifications" },
|
|
7
|
+
{ id: "grading", label: "Grading" },
|
|
7
8
|
{ id: "ingredients", label: "Ingredients" },
|
|
8
9
|
{ id: "howToUse", label: "How to Use" },
|
|
9
10
|
{ id: "reviews", label: "Reviews" },
|
|
11
|
+
// W1-34 — Q&A tab; consumers pass `qaContent` to surface it.
|
|
12
|
+
{ id: "qa", label: "Q&A" },
|
|
10
13
|
];
|
|
11
|
-
export function ProductTabsShell({ descriptionContent, specsContent, ingredientsContent, howToUseContent, reviewsContent, customTabs = [], className = "", }) {
|
|
14
|
+
export function ProductTabsShell({ descriptionContent, specsContent, gradingContent, ingredientsContent, howToUseContent, reviewsContent, qaContent, customTabs = [], className = "", }) {
|
|
12
15
|
const staticContentMap = {
|
|
13
16
|
description: descriptionContent,
|
|
14
17
|
specs: specsContent,
|
|
18
|
+
grading: gradingContent,
|
|
15
19
|
ingredients: ingredientsContent,
|
|
16
20
|
howToUse: howToUseContent,
|
|
17
21
|
reviews: reviewsContent,
|
|
22
|
+
qa: qaContent,
|
|
18
23
|
};
|
|
19
24
|
const visibleStatic = STATIC_TABS.filter((t) => staticContentMap[t.id] != null);
|
|
20
25
|
const visibleCustom = customTabs.filter((t) => t.content != null);
|
|
@@ -54,6 +54,10 @@ export { ProductGalleryClient } from "./ProductGalleryClient";
|
|
|
54
54
|
export type { ProductGalleryClientProps } from "./ProductGalleryClient";
|
|
55
55
|
export { ProductTabsShell } from "./ProductTabsShell";
|
|
56
56
|
export type { ProductTabsShellProps, CustomTabDef } from "./ProductTabsShell";
|
|
57
|
+
export { ProductGradingTab } from "./ProductGradingTab";
|
|
58
|
+
export type { ProductGradingTabProps } from "./ProductGradingTab";
|
|
59
|
+
export { ProductCardMetadataSection } from "./ProductCardMetadataSection";
|
|
60
|
+
export type { ProductCardMetadataSectionProps } from "./ProductCardMetadataSection";
|
|
57
61
|
export { CustomFieldsEditor } from "./CustomFieldsEditor";
|
|
58
62
|
export type { CustomFieldsEditorProps } from "./CustomFieldsEditor";
|
|
59
63
|
export { CustomSectionsEditor } from "./CustomSectionsEditor";
|
|
@@ -32,6 +32,9 @@ export { MakeOfferButton } from "./MakeOfferButton";
|
|
|
32
32
|
export { RelatedProducts } from "./RelatedProducts";
|
|
33
33
|
export { ProductGalleryClient } from "./ProductGalleryClient";
|
|
34
34
|
export { ProductTabsShell } from "./ProductTabsShell";
|
|
35
|
+
// W1-34: Product detail missing sections
|
|
36
|
+
export { ProductGradingTab } from "./ProductGradingTab";
|
|
37
|
+
export { ProductCardMetadataSection } from "./ProductCardMetadataSection";
|
|
35
38
|
export { CustomFieldsEditor } from "./CustomFieldsEditor";
|
|
36
39
|
export { CustomSectionsEditor } from "./CustomSectionsEditor";
|
|
37
40
|
export { CustomSectionTabContent } from "./CustomSectionTabContent";
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* Client — useAuthGate() shows LoginRequiredModal for Tier 1 (public) actions
|
|
10
10
|
* Server — checkActionAllowed() enforces the same guards on every server action
|
|
11
11
|
* Admin — ActionPermissionsManager toggles defaultEnabled via siteSettings.actionConfig
|
|
12
|
+
*
|
|
13
|
+
* Label source of truth: `ACTIONS.{RESOURCE}["..."].label` in
|
|
14
|
+
* _internal/shared/actions/action-registry.ts. `ACTION_META` derives its labels
|
|
15
|
+
* via `regLabel()` where a registry counterpart exists; entries with no
|
|
16
|
+
* counterpart keep an inline literal until a registry leaf is added (W1-5).
|
|
12
17
|
*/
|
|
13
18
|
export type ActionVariant = "primary" | "secondary" | "outline" | "ghost" | "danger" | "warning";
|
|
14
19
|
export declare const ACTION_ID: {
|
|
@@ -9,7 +9,26 @@
|
|
|
9
9
|
* Client — useAuthGate() shows LoginRequiredModal for Tier 1 (public) actions
|
|
10
10
|
* Server — checkActionAllowed() enforces the same guards on every server action
|
|
11
11
|
* Admin — ActionPermissionsManager toggles defaultEnabled via siteSettings.actionConfig
|
|
12
|
+
*
|
|
13
|
+
* Label source of truth: `ACTIONS.{RESOURCE}["..."].label` in
|
|
14
|
+
* _internal/shared/actions/action-registry.ts. `ACTION_META` derives its labels
|
|
15
|
+
* via `regLabel()` where a registry counterpart exists; entries with no
|
|
16
|
+
* counterpart keep an inline literal until a registry leaf is added (W1-5).
|
|
17
|
+
*/
|
|
18
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
19
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
// Label-from-registry helper (W1-5)
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
/**
|
|
23
|
+
* Returns the label for `ACTIONS[resource][id]`. The `fallback` is used only
|
|
24
|
+
* when the registry has no leaf yet — orphan IDs should be added to ACTIONS
|
|
25
|
+
* over time. Adding the fallback parameter as required ensures every call
|
|
26
|
+
* site documents what the inline label was at migration time, simplifying
|
|
27
|
+
* the eventual cleanup.
|
|
12
28
|
*/
|
|
29
|
+
function regLabel(resource, id, fallback) {
|
|
30
|
+
return ACTIONS[resource]?.[id]?.label ?? fallback;
|
|
31
|
+
}
|
|
13
32
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
33
|
// § 1 Product / listing actions
|
|
15
34
|
// Used by: detail pages, BulkActionsBar, BuyBar
|
|
@@ -64,49 +83,50 @@ export const ACTION_ID = {
|
|
|
64
83
|
DELETE_ACCOUNT: "delete-account",
|
|
65
84
|
};
|
|
66
85
|
export const ACTION_META = {
|
|
67
|
-
[ACTION_ID.BUY_NOW]: { id: ACTION_ID.BUY_NOW, label: "Buy Now", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to purchase items." },
|
|
68
|
-
[ACTION_ID.ADD_TO_CART]: { id: ACTION_ID.ADD_TO_CART, label: "Add to Cart", variant: "secondary", iconName: "ShoppingCart" },
|
|
69
|
-
[ACTION_ID.ADD_TO_WISHLIST]: { id: ACTION_ID.ADD_TO_WISHLIST, label: "Add to Wishlist", variant: "ghost", iconName: "Heart", requiresAuth: true, authMessage: "You need to be signed in to save items to your wishlist." },
|
|
70
|
-
[ACTION_ID.REMOVE_FROM_WISHLIST]: { id: ACTION_ID.REMOVE_FROM_WISHLIST, label: "Remove from Wishlist", variant: "ghost", iconName: "HeartOff", requiresAuth: true, authMessage: "You need to be signed in to manage your wishlist." },
|
|
71
|
-
[ACTION_ID.MAKE_OFFER]: { id: ACTION_ID.MAKE_OFFER, label: "Make an Offer", variant: "outline", iconName: "Tag", requiresAuth: true, authMessage: "You need to be signed in to make an offer." },
|
|
72
|
-
[ACTION_ID.SHARE]: { id: ACTION_ID.SHARE, label: "Share", variant: "ghost", iconName: "Share2" },
|
|
73
|
-
[ACTION_ID.COMPARE]: { id: ACTION_ID.COMPARE, label: "Compare", variant: "secondary", iconName: "Columns" },
|
|
74
|
-
[ACTION_ID.PLACE_BID]: { id: ACTION_ID.PLACE_BID, label: "Place Bid", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to place a bid." },
|
|
75
|
-
[ACTION_ID.BUY_NOW_AUCTION]: { id: ACTION_ID.BUY_NOW_AUCTION, label: "Buy Now", variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to purchase items." },
|
|
76
|
-
[ACTION_ID.WATCH_AUCTION]: { id: ACTION_ID.WATCH_AUCTION, label: "Watch", variant: "ghost", iconName: "Eye", requiresAuth: true, authMessage: "You need to be signed in to watch auctions." },
|
|
77
|
-
[ACTION_ID.UNWATCH_AUCTION]: { id: ACTION_ID.UNWATCH_AUCTION, label: "Unwatch", variant: "ghost", iconName: "EyeOff", requiresAuth: true, authMessage: "You need to be signed in to manage your watchlist." },
|
|
78
|
-
[ACTION_ID.RESERVE_NOW]: { id: ACTION_ID.RESERVE_NOW, label: "Reserve Now", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to reserve a pre-order." },
|
|
79
|
-
[ACTION_ID.CANCEL_RESERVATION]: { id: ACTION_ID.CANCEL_RESERVATION, label: "Cancel Reservation", variant: "danger", iconName: "X", requiresAuth: true, authMessage: "You need to be signed in to cancel a reservation." },
|
|
86
|
+
[ACTION_ID.BUY_NOW]: { id: ACTION_ID.BUY_NOW, label: regLabel("PRODUCT", "buy-now", "Buy Now"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to purchase items." },
|
|
87
|
+
[ACTION_ID.ADD_TO_CART]: { id: ACTION_ID.ADD_TO_CART, label: regLabel("PRODUCT", "add-to-cart", "Add to Cart"), variant: "secondary", iconName: "ShoppingCart" },
|
|
88
|
+
[ACTION_ID.ADD_TO_WISHLIST]: { id: ACTION_ID.ADD_TO_WISHLIST, label: regLabel("PRODUCT", "add-to-wishlist", "Add to Wishlist"), variant: "ghost", iconName: "Heart", requiresAuth: true, authMessage: "You need to be signed in to save items to your wishlist." },
|
|
89
|
+
[ACTION_ID.REMOVE_FROM_WISHLIST]: { id: ACTION_ID.REMOVE_FROM_WISHLIST, label: regLabel("PRODUCT", "remove-from-wishlist", "Remove from Wishlist"), variant: "ghost", iconName: "HeartOff", requiresAuth: true, authMessage: "You need to be signed in to manage your wishlist." },
|
|
90
|
+
[ACTION_ID.MAKE_OFFER]: { id: ACTION_ID.MAKE_OFFER, label: regLabel("PRODUCT", "make-offer", "Make an Offer"), variant: "outline", iconName: "Tag", requiresAuth: true, authMessage: "You need to be signed in to make an offer." },
|
|
91
|
+
[ACTION_ID.SHARE]: { id: ACTION_ID.SHARE, label: regLabel("PRODUCT", "share", "Share"), variant: "ghost", iconName: "Share2" },
|
|
92
|
+
[ACTION_ID.COMPARE]: { id: ACTION_ID.COMPARE, label: regLabel("PRODUCT", "compare", "Compare"), variant: "secondary", iconName: "Columns" },
|
|
93
|
+
[ACTION_ID.PLACE_BID]: { id: ACTION_ID.PLACE_BID, label: regLabel("AUCTION", "place-bid", "Place Bid"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to place a bid." },
|
|
94
|
+
[ACTION_ID.BUY_NOW_AUCTION]: { id: ACTION_ID.BUY_NOW_AUCTION, label: regLabel("AUCTION", "buy-it-now", "Buy Now"), variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to purchase items." },
|
|
95
|
+
[ACTION_ID.WATCH_AUCTION]: { id: ACTION_ID.WATCH_AUCTION, label: regLabel("AUCTION", "watch", "Watch"), variant: "ghost", iconName: "Eye", requiresAuth: true, authMessage: "You need to be signed in to watch auctions." },
|
|
96
|
+
[ACTION_ID.UNWATCH_AUCTION]: { id: ACTION_ID.UNWATCH_AUCTION, label: regLabel("AUCTION", "unwatch", "Unwatch"), variant: "ghost", iconName: "EyeOff", requiresAuth: true, authMessage: "You need to be signed in to manage your watchlist." },
|
|
97
|
+
[ACTION_ID.RESERVE_NOW]: { id: ACTION_ID.RESERVE_NOW, label: regLabel("PRE_ORDER", "reserve-now", "Reserve Now"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to reserve a pre-order." },
|
|
98
|
+
[ACTION_ID.CANCEL_RESERVATION]: { id: ACTION_ID.CANCEL_RESERVATION, label: regLabel("PRE_ORDER", "cancel-reservation", "Cancel Reservation"), variant: "danger", iconName: "X", requiresAuth: true, authMessage: "You need to be signed in to cancel a reservation." },
|
|
80
99
|
// ── Checkout / navigation CTAs ────────────────────────────────────────────
|
|
81
|
-
[ACTION_ID.CHECKOUT]: { id: ACTION_ID.CHECKOUT, label: "Checkout", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
82
|
-
[ACTION_ID.CONTINUE_TO_VERIFY]: { id: ACTION_ID.CONTINUE_TO_VERIFY, label: "Continue", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
83
|
-
[ACTION_ID.SEND_OTP]: { id: ACTION_ID.SEND_OTP, label: "Send Code", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
84
|
-
[ACTION_ID.VERIFY_OTP]: { id: ACTION_ID.VERIFY_OTP, label: "Verify & Continue", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
85
|
-
[ACTION_ID.PAY_ONLINE]: { id: ACTION_ID.PAY_ONLINE, label: "Pay Online", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
86
|
-
[ACTION_ID.PAY_COD]: { id: ACTION_ID.PAY_COD, label: "Cash on Delivery", variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
87
|
-
[ACTION_ID.APPLY_COUPON]: { id: ACTION_ID.APPLY_COUPON, label: "Apply", variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to apply a coupon." },
|
|
88
|
-
[ACTION_ID.REMOVE_COUPON]: { id: ACTION_ID.REMOVE_COUPON, label: "Remove", variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to manage coupons." },
|
|
89
|
-
[ACTION_ID.ENTER_PRIZE_DRAW]: { id: ACTION_ID.ENTER_PRIZE_DRAW, label: "Enter Draw", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to enter a prize draw." },
|
|
100
|
+
[ACTION_ID.CHECKOUT]: { id: ACTION_ID.CHECKOUT, label: regLabel("CART", "checkout", "Checkout"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
101
|
+
[ACTION_ID.CONTINUE_TO_VERIFY]: { id: ACTION_ID.CONTINUE_TO_VERIFY, label: regLabel("CHECKOUT", "continue-to-verification", "Continue"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
102
|
+
[ACTION_ID.SEND_OTP]: { id: ACTION_ID.SEND_OTP, label: regLabel("CHECKOUT", "send-otp", "Send Code"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
103
|
+
[ACTION_ID.VERIFY_OTP]: { id: ACTION_ID.VERIFY_OTP, label: regLabel("CHECKOUT", "verify-otp", "Verify & Continue"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
104
|
+
[ACTION_ID.PAY_ONLINE]: { id: ACTION_ID.PAY_ONLINE, label: regLabel("CHECKOUT", "pay-online", "Pay Online"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
105
|
+
[ACTION_ID.PAY_COD]: { id: ACTION_ID.PAY_COD, label: regLabel("CHECKOUT", "pay-cod", "Cash on Delivery"), variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
|
|
106
|
+
[ACTION_ID.APPLY_COUPON]: { id: ACTION_ID.APPLY_COUPON, label: regLabel("CHECKOUT", "apply-coupon", "Apply"), variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to apply a coupon." },
|
|
107
|
+
[ACTION_ID.REMOVE_COUPON]: { id: ACTION_ID.REMOVE_COUPON, label: regLabel("CHECKOUT", "remove-coupon", "Remove"), variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to manage coupons." },
|
|
108
|
+
[ACTION_ID.ENTER_PRIZE_DRAW]: { id: ACTION_ID.ENTER_PRIZE_DRAW, label: regLabel("PRIZE_DRAW", "enter-draw", "Enter Draw"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to enter a prize draw." },
|
|
109
|
+
// ── Inline literals: no registry counterpart yet (orphan IDs — add to ACTIONS in a follow-up sweep) ─
|
|
90
110
|
[ACTION_ID.ENTER_RAFFLE]: { id: ACTION_ID.ENTER_RAFFLE, label: "Enter Raffle", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to enter a raffle." },
|
|
91
111
|
[ACTION_ID.REPORT_LISTING]: { id: ACTION_ID.REPORT_LISTING, label: "Report Listing", variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to report a listing." },
|
|
92
|
-
[ACTION_ID.FOLLOW_STORE]: { id: ACTION_ID.FOLLOW_STORE, label: "Follow Store", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to follow a store." },
|
|
112
|
+
[ACTION_ID.FOLLOW_STORE]: { id: ACTION_ID.FOLLOW_STORE, label: regLabel("STORE", "follow", "Follow Store"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to follow a store." },
|
|
93
113
|
// ── Reviews & social ──────────────────────────────────────────────────────
|
|
94
|
-
[ACTION_ID.WRITE_REVIEW]: { id: ACTION_ID.WRITE_REVIEW, label: "Write a Review", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to write a review." },
|
|
114
|
+
[ACTION_ID.WRITE_REVIEW]: { id: ACTION_ID.WRITE_REVIEW, label: regLabel("USER", "write-review", "Write a Review"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to write a review." },
|
|
95
115
|
[ACTION_ID.MESSAGE_SELLER]: { id: ACTION_ID.MESSAGE_SELLER, label: "Message Seller", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to message a seller." },
|
|
96
116
|
// ── Seller CTAs ───────────────────────────────────────────────────────────
|
|
97
117
|
[ACTION_ID.BECOME_SELLER]: { id: ACTION_ID.BECOME_SELLER, label: "Apply as Seller", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to apply as a seller." },
|
|
98
|
-
[ACTION_ID.REQUEST_PAYOUT]: { id: ACTION_ID.REQUEST_PAYOUT, label: "Request Payout", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to request a payout.", requiredPermission: "seller.payouts.request" },
|
|
99
|
-
[ACTION_ID.RESPOND_TO_REVIEW]: { id: ACTION_ID.RESPOND_TO_REVIEW, label: "Respond to Review", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to respond to a review.", requiredPermission: "seller.reviews.respond" },
|
|
118
|
+
[ACTION_ID.REQUEST_PAYOUT]: { id: ACTION_ID.REQUEST_PAYOUT, label: regLabel("STORE", "request-payout", "Request Payout"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to request a payout.", requiredPermission: "seller.payouts.request" },
|
|
119
|
+
[ACTION_ID.RESPOND_TO_REVIEW]: { id: ACTION_ID.RESPOND_TO_REVIEW, label: regLabel("STORE", "reply-review", "Respond to Review"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to respond to a review.", requiredPermission: "seller.reviews.respond" },
|
|
100
120
|
// ── User account actions ──────────────────────────────────────────────────
|
|
101
|
-
[ACTION_ID.CANCEL_ORDER]: { id: ACTION_ID.CANCEL_ORDER, label: "Cancel Order", variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to cancel an order." },
|
|
102
|
-
[ACTION_ID.REQUEST_RETURN]: { id: ACTION_ID.REQUEST_RETURN, label: "Request Return", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to request a return." },
|
|
103
|
-
[ACTION_ID.REORDER]: { id: ACTION_ID.REORDER, label: "Reorder", variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to reorder." },
|
|
104
|
-
[ACTION_ID.TRACK_ORDER]: { id: ACTION_ID.TRACK_ORDER, label: "Track Order", variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to track your order." },
|
|
121
|
+
[ACTION_ID.CANCEL_ORDER]: { id: ACTION_ID.CANCEL_ORDER, label: regLabel("USER", "cancel-order", "Cancel Order"), variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to cancel an order." },
|
|
122
|
+
[ACTION_ID.REQUEST_RETURN]: { id: ACTION_ID.REQUEST_RETURN, label: regLabel("USER", "request-return", "Request Return"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to request a return." },
|
|
123
|
+
[ACTION_ID.REORDER]: { id: ACTION_ID.REORDER, label: regLabel("USER", "reorder", "Reorder"), variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to reorder." },
|
|
124
|
+
[ACTION_ID.TRACK_ORDER]: { id: ACTION_ID.TRACK_ORDER, label: regLabel("USER", "track-order", "Track Order"), variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to track your order." },
|
|
105
125
|
[ACTION_ID.EDIT_PROFILE]: { id: ACTION_ID.EDIT_PROFILE, label: "Edit Profile", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to edit your profile." },
|
|
106
126
|
[ACTION_ID.ADD_ADDRESS]: { id: ACTION_ID.ADD_ADDRESS, label: "Add Address", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to add an address." },
|
|
107
127
|
[ACTION_ID.EDIT_ADDRESS]: { id: ACTION_ID.EDIT_ADDRESS, label: "Edit Address", variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to edit an address." },
|
|
108
|
-
[ACTION_ID.DELETE_ADDRESS]: { id: ACTION_ID.DELETE_ADDRESS, label: "Delete Address", variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to delete an address." },
|
|
109
|
-
[ACTION_ID.CHANGE_PASSWORD]: { id: ACTION_ID.CHANGE_PASSWORD, label: "Change Password", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to change your password." },
|
|
128
|
+
[ACTION_ID.DELETE_ADDRESS]: { id: ACTION_ID.DELETE_ADDRESS, label: regLabel("USER", "delete-address", "Delete Address"), variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to delete an address." },
|
|
129
|
+
[ACTION_ID.CHANGE_PASSWORD]: { id: ACTION_ID.CHANGE_PASSWORD, label: regLabel("USER", "update-password", "Change Password"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to change your password." },
|
|
110
130
|
[ACTION_ID.DELETE_ACCOUNT]: { id: ACTION_ID.DELETE_ACCOUNT, label: "Delete Account", variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to delete your account.", requiredPermission: "user.account.delete" },
|
|
111
131
|
};
|
|
112
132
|
// Detail page action groups — ordered top-to-bottom in the right-hand panel
|