@mohasinac/appkit 4.13.0 → 4.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +14 -5
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +3 -0
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +6 -0
- package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +8 -0
- package/dist/_internal/client/features/lottery/LotteryDetailView.js +4 -1
- package/dist/_internal/client/features/lottery/LotteryListView.d.ts +7 -0
- package/dist/_internal/client/features/lottery/LotteryListView.js +1 -1
- package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +4 -1
- package/dist/_internal/client/features/lottery/slot-collage-items.d.ts +15 -0
- package/dist/_internal/client/features/lottery/slot-collage-items.js +13 -0
- package/dist/_internal/server/features/auctions/actions.js +6 -0
- package/dist/_internal/server/features/bundles/data.d.ts +16 -0
- package/dist/_internal/server/features/bundles/data.js +26 -0
- package/dist/_internal/server/features/bundles/index.d.ts +1 -1
- package/dist/_internal/server/features/bundles/index.js +1 -1
- package/dist/_internal/server/features/checkout/actions.js +6 -0
- package/dist/_internal/server/features/checkout/locked-lines.d.ts +38 -0
- package/dist/_internal/server/features/checkout/locked-lines.js +171 -0
- package/dist/_internal/server/features/homepage/index.d.ts +0 -1
- package/dist/_internal/server/features/homepage/index.js +7 -1
- package/dist/_internal/server/features/lottery/adapters.js +4 -0
- package/dist/_internal/server/features/products/data.js +15 -37
- package/dist/_internal/server/features/products/index.d.ts +1 -1
- package/dist/_internal/server/features/products/index.js +1 -1
- package/dist/_internal/server/features/products/list-public.d.ts +46 -20
- package/dist/_internal/server/features/products/list-public.js +266 -90
- package/dist/_internal/server/features/products/listing-tab-counts.d.ts +44 -0
- package/dist/_internal/server/features/products/listing-tab-counts.js +97 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +51 -0
- package/dist/_internal/server/features/site-settings/adapters.js +75 -0
- package/dist/_internal/server/features/site-settings/index.d.ts +1 -0
- package/dist/_internal/server/features/site-settings/index.js +1 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +56 -0
- package/dist/_internal/server/features/stores/adapters.js +91 -0
- package/dist/_internal/server/features/stores/index.d.ts +1 -0
- package/dist/_internal/server/features/stores/index.js +1 -0
- package/dist/_internal/server/jobs/core/auctionSettlement.js +56 -2
- package/dist/_internal/server/jobs/core/offerExpiry.js +45 -9
- package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.d.ts +5 -0
- package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.js +33 -1
- package/dist/_internal/server/jobs/handlers/messages.d.ts +7 -0
- package/dist/_internal/server/jobs/handlers/messages.js +10 -0
- package/dist/_internal/shared/actions/action-registry.js +15 -1
- package/dist/_internal/shared/checkout/lanes.d.ts +25 -0
- package/dist/_internal/shared/checkout/lanes.js +26 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +27 -0
- package/dist/_internal/shared/features/auctions/config.js +27 -0
- package/dist/_internal/shared/features/blog/schema.d.ts +2 -2
- package/dist/_internal/shared/features/cart/schema.d.ts +12 -12
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
- package/dist/_internal/shared/features/emi/schedule.d.ts +24 -3
- package/dist/_internal/shared/features/emi/schedule.js +20 -5
- package/dist/_internal/shared/features/events/schema.d.ts +2 -2
- package/dist/_internal/shared/features/homepage/section-gate.d.ts +14 -0
- package/dist/_internal/shared/features/homepage/section-gate.js +51 -0
- package/dist/_internal/shared/features/offers/config.d.ts +30 -0
- package/dist/_internal/shared/features/offers/config.js +59 -0
- package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
- package/dist/_internal/shared/features/products/schema.d.ts +32 -32
- package/dist/_internal/shared/features/products/to-product-item.js +6 -0
- package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
- package/dist/_internal/shared/features/site-settings/fees.d.ts +60 -0
- package/dist/_internal/shared/features/site-settings/fees.js +39 -0
- package/dist/_internal/shared/features/site-settings/types.d.ts +40 -0
- package/dist/_internal/shared/features/site-settings/types.js +1 -0
- package/dist/_internal/shared/history/history.d.ts +66 -0
- package/dist/_internal/shared/history/history.js +158 -0
- package/dist/_internal/shared/history/index.d.ts +4 -0
- package/dist/_internal/shared/history/index.js +2 -0
- package/dist/_internal/shared/history/types.d.ts +61 -0
- package/dist/_internal/shared/history/types.js +6 -0
- package/dist/_internal/shared/listing-types/_registry.d.ts +54 -1
- package/dist/_internal/shared/listing-types/_registry.js +67 -0
- package/dist/_internal/shared/listing-types/art/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/art/config.js +5 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +13 -0
- package/dist/_internal/shared/listing-types/auction/config.js +21 -0
- package/dist/_internal/shared/listing-types/availability.d.ts +79 -0
- package/dist/_internal/shared/listing-types/availability.js +104 -0
- package/dist/_internal/shared/listing-types/capabilities.d.ts +15 -0
- package/dist/_internal/shared/listing-types/capabilities.js +12 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/classified/config.js +5 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +11 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +22 -2
- package/dist/_internal/shared/listing-types/live/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/live/config.js +5 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +19 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +11 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +15 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/standard/config.js +5 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +5 -0
- package/dist/client.d.ts +7 -5
- package/dist/client.js +2 -2
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/constants/field-names.d.ts +49 -2
- package/dist/constants/field-names.js +53 -2
- package/dist/constants/table-keys.d.ts +17 -0
- package/dist/constants/table-keys.js +17 -0
- package/dist/errors/error-codes.d.ts +13 -1
- package/dist/errors/error-codes.js +13 -1
- package/dist/errors/error-display-map.js +12 -3
- package/dist/errors/messages.d.ts +10 -2
- package/dist/errors/messages.js +10 -2
- package/dist/features/about/components/AboutView.d.ts +3 -0
- package/dist/features/about/components/AboutView.js +3 -2
- package/dist/features/about/components/PolicyPageView.d.ts +2 -1
- package/dist/features/about/components/PolicyPageView.js +61 -23
- package/dist/features/about/components/PublicProfileView.js +5 -0
- package/dist/features/about/schemas/firestore.d.ts +3 -0
- package/dist/features/account/components/UserOrdersView.js +7 -0
- package/dist/features/account/components/index.d.ts +0 -2
- package/dist/features/account/components/index.js +0 -1
- package/dist/features/account/schemas/index.d.ts +2 -2
- package/dist/features/account/types/index.d.ts +21 -2
- package/dist/features/addresses/schemas/index.d.ts +4 -4
- package/dist/features/admin/components/AdminArtView.js +8 -3
- package/dist/features/admin/components/AdminClassifiedView.js +8 -3
- package/dist/features/admin/components/AdminDigitalCodesView.js +8 -3
- package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -0
- package/dist/features/admin/components/AdminLiveView.js +8 -3
- package/dist/features/admin/components/AdminOffersView.d.ts +4 -0
- package/dist/features/admin/components/AdminOffersView.js +186 -0
- package/dist/features/admin/components/AdminOrdersView.js +10 -1
- package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -0
- package/dist/features/admin/components/AdminProductsView.js +16 -29
- package/dist/features/admin/components/AdminSiteSettingsView.js +31 -13
- package/dist/features/admin/components/AdminSiteView.js +6 -2
- package/dist/features/admin/components/AdminStickersView.js +8 -3
- package/dist/features/admin/components/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +86 -0
- package/dist/features/admin/constants/filter-tabs.js +55 -0
- package/dist/features/admin/schemas/firestore.d.ts +122 -3
- package/dist/features/admin/schemas/firestore.js +1 -0
- package/dist/features/auctions/actions/bid-actions.d.ts +20 -5
- package/dist/features/auctions/actions/bid-actions.js +93 -20
- package/dist/features/auctions/components/AuctionDetailPageView.js +11 -6
- package/dist/features/auctions/components/AuctionsListView.js +5 -6
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +7 -0
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +19 -2
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +8 -4
- package/dist/features/auctions/repository/bid.repository.d.ts +13 -0
- package/dist/features/auctions/repository/bid.repository.js +23 -0
- package/dist/features/auctions/schemas/firestore.d.ts +17 -0
- package/dist/features/auctions/schemas/index.d.ts +12 -12
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +10 -1
- package/dist/features/blog/actions/blog-actions.d.ts +7 -7
- package/dist/features/blog/schemas/index.d.ts +6 -6
- package/dist/features/cart/repository/cart.repository.js +1 -0
- package/dist/features/cart/schemas/firestore.d.ts +15 -0
- package/dist/features/catalogue/schemas/validation.d.ts +4 -4
- package/dist/features/categories/components/BrandDetailPageView.js +23 -40
- package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -7
- package/dist/features/categories/components/BrandDetailTabs.js +8 -12
- package/dist/features/categories/components/BundleCollage.js +1 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +53 -57
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +10 -8
- package/dist/features/categories/components/CategoryDetailTabs.js +11 -16
- package/dist/features/categories/components/CategoryProductsListing.js +11 -1
- package/dist/features/categories/components/ConcernCard.js +7 -1
- package/dist/features/categories/schemas/firestore.d.ts +13 -0
- package/dist/features/categories/schemas/index.d.ts +26 -26
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +16 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.js +23 -3
- package/dist/features/events/components/AdminEventsView.js +15 -11
- package/dist/features/events/components/EventFilters.d.ts +9 -1
- package/dist/features/events/components/EventFilters.js +45 -21
- package/dist/features/events/components/EventsIndexListing.d.ts +6 -1
- package/dist/features/events/components/EventsIndexListing.js +2 -2
- package/dist/features/events/components/EventsListPageView.js +13 -4
- package/dist/features/events/repository/events.repository.d.ts +17 -0
- package/dist/features/events/repository/events.repository.js +27 -0
- package/dist/features/events/schemas/firestore.d.ts +12 -0
- package/dist/features/events/schemas/firestore.js +12 -0
- package/dist/features/events/schemas/index.d.ts +50 -50
- package/dist/features/events/types/index.d.ts +11 -0
- package/dist/features/events/types/index.js +57 -1
- package/dist/features/faq/actions/faq-actions.d.ts +12 -12
- package/dist/features/faq/schemas/index.d.ts +8 -8
- package/dist/features/filters/FilterFacetSection.d.ts +7 -1
- package/dist/features/filters/FilterFacetSection.js +11 -3
- package/dist/features/grouped/schemas/index.d.ts +4 -4
- package/dist/features/homepage/components/AnnouncementBar.d.ts +7 -1
- package/dist/features/homepage/components/AnnouncementBar.js +3 -3
- package/dist/features/homepage/components/MarketplaceHomepageView.js +20 -6
- package/dist/features/homepage/hooks/homepage-query.d.ts +16 -0
- package/dist/features/homepage/hooks/homepage-query.js +45 -0
- package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +10 -0
- package/dist/features/homepage/hooks/useFeaturedAuctions.js +28 -8
- package/dist/features/homepage/hooks/useFeaturedPreOrders.js +17 -8
- package/dist/features/homepage/hooks/useFeaturedProducts.js +15 -8
- package/dist/features/homepage/schemas/index.d.ts +2 -2
- package/dist/features/layout/AppLayoutShell.d.ts +11 -1
- package/dist/features/layout/AppLayoutShell.js +15 -14
- package/dist/features/layout/BackToTop.d.ts +9 -0
- package/dist/features/layout/BackToTop.js +10 -1
- package/dist/features/layout/BottomActions.js +28 -25
- package/dist/features/layout/BottomChrome.d.ts +34 -0
- package/dist/features/layout/BottomChrome.js +55 -0
- package/dist/features/layout/BottomNavLayout.d.ts +6 -0
- package/dist/features/layout/BottomNavLayout.js +7 -1
- package/dist/features/layout/BottomNavbar.js +15 -2
- package/dist/features/layout/FooterLayout.js +11 -1
- package/dist/features/layout/NavbarLayout.js +1 -1
- package/dist/features/layout/TitleBarLayout.d.ts +7 -0
- package/dist/features/layout/TitleBarLayout.js +28 -5
- package/dist/features/layout/index.d.ts +2 -0
- package/dist/features/layout/index.js +1 -0
- package/dist/features/lottery/types/index.d.ts +3 -0
- package/dist/features/media/MediaPickerModal.js +5 -1
- package/dist/features/messages/schemas/index.d.ts +2 -2
- package/dist/features/orders/components/OrderScopeTabs.d.ts +20 -0
- package/dist/features/orders/components/OrderScopeTabs.js +31 -0
- package/dist/features/orders/constants/order-scope.d.ts +48 -0
- package/dist/features/orders/constants/order-scope.js +111 -0
- package/dist/features/orders/repository/orders.repository.d.ts +54 -7
- package/dist/features/orders/repository/orders.repository.js +116 -22
- package/dist/features/orders/schemas/firestore.d.ts +117 -0
- package/dist/features/orders/schemas/firestore.js +30 -0
- package/dist/features/orders/schemas/index.d.ts +18 -18
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +14 -9
- package/dist/features/pre-orders/components/PreOrdersListView.js +6 -5
- package/dist/features/pre-orders/schemas/index.d.ts +1 -1
- package/dist/features/products/actions/product-actions.d.ts +14 -0
- package/dist/features/products/actions/product-actions.js +49 -11
- package/dist/features/products/api/route.d.ts +12 -0
- package/dist/features/products/api/route.js +42 -100
- package/dist/features/products/components/ArtStickersListView.js +2 -2
- package/dist/features/products/components/AuctionsIndexListing.js +13 -11
- package/dist/features/products/components/AvailabilityTabs.d.ts +11 -0
- package/dist/features/products/components/AvailabilityTabs.js +47 -0
- package/dist/features/products/components/MakeOfferButton.js +48 -49
- package/dist/features/products/components/PrizeDrawCollage.d.ts +20 -4
- package/dist/features/products/components/PrizeDrawCollage.js +9 -1
- package/dist/features/products/components/PrizeDrawsIndexListing.js +14 -12
- package/dist/features/products/components/PrizeDrawsListingView.js +2 -2
- package/dist/features/products/components/PrizeDrawsSection.d.ts +7 -1
- package/dist/features/products/components/PrizeDrawsSection.js +23 -18
- package/dist/features/products/components/ProductDetailPageView.d.ts +4 -1
- package/dist/features/products/components/ProductDetailPageView.js +18 -4
- package/dist/features/products/components/ProductForm.js +5 -2
- package/dist/features/products/components/ProductGrid.js +6 -1
- package/dist/features/products/components/ProductsIndexListing.js +22 -32
- package/dist/features/products/components/ProductsIndexPageView.js +6 -9
- package/dist/features/products/components/index.d.ts +2 -2
- package/dist/features/products/components/index.js +1 -1
- package/dist/features/products/config/listing-type-listing-config.d.ts +20 -0
- package/dist/features/products/config/listing-type-listing-config.js +22 -10
- package/dist/features/products/constants/action-defs.d.ts +2 -0
- package/dist/features/products/constants/action-defs.js +6 -0
- package/dist/features/products/hooks/useAvailabilityScope.d.ts +13 -0
- package/dist/features/products/hooks/useAvailabilityScope.js +19 -0
- package/dist/features/products/hooks/useProducts.d.ts +5 -0
- package/dist/features/products/hooks/useProducts.js +13 -2
- package/dist/features/products/repository/products.repository.d.ts +31 -2
- package/dist/features/products/repository/products.repository.js +53 -21
- package/dist/features/products/schemas/firestore.d.ts +17 -3
- package/dist/features/products/schemas/firestore.js +7 -2
- package/dist/features/products/schemas/index.d.ts +66 -66
- package/dist/features/products/schemas/product-features.validators.d.ts +6 -6
- package/dist/features/products/types/index.d.ts +16 -1
- package/dist/features/promotions/schemas/index.d.ts +2 -2
- package/dist/features/reviews/schemas/index.d.ts +16 -16
- package/dist/features/search/schemas/index.d.ts +4 -4
- package/dist/features/seller/actions/offer-actions.js +25 -2
- package/dist/features/seller/components/SellerArtView.js +5 -0
- package/dist/features/seller/components/SellerAuctionsView.js +13 -15
- package/dist/features/seller/components/SellerClassifiedView.js +5 -0
- package/dist/features/seller/components/SellerDigitalCodesView.js +5 -0
- package/dist/features/seller/components/SellerLiveView.js +5 -0
- package/dist/features/seller/components/SellerOrdersView.js +31 -9
- package/dist/features/seller/components/SellerPayoutSettingsView.js +19 -18
- package/dist/features/seller/components/SellerPreOrdersView.js +5 -0
- package/dist/features/seller/components/SellerPrizeDrawsView.js +14 -16
- package/dist/features/seller/components/SellerProductShell.js +7 -1
- package/dist/features/seller/components/SellerProductsView.js +11 -8
- package/dist/features/seller/components/SellerStickersView.js +5 -0
- package/dist/features/seller/components/index.d.ts +0 -2
- package/dist/features/seller/components/index.js +0 -1
- package/dist/features/seller/repository/offer.repository.d.ts +73 -16
- package/dist/features/seller/repository/offer.repository.js +38 -3
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/seller/schemas/offer-forms.d.ts +25 -0
- package/dist/features/seller/schemas/offer-forms.js +21 -0
- package/dist/features/seller/server.d.ts +0 -1
- package/dist/features/seller/server.js +0 -1
- package/dist/features/shell/SectionForm.d.ts +99 -0
- package/dist/features/shell/SectionForm.js +128 -0
- package/dist/features/shell/entity-form.d.ts +109 -0
- package/dist/features/shell/entity-form.js +96 -0
- package/dist/features/shell/index.d.ts +4 -0
- package/dist/features/shell/index.js +2 -0
- package/dist/features/stores/api/[storeSlug]/route.js +2 -25
- package/dist/features/stores/components/StoreAuctionsListing.js +15 -1
- package/dist/features/stores/components/StoreClassifiedsListing.js +12 -2
- package/dist/features/stores/components/StoreDetailLayoutView.js +24 -71
- package/dist/features/stores/components/StoreDigitalCodesListing.js +12 -2
- package/dist/features/stores/components/StoreLiveItemsListing.js +12 -2
- package/dist/features/stores/components/StorePreOrdersListing.js +12 -1
- package/dist/features/stores/components/StoreProductsListing.js +11 -1
- package/dist/features/stores/repository/store.repository.js +6 -0
- package/dist/features/stores/schemas/index.d.ts +6 -6
- package/dist/features/support/schemas/index.d.ts +4 -4
- package/dist/features/tester/actions/checklist-item-actions.d.ts +4 -4
- package/dist/features/tester/seed-data/offers-tester-seed-data.js +119 -3
- package/dist/features/tester/seed-data/products-tester-seed-data.js +131 -1
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +105 -1
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +13 -10
- package/dist/index.js +14 -9
- package/dist/next/routing/route-map.d.ts +7 -1
- package/dist/next/routing/route-map.js +5 -0
- package/dist/providers/db-firebase/base.repository.d.ts +30 -0
- package/dist/providers/db-firebase/base.repository.js +50 -1
- package/dist/providers/db-firebase/index.d.ts +1 -1
- package/dist/providers/db-firebase/index.js +1 -1
- package/dist/providers/db-firebase/sieve.d.ts +25 -0
- package/dist/providers/db-firebase/sieve.js +52 -18
- package/dist/react/hooks/useUrlTable.js +10 -1
- package/dist/schemas/registry.d.ts +68 -68
- package/dist/seed/_helpers/category-forest.d.ts +2 -0
- package/dist/seed/_helpers/category-forest.js +309 -0
- package/dist/seed/_helpers/category-tree.d.ts +27 -0
- package/dist/seed/_helpers/category-tree.js +103 -0
- package/dist/seed/catalogue-seed-data.js +12 -12
- package/dist/seed/categories-seed-data.js +177 -162
- package/dist/seed/events-seed-data.js +61 -10
- package/dist/seed/homepage-sections-seed-data.js +5 -1
- package/dist/seed/offers-seed-data.js +32 -27
- package/dist/seed/products-art-seed-data.js +50 -12
- package/dist/seed/products-auctions-seed-data.js +97 -18
- package/dist/seed/products-classifieds-seed-data.js +80 -16
- package/dist/seed/products-digital-codes-seed-data.js +66 -18
- package/dist/seed/products-live-items-seed-data.js +64 -13
- package/dist/seed/products-preorders-seed-data.d.ts +0 -1
- package/dist/seed/products-preorders-seed-data.js +52 -13
- package/dist/seed/products-prize-draws-seed-data.js +63 -14
- package/dist/seed/products-standard-seed-data.js +99 -26
- package/dist/seed/products-stickers-seed-data.js +47 -12
- package/dist/seed/reviews-seed-data.js +6 -1
- package/dist/seed/site-settings-seed-data.d.ts +1 -38
- package/dist/seed/site-settings-seed-data.js +38 -76
- package/dist/seed/stores-seed-data.js +71 -1
- package/dist/seed/users-seed-data.js +15 -5
- package/dist/server-entry.d.ts +1 -2
- package/dist/server-entry.js +4 -3
- package/dist/server.d.ts +4 -3
- package/dist/server.js +8 -4
- package/dist/styles.css +78 -12
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Button.style.css +19 -4
- package/dist/ui/components/CollapsibleSection.d.ts +17 -1
- package/dist/ui/components/CollapsibleSection.js +6 -2
- package/dist/ui/components/FilterChipGroup.d.ts +25 -1
- package/dist/ui/components/FilterChipGroup.js +21 -4
- package/dist/ui/components/HandMode.style.css +47 -0
- package/dist/ui/components/Layout.d.ts +9 -2
- package/dist/ui/components/Layout.js +1 -1
- package/dist/ui/components/ListingLayout.js +8 -8
- package/dist/ui/components/ListingLayout.style.css +8 -7
- package/dist/ui/components/StickyToolbar.d.ts +3 -1
- package/dist/ui/components/StickyToolbar.js +62 -20
- package/dist/ui/components/bottom-chrome-slot.d.ts +21 -0
- package/dist/ui/components/bottom-chrome-slot.js +29 -0
- package/dist/ui/components/index.style.css +3 -0
- package/dist/ui/forms/FormShell.d.ts +43 -13
- package/dist/ui/forms/FormShell.js +22 -11
- package/dist/ui/forms/index.d.ts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { Div } from "../../ui";
|
|
5
|
+
import { BOTTOM_CHROME_SLOT_ID } from "../../ui/components/bottom-chrome-slot";
|
|
6
|
+
/**
|
|
7
|
+
* BottomChrome — the middle tier of the bottom edge.
|
|
8
|
+
*
|
|
9
|
+
* The bottom edge is three tiers: the nav bar is the floor, `BackToTop` is the
|
|
10
|
+
* ceiling, and *everything else* lives in here. This container is what makes
|
|
11
|
+
* that middle tier a single thing with a single height:
|
|
12
|
+
*
|
|
13
|
+
* --keyboard-inset-height useVisualViewportInset (the on-screen keyboard)
|
|
14
|
+
* --bottom-nav-height BottomNavLayout (the tab bar)
|
|
15
|
+
* --bottom-chrome-height THIS COMPONENT (CTA row, pagination, …)
|
|
16
|
+
* ↑ BackToTop floats above the sum of all three
|
|
17
|
+
*
|
|
18
|
+
* Because it is a flex column, its own `offsetHeight` IS the sum of whatever is
|
|
19
|
+
* inside it — a bar changing height, revealing on scroll, entering bulk mode,
|
|
20
|
+
* or being added years from now all just resize this box, and the one
|
|
21
|
+
* ResizeObserver below picks every case up. Consumers read one variable and
|
|
22
|
+
* never have to learn about individual bars. Publishing a separate var per bar
|
|
23
|
+
* was the alternative, and it fails the moment someone adds bar number three
|
|
24
|
+
* without also editing every consumer's `calc()`.
|
|
25
|
+
*
|
|
26
|
+
* Two constraints this container places on its children:
|
|
27
|
+
* - A child must hide by COLLAPSING, not by transforming. `translate-y-full`
|
|
28
|
+
* leaves layout height behind, so a hidden bar would still be reserved here
|
|
29
|
+
* on every page that has no CTA. See BottomActions' grid-rows collapse.
|
|
30
|
+
* - No `overflow: hidden` here, ever. BottomActions' bulk-picker and info
|
|
31
|
+
* panels are `absolute bottom-full` — deliberately outside the flow (they
|
|
32
|
+
* are transient tap-to-open overlays, not persistent bands, so they are
|
|
33
|
+
* excluded from the published height) — and clipping would erase them.
|
|
34
|
+
*/
|
|
35
|
+
export function BottomChrome({ children }) {
|
|
36
|
+
const ref = useRef(null);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const root = document.documentElement;
|
|
39
|
+
const el = ref.current;
|
|
40
|
+
if (!el)
|
|
41
|
+
return;
|
|
42
|
+
const write = () => root.style.setProperty("--bottom-chrome-height", `${el.offsetHeight}px`);
|
|
43
|
+
const observer = new ResizeObserver(write);
|
|
44
|
+
observer.observe(el);
|
|
45
|
+
write();
|
|
46
|
+
return () => {
|
|
47
|
+
observer.disconnect();
|
|
48
|
+
root.style.setProperty("--bottom-chrome-height", "0px");
|
|
49
|
+
};
|
|
50
|
+
}, []);
|
|
51
|
+
return (_jsxs(Div, { ref: ref, layout: "flex-col",
|
|
52
|
+
// `pointer-events-none` so an empty tier never eats clicks meant for the
|
|
53
|
+
// page underneath; every child re-enables with `pointer-events-auto`.
|
|
54
|
+
className: "fixed left-0 right-0 bottom-[calc(var(--keyboard-inset-height,0px)+var(--bottom-nav-height,4rem))] z-[var(--appkit-z-bottom-nav)] pointer-events-none", children: [_jsx(Div, { id: BOTTOM_CHROME_SLOT_ID, className: "contents" }), children] }));
|
|
55
|
+
}
|
|
@@ -15,5 +15,11 @@ export interface BottomNavLayoutProps {
|
|
|
15
15
|
* `--bottom-nav-height: 4rem` to `:root` (sub-`lg` only — the nav itself is
|
|
16
16
|
* `lg:hidden`). Sticky CTAs read `var(--bottom-nav-height, 0px)` to position
|
|
17
17
|
* themselves above the navbar so they don't get clipped.
|
|
18
|
+
*
|
|
19
|
+
* Hand mode: the item strip carries `appkit-hand-mirror`, so slot order
|
|
20
|
+
* reverses in left-hand mode (CSS-only via `[data-hand]` — applies on the
|
|
21
|
+
* first paint, no flash). This deliberately covers BOTH bottom bars: the
|
|
22
|
+
* public `BottomNavbar` and `DashboardLayoutClient`'s `dashboard-bottom-navbar`
|
|
23
|
+
* both render through here, and they must behave identically.
|
|
18
24
|
*/
|
|
19
25
|
export declare function BottomNavLayout({ ariaLabel, children, id, className, }: BottomNavLayoutProps): React.JSX.Element;
|
|
@@ -13,6 +13,12 @@ import { useVisualViewportInset } from "../../react/hooks/useVisualViewportInset
|
|
|
13
13
|
* `--bottom-nav-height: 4rem` to `:root` (sub-`lg` only — the nav itself is
|
|
14
14
|
* `lg:hidden`). Sticky CTAs read `var(--bottom-nav-height, 0px)` to position
|
|
15
15
|
* themselves above the navbar so they don't get clipped.
|
|
16
|
+
*
|
|
17
|
+
* Hand mode: the item strip carries `appkit-hand-mirror`, so slot order
|
|
18
|
+
* reverses in left-hand mode (CSS-only via `[data-hand]` — applies on the
|
|
19
|
+
* first paint, no flash). This deliberately covers BOTH bottom bars: the
|
|
20
|
+
* public `BottomNavbar` and `DashboardLayoutClient`'s `dashboard-bottom-navbar`
|
|
21
|
+
* both render through here, and they must behave identically.
|
|
16
22
|
*/
|
|
17
23
|
export function BottomNavLayout({ ariaLabel, children, id = "bottom-navbar", className, }) {
|
|
18
24
|
const isKeyboardOpen = useVisualViewportInset();
|
|
@@ -37,5 +43,5 @@ export function BottomNavLayout({ ariaLabel, children, id = "bottom-navbar", cla
|
|
|
37
43
|
root.style.setProperty("--bottom-nav-height", "0px");
|
|
38
44
|
};
|
|
39
45
|
}, [isKeyboardOpen]);
|
|
40
|
-
return (_jsx(Nav, { id: id, "aria-label": ariaLabel, "aria-hidden": isKeyboardOpen, className: `fixed bottom-0 left-0 right-0 lg:hidden z-[var(--appkit-z-bottom-nav)] bg-[color-mix(in_srgb,var(--appkit-color-bg)_90%,transparent)] backdrop-blur-md border-t border-[var(--appkit-color-border)] shadow-2xl pb-[env(safe-area-inset-bottom,0px)] transition-transform duration-200 ease-out ${isKeyboardOpen ? "translate-y-full pointer-events-none" : "translate-y-0"}${className ? ` ${className}` : ""}`, children: _jsx(Ul, { className: "flex items-stretch h-16", children: children }) }));
|
|
46
|
+
return (_jsx(Nav, { id: id, "aria-label": ariaLabel, "aria-hidden": isKeyboardOpen, className: `fixed bottom-0 left-0 right-0 lg:hidden z-[var(--appkit-z-bottom-nav)] bg-[color-mix(in_srgb,var(--appkit-color-bg)_90%,transparent)] backdrop-blur-md border-t border-[var(--appkit-color-border)] shadow-2xl pb-[env(safe-area-inset-bottom,0px)] transition-transform duration-200 ease-out ${isKeyboardOpen ? "translate-y-full pointer-events-none" : "translate-y-0"}${className ? ` ${className}` : ""}`, children: _jsx(Ul, { className: "appkit-hand-mirror flex items-stretch h-16", children: children }) }));
|
|
41
47
|
}
|
|
@@ -6,7 +6,20 @@ import { BottomNavLayout } from "./BottomNavLayout";
|
|
|
6
6
|
import { NavItem } from "./NavItem";
|
|
7
7
|
import { useWishlistCount } from "../wishlist/hooks/useWishlistCount";
|
|
8
8
|
import { useCartCount } from "../cart/hooks/useCartCount";
|
|
9
|
-
|
|
9
|
+
/** Badge counter class for the cart/wishlist slots.
|
|
10
|
+
*
|
|
11
|
+
* Horizontal offset comes from `.appkit-hand-badge` + the `--lg` (0.5rem) size
|
|
12
|
+
* modifier (HandMode.style.css), NOT a `-right-2` utility — the badge hugs the
|
|
13
|
+
* icon's outer top corner, which is top-left in left-hand mode. Do not re-add
|
|
14
|
+
* `-right-2`: Tailwind runs with `important: true`, so the utility would beat
|
|
15
|
+
* the unlayered rule and the badge would silently stop mirroring.
|
|
16
|
+
*
|
|
17
|
+
* Colours use the `-solid`/`-on-solid` overlay pair (Root Cause #67), matching
|
|
18
|
+
* TitleBarLayout's `countBadge`. The previous `bg-[var(--appkit-color-error)]` +
|
|
19
|
+
* `text-[var(--appkit-color-text-on-primary)]` used the theme-INVERTING ink
|
|
20
|
+
* token as a fill, which audit-status-color-pairs can't see through arbitrary
|
|
21
|
+
* -value syntax. */
|
|
22
|
+
const CLS_COUNT_BADGE = "appkit-hand-badge appkit-hand-badge--lg absolute -top-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-error-solid px-[var(--appkit-space-1)] leading-none text-error-on-solid";
|
|
10
23
|
/**
|
|
11
24
|
* BottomNavbar � mobile bottom navigation bar (5 slots: home, shop, search, cart, profile).
|
|
12
25
|
*
|
|
@@ -36,6 +49,6 @@ export function BottomNavbar({ user, userId, homeHref, shopHref, cartHref, wishl
|
|
|
36
49
|
const visibleItems = navItems.slice(0, cartHref ? 3 : 4);
|
|
37
50
|
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [visibleItems.map((item) => (_jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: item.href, label: item.label, icon: item.icon ?? defaultIcon, isActive: pathname === item.href || (item.href !== "/" && pathname.startsWith(item.href + "/")), variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }, item.key))), cartHref && (_jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": cartCount > 0 ? `${labels.cart}, ${cartCount} items` : labels.cart, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), cartCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: CLS_COUNT_BADGE, children: cartCount > 9 ? "9+" : cartCount }))] }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) })), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onMoreToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.more, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h16" }) }), _jsx(Span, { className: labelClassName, children: labels.more })] }) })] }));
|
|
38
51
|
}
|
|
39
|
-
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [_jsx(Li, { className: "flex-1
|
|
52
|
+
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [_jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: homeHref, label: labels.home, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), isActive: pathname === homeHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: shopHref, label: labels.products, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" }) }), isActive: pathname === shopHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onSearchToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.search, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.search })] }) }), _jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": cartCount > 0 ? `${labels.cart}, ${cartCount} items` : labels.cart, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), cartCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: CLS_COUNT_BADGE, children: cartCount > 9 ? "9+" : cartCount }))] }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) }), wishlistHref && (_jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: wishlistHref, variant: "none", className: `${slotClassName} ${pathname === wishlistHref ? activeClassName : inactiveClassName}`, "aria-label": wishlistCount > 0 ? `${labels.wishlist}, ${wishlistCount} items` : labels.wishlist, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4.318 6.318a4.5 4.5 0 0 0 0 6.364L12 20.364l7.682-7.682a4.5 4.5 0 0 0-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 0 0-6.364 0z" }) }), wishlistCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: CLS_COUNT_BADGE, children: wishlistCount > 9 ? "9+" : wishlistCount }))] }), _jsx(Span, { className: labelClassName, children: labels.wishlist })] }) })), _jsx(Li, { className: "flex-1", children: user ? (_jsxs(TextLink, { href: profileHref, variant: "none", className: `${slotClassName} ${pathname === profileHref ? activeClassName : inactiveClassName}`, "aria-label": labels.profile, children: [_jsx(AvatarDisplay, { cropData: user.avatarMetadata ||
|
|
40
53
|
(user.photoURL ? { url: user.photoURL, position: { x: 50, y: 50 }, zoom: 1 } : null), size: "sm", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && (_jsx(Span, { className: `text-[7px] uppercase leading-none ${getRoleBadgeClass ? getRoleBadgeClass(user.role) : "text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]"}`, weight: "semibold", children: user.role }))] })) : (_jsx(NavItem, { href: loginHref, label: labels.profile, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }), isActive: pathname === loginHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName })) })] }));
|
|
41
54
|
}
|
|
@@ -8,7 +8,17 @@ export function FooterLayout({ brandName, brandDescription, socialLinks, linkGro
|
|
|
8
8
|
const [openGroups, setOpenGroups] = useState({});
|
|
9
9
|
const toggleGroup = (idx) => setOpenGroups((prev) => ({ ...prev, [idx]: !prev[idx] }));
|
|
10
10
|
const visibleTrustItems = trustBarItems.filter((item) => item.visible !== false);
|
|
11
|
-
return (_jsxs(BlockFooter, { id: id,
|
|
11
|
+
return (_jsxs(BlockFooter, { id: id,
|
|
12
|
+
// The footer is the last thing on the page, so it is what the fixed bottom
|
|
13
|
+
// chrome actually covers — the copyright and bottom-links rows used to sit
|
|
14
|
+
// under the nav bar and CTA tier, unreadable. <Main> carried a margin for
|
|
15
|
+
// this, but <Main> is a SIBLING before the footer, so that only ever opened
|
|
16
|
+
// a gap between the two and never cleared the page's own end.
|
|
17
|
+
//
|
|
18
|
+
// Padding here rather than on the scroll container so the footer's own
|
|
19
|
+
// background continues behind the translucent chrome — otherwise scrolling
|
|
20
|
+
// to the end shows a band of bare page background under the nav.
|
|
21
|
+
className: "bg-[var(--appkit-color-bg)] border-t border-[var(--appkit-color-border-subtle)] pb-[calc(var(--bottom-nav-height,4rem)+var(--bottom-chrome-height,0px))]", children: [showTrustBar && visibleTrustItems.length > 0 && (_jsx(Div, { border: "subtle", className: "border-b", padding: "y-xl", children: _jsx(Div, { paddingX: "x-page", className: "container mx-auto max-w-[1920px]", children: _jsx(Row, { as: "ul", wrap: true, justify: "center", gap: "lg", className: "lg:gap-[var(--appkit-space-10)]", children: visibleTrustItems.map((item, i) => (_jsx(Li, { children: _jsxs(Row, { textSize: "sm", gap: "sm", className: "min-w-[160px]", children: [_jsx(Span, { className: "flex-shrink-0 text-primary-600 dark:text-secondary-400 w-5 h-5", children: item.icon }), _jsxs(Span, { children: [_jsx(Span, { size: "sm", weight: "bold", className: "block", color: "primary", children: item.label }), item.subtitle && (_jsx(Span, { size: "xs", className: "block", color: "muted", weight: "medium", children: item.subtitle }))] })] }) }, item.id ?? String(i)))) }) }) })), _jsxs(Div, { paddingX: "x-page", className: "container mx-auto max-w-[1920px] lg:py-[var(--appkit-space-14)]", padding: "y-2xl", children: [_jsxs(Div, { className: "grid grid-cols-1 lg:grid-cols-5 gap-[var(--appkit-space-10)] lg:gap-[var(--appkit-space-16)]", children: [_jsxs(Stack, { className: "lg:col-span-2", gap: "md", children: [_jsx(Heading, { level: 5, size: "xl", weight: "bold", color: "primary", children: brandName }), _jsx(Text, { className: "leading-relaxed max-w-sm", color: "muted", size: "sm", weight: "medium", children: brandDescription }), socialLinks.length > 0 && (_jsx(Row, { as: Ul, gap: "sm", children: socialLinks.map((link) => (_jsx(Li, { children: _jsx(Link, { href: link.href, "aria-label": link.ariaLabel, target: "_blank", rel: "noopener noreferrer", className: "flex items-center justify-center w-9 h-9 rounded-full border border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:text-primary dark:hover:text-secondary hover:border-primary/40 dark:hover:border-secondary/40 transition-colors", children: link.icon }) }, link.platform))) })), newsletterEnabled && newsletterSlot && (_jsx(Div, { padding: "t-xs", children: newsletterSlot }))] }), _jsx(Div, { className: `lg:col-span-3 grid grid-cols-1 gap-[var(--appkit-space-0)] lg:gap-[var(--appkit-space-8)] ${linkGroups.length <= 3 ? "lg:grid-cols-3" :
|
|
12
22
|
linkGroups.length === 4 ? "lg:grid-cols-4" :
|
|
13
23
|
"lg:grid-cols-5"}`, children: linkGroups.map((group, idx) => (_jsxs(Div, { children: [_jsxs(Button, { type: "button", variant: "ghost", onClick: () => toggleGroup(idx), className: "w-full justify-between border-b py-[var(--appkit-space-3)] text-left text-[length:var(--appkit-text-sm)] font-semibold transition-colors border-[var(--appkit-color-border-subtle)] text-[var(--appkit-color-text)] hover:text-primary dark:hover:text-secondary lg:hidden", "aria-expanded": !!openGroups[idx], children: [group.heading, _jsx(ChevronDown, { className: `w-4 h-4 transition-transform duration-200 ${openGroups[idx] ? "rotate-180" : ""}` })] }), _jsx(Ul, { className: `lg:hidden overflow-hidden transition-all duration-200 ${openGroups[idx] ? "max-h-96 py-[var(--appkit-space-3)]" : "max-h-0"} space-y-2.5`, children: group.links.map((link) => (_jsx(Li, { children: _jsx(Link, { href: link.href, className: "text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-text-muted)] hover:text-primary dark:hover:text-secondary transition-colors", children: link.label }) }, link.href))) }), _jsxs(Div, { className: "hidden lg:block", children: [_jsx(Text, { className: "tracking-wider mb-3", color: "muted", size: "xs", weight: "bold", transform: "uppercase", children: group.heading }), _jsx(Ul, { className: "space-y-2.5", children: group.links.map((link) => (_jsx(Li, { children: _jsx(Link, { href: link.href, className: "text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-text-muted)] hover:text-primary dark:hover:text-secondary transition-colors", children: link.label }) }, link.href))) })] })] }, idx))) })] }), _jsxs(Stack, { direction: "sm-row", color: "muted", textSize: "xs", justify: "between", border: "default", className: "mt-10 border-t border-[var(--appkit-color-border-subtle)] text-[var(--appkit-color-text-muted)]", padding: "t-lg", align: "center", gap: "3", children: [_jsx(Text, { size: "xs", color: "muted", weight: "medium", children: copyrightText }), bottomLinks.length > 0 && (_jsx(Nav, { "aria-label": "Site utilities", children: _jsx(Ul, { className: "flex flex-wrap items-center gap-x-4 gap-y-1 justify-center", children: bottomLinks.map((link) => (_jsx(Li, { children: _jsx(Link, { href: link.href, className: "text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] hover:text-primary dark:hover:text-secondary transition-colors underline-offset-2 hover:underline", children: link.label }) }, link.href))) }) })), madeInText && (_jsx(Text, { size: "xs", color: "muted", weight: "medium", children: madeInText }))] })] })] }));
|
|
14
24
|
}
|
|
@@ -51,5 +51,5 @@ export function NavbarLayout({ items, activeHref, id = "main-navbar", ariaLabel
|
|
|
51
51
|
if (inline) {
|
|
52
52
|
return (_jsx(Ul, { "aria-label": ariaLabel, className: "hidden xl:flex items-center justify-center gap-[var(--appkit-space-0-5)] xl:gap-[var(--appkit-space-1)] w-full", children: items.map((item) => (_jsx(Li, { children: renderItem ? (renderItem(item, activeHref === item.href)) : (_jsx(DefaultNavItem, { item: item, isActive: activeHref === item.href })) }, item.href))) }));
|
|
53
53
|
}
|
|
54
|
-
return (_jsx(Nav, { border: "subtle", id: id, "aria-label": ariaLabel, className: "hidden lg:block bg-[var(--appkit-color-bg)]/95 backdrop-blur-md border-b", children: _jsxs(Row, { className: "container mx-auto sm:px-[var(--appkit-space-6)] lg:px-[var(--appkit-space-8)] max-w-[1920px] h-10", padding: "x-md", align: "center", children: [_jsxs(Div, { className: `relative flex-1 ${__O.hidden}`, children: [canScrollLeft && (_jsx(NavbarChevron, { direction: "left", onClick: () => scroll("left") })), _jsx(Ul, { ref: scrollRef, className: "flex items-center justify-center gap-[var(--appkit-space-0-5)] lg:gap-[var(--appkit-space-1)] overflow-x-auto [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]", children: items.map((item) => (_jsx(Li, { className: "shrink-0", children: renderItem ? (renderItem(item, activeHref === item.href)) : (_jsx(DefaultNavItem, { item: item, isActive: activeHref === item.href })) }, item.href))) }), canScrollRight && (_jsx(NavbarChevron, { direction: "right", onClick: () => scroll("right") }))] }), rightSlot && _jsx(Div, { className: "shrink-0
|
|
54
|
+
return (_jsx(Nav, { border: "subtle", id: id, "aria-label": ariaLabel, className: "hidden lg:block bg-[var(--appkit-color-bg)]/95 backdrop-blur-md border-b", children: _jsxs(Row, { className: "container mx-auto sm:px-[var(--appkit-space-6)] lg:px-[var(--appkit-space-8)] max-w-[1920px] h-10", padding: "x-md", align: "center", reverse: "hand", children: [_jsxs(Div, { className: `relative flex-1 ${__O.hidden}`, children: [canScrollLeft && (_jsx(NavbarChevron, { direction: "left", onClick: () => scroll("left") })), _jsx(Ul, { ref: scrollRef, className: "flex items-center justify-center gap-[var(--appkit-space-0-5)] lg:gap-[var(--appkit-space-1)] overflow-x-auto [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]", children: items.map((item) => (_jsx(Li, { className: "shrink-0", children: renderItem ? (renderItem(item, activeHref === item.href)) : (_jsx(DefaultNavItem, { item: item, isActive: activeHref === item.href })) }, item.href))) }), canScrollRight && (_jsx(NavbarChevron, { direction: "right", onClick: () => scroll("right") }))] }), rightSlot && _jsx(Div, { className: "shrink-0", children: rightSlot })] }) }));
|
|
55
55
|
}
|
|
@@ -64,6 +64,13 @@ export interface TitleBarLayoutProps {
|
|
|
64
64
|
* TB1 (h-14, all screens): wordmark (+ mark on mobile) | centred mark (md+) | [secondary actions lg+] | search | deals | theme | hamburger
|
|
65
65
|
* TB2 (h-10, below lg only): wishlist | cart | profile — mirrors what TB1 hides below lg
|
|
66
66
|
*
|
|
67
|
+
* Hand mode: both rows carry `reverse="hand"`, so in left-hand mode the whole
|
|
68
|
+
* bar mirrors — wordmark to the right edge, action cluster (hamburger
|
|
69
|
+
* outermost) to the left. Purely CSS via `[data-hand]`, so it lands on the
|
|
70
|
+
* first paint and this file stays server-safe (no `useHandMode()`, no
|
|
71
|
+
* `"use client"`). The centred mark and the brand lockup deliberately do not
|
|
72
|
+
* flip; see their inline notes.
|
|
73
|
+
*
|
|
67
74
|
* Receives all domain data as props — zero domain imports.
|
|
68
75
|
*/
|
|
69
76
|
export declare function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, searchOpen, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, compareHref, wishlistHref, wishlistCount, cartHref, cartCount, profileHref, unreadNotificationCount, notificationsHref, loginHref, registerHref, user, notificationSlot, devSlot, navSlot, promoStripText, isDark, onToggleTheme, onTourStart, hasDashboardNav, onToggleDashboardNav, hideSidebarToggle, id, className, }: TitleBarLayoutProps): React.JSX.Element;
|
|
@@ -3,8 +3,14 @@ import Link from "next/link";
|
|
|
3
3
|
import { Avatar, BlockHeader, Button, Div, Row, Section, SiteLogo, SiteMark, Span } from "../../ui";
|
|
4
4
|
/** Shared icon-button class for all action buttons in the title bar. */
|
|
5
5
|
const iconBtn = "flex items-center justify-center w-9 h-9 rounded-lg text-[var(--appkit-color-text-muted)] hover:bg-primary-50 hover:text-primary-700 dark:hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-secondary-400 transition-colors";
|
|
6
|
-
/** Badge counter class for wishlist/cart counts.
|
|
7
|
-
|
|
6
|
+
/** Badge counter class for wishlist/cart counts.
|
|
7
|
+
*
|
|
8
|
+
* The horizontal offset lives in `.appkit-hand-badge` (HandMode.style.css), NOT
|
|
9
|
+
* in a `-right-*` utility — the badge hugs the icon's outer top corner, which
|
|
10
|
+
* is top-left in left-hand mode. Do not re-add `-right-0.5`: Tailwind runs with
|
|
11
|
+
* `important: true`, so the utility would beat the unlayered rule and the badge
|
|
12
|
+
* would silently stop mirroring. The vertical `-top-0.5` is hand-neutral and stays. */
|
|
13
|
+
const countBadge = "appkit-hand-badge absolute -top-0.5 flex items-center justify-center w-4 h-4 rounded-full bg-error-solid text-error-on-solid text-[10px] font-bold leading-none";
|
|
8
14
|
/**
|
|
9
15
|
* TitleBarLayout — generic top sticky title-bar shell.
|
|
10
16
|
*
|
|
@@ -12,6 +18,13 @@ const countBadge = "absolute -top-0.5 -right-0.5 flex items-center justify-cente
|
|
|
12
18
|
* TB1 (h-14, all screens): wordmark (+ mark on mobile) | centred mark (md+) | [secondary actions lg+] | search | deals | theme | hamburger
|
|
13
19
|
* TB2 (h-10, below lg only): wishlist | cart | profile — mirrors what TB1 hides below lg
|
|
14
20
|
*
|
|
21
|
+
* Hand mode: both rows carry `reverse="hand"`, so in left-hand mode the whole
|
|
22
|
+
* bar mirrors — wordmark to the right edge, action cluster (hamburger
|
|
23
|
+
* outermost) to the left. Purely CSS via `[data-hand]`, so it lands on the
|
|
24
|
+
* first paint and this file stays server-safe (no `useHandMode()`, no
|
|
25
|
+
* `"use client"`). The centred mark and the brand lockup deliberately do not
|
|
26
|
+
* flip; see their inline notes.
|
|
27
|
+
*
|
|
15
28
|
* Receives all domain data as props — zero domain imports.
|
|
16
29
|
*/
|
|
17
30
|
export function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, searchOpen, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, compareHref, wishlistHref, wishlistCount = 0, cartHref, cartCount = 0, profileHref, unreadNotificationCount = 0, notificationsHref, loginHref, registerHref, user, notificationSlot, devSlot, navSlot, promoStripText, isDark = false, onToggleTheme, onTourStart, hasDashboardNav, onToggleDashboardNav, hideSidebarToggle = false, id = "titlebar", className = "", }) {
|
|
@@ -30,10 +43,20 @@ export function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, s
|
|
|
30
43
|
const wishlistEl = wishlistHref ? (_jsxs(Link, { href: wishlistHref, "aria-label": `Wishlist${wishlistCount > 0 ? `, ${wishlistCount} items` : ""}`, className: `relative ${iconBtn}`, "data-tour": "nav-wishlist", children: [_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4.318 6.318a4.5 4.5 0 0 0 0 6.364L12 20.364l7.682-7.682a4.5 4.5 0 0 0-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 0 0-6.364 0z" }) }), wishlistCount > 0 && (_jsx(Span, { className: countBadge, children: wishlistCount > 9 ? "9+" : wishlistCount }))] })) : null;
|
|
31
44
|
const cartEl = cartHref ? (_jsxs(Link, { href: cartHref, "aria-label": `Cart${cartCount > 0 ? `, ${cartCount} items` : ""}`, className: `relative ${iconBtn}`, "data-tour": "nav-cart", children: [_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 0 0-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 0 0-16.536-1.84M7.5 14.25 5.106 5.272M6 20.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm12.75 0a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0z" }) }), cartCount > 0 && (_jsx(Span, { className: countBadge, children: cartCount > 9 ? "9+" : cartCount }))] })) : null;
|
|
32
45
|
const profileEl = profileHref ? (_jsx(Link, { href: profileHref, "aria-label": user ? `Profile — ${user.displayName ?? user.email}` : "Sign in", className: `relative ${iconBtn}`, "data-tour": "nav-profile", children: _jsx(Avatar, { src: user?.photoURL ?? undefined, name: user?.displayName ?? user?.email ?? undefined, alt: user?.displayName ?? "Profile", size: "sm" }) })) : null;
|
|
33
|
-
const authButtonsEl = !user && (loginHref || registerHref) ? (
|
|
46
|
+
const authButtonsEl = !user && (loginHref || registerHref) ? (
|
|
47
|
+
// Mirrors with TB1 so Register (the primary CTA) keeps the outer, more
|
|
48
|
+
// reachable edge instead of ending up tucked inside after the flip.
|
|
49
|
+
_jsxs(Row, { gap: "xs", reverse: "hand", className: "hidden lg:flex", children: [loginHref && (_jsx(Link, { href: loginHref, className: "px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-text-muted)] hover:text-primary-700 dark:hover:text-secondary-400 transition-colors rounded-lg hover:bg-primary-50 dark:hover:bg-[var(--appkit-color-surface-elevated)]", children: "Sign in" })), registerHref && (_jsx(Link, { href: registerHref, className: "px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] font-semibold rounded-lg bg-primary text-white hover:bg-primary/90 transition-colors btn-glow shadow-sm", children: "Register" }))] })) : null;
|
|
34
50
|
const hasTb2 = !!(notificationsEl || wishlistEl || cartEl || profileEl);
|
|
35
51
|
// ── Render ───────────────────────────────────────────────────────────────────
|
|
36
|
-
return (_jsxs(BlockHeader, { id: id, className: `sticky top-0 z-50 bg-[var(--appkit-color-bg)]/95 backdrop-blur-md border-b border-[var(--appkit-color-border-subtle)] shadow-sm ${className}`, children: [promoStripText && (_jsx(Section, { color: "inverse", tone: "accent-banner", className: "text-[length:var(--appkit-text-xs)] text-center font-medium", padding: "y-2xs", children: promoStripText })), _jsxs(Div, { paddingX: "x-page", className: "container mx-auto max-w-[1920px]", children: [_jsxs(Row, { justify: "between", gap: "none", className: "relative h-14", children: [_jsx(Row, { gap: "3", children: _jsxs(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center gap-[var(--appkit-space-2)] transition-opacity hover:opacity-80", children: [_jsx(Div, { className: "flex md:hidden", children: _jsx(SiteMark, { title: brandShortName ?? brandName, size: "sm" }) }), _jsx(SiteLogo, { title: brandName, size: "md" })] }) }), _jsx(Row, { className: "hidden md:flex absolute inset-y-0 left-1/2 -translate-x-1/2", align: "center", children: _jsx(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center transition-opacity hover:opacity-80", children: _jsx(SiteMark, { src: siteLogoUrl, title: brandName, size: "lg" }) }) }), _jsxs(Row, { gap: "xs", children: [devSlot, navSlot, compareEl, notificationSlot, notificationsEl && _jsx(Div, { className: "hidden lg:flex", children: notificationsEl }), wishlistEl && _jsx(Div, { className: "hidden lg:flex", children: wishlistEl }), cartEl && _jsx(Div, { className: "hidden lg:flex", children: cartEl }), user
|
|
52
|
+
return (_jsxs(BlockHeader, { id: id, className: `sticky top-0 z-50 bg-[var(--appkit-color-bg)]/95 backdrop-blur-md border-b border-[var(--appkit-color-border-subtle)] shadow-sm ${className}`, children: [promoStripText && (_jsx(Section, { color: "inverse", tone: "accent-banner", className: "text-[length:var(--appkit-text-xs)] text-center font-medium", padding: "y-2xs", children: promoStripText })), _jsxs(Div, { paddingX: "x-page", className: "container mx-auto max-w-[1920px]", children: [_jsxs(Row, { justify: "between", gap: "none", reverse: "hand", className: "relative h-14", children: [_jsx(Row, { gap: "3", children: _jsxs(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center gap-[var(--appkit-space-2)] transition-opacity hover:opacity-80", children: [_jsx(Div, { className: "flex md:hidden", children: _jsx(SiteMark, { title: brandShortName ?? brandName, size: "sm" }) }), _jsx(SiteLogo, { title: brandName, size: "md" })] }) }), _jsx(Row, { className: "hidden md:flex absolute inset-y-0 left-1/2 -translate-x-1/2", align: "center", children: _jsx(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center transition-opacity hover:opacity-80", children: _jsx(SiteMark, { src: siteLogoUrl, title: brandName, size: "lg" }) }) }), _jsxs(Row, { gap: "xs", reverse: "hand", children: [devSlot, navSlot, compareEl, notificationSlot, notificationsEl && _jsx(Div, { className: "hidden lg:flex", children: notificationsEl }), wishlistEl && _jsx(Div, { className: "hidden lg:flex", children: wishlistEl }), cartEl && _jsx(Div, { className: "hidden lg:flex", children: cartEl }), user
|
|
37
53
|
? profileEl && _jsx(Div, { className: "hidden lg:flex", children: profileEl })
|
|
38
|
-
: authButtonsEl ?? (profileEl && _jsx(Div, { className: "hidden lg:flex", children: profileEl })), searchBtn, promotionsEl, tourBtn, themeBtn, hamburgerBtn] })] }), hasTb2 && (
|
|
54
|
+
: authButtonsEl ?? (profileEl && _jsx(Div, { className: "hidden lg:flex", children: profileEl })), searchBtn, promotionsEl, tourBtn, themeBtn, hamburgerBtn] })] }), hasTb2 && (
|
|
55
|
+
// `justify="end"` is CORRECT alongside `reverse="hand"` — do not
|
|
56
|
+
// "fix" it to "start". `row-reverse` inverts the main axis, so
|
|
57
|
+
// main-end IS the left edge: flex-end already packs this row left in
|
|
58
|
+
// left-hand mode, in mirrored order (profile leftmost). Adding a raw
|
|
59
|
+
// `justify-start` className would also trip audit-inline-styles'
|
|
60
|
+
// RAW_JUSTIFY_ON_ROW.
|
|
61
|
+
_jsxs(Row, { border: "subtle", as: "nav", "aria-label": "Account actions", justify: "end", gap: "xs", reverse: "hand", className: "flex lg:hidden h-10 border-t px-[var(--appkit-space-1)]", children: [notificationsEl, wishlistEl, cartEl, profileEl] }))] })] }));
|
|
39
62
|
}
|
|
@@ -31,6 +31,8 @@ export type { BottomAction, BottomBulkConfig, BottomActionsState, BottomActionsD
|
|
|
31
31
|
export { useBottomActions } from "./hooks/useBottomActions";
|
|
32
32
|
export type { UseBottomActionsOptions } from "./hooks/useBottomActions";
|
|
33
33
|
export { default as BottomActions } from "./BottomActions";
|
|
34
|
+
export { BottomChrome } from "./BottomChrome";
|
|
35
|
+
export type { BottomChromeProps } from "./BottomChrome";
|
|
34
36
|
export { BottomNavbar } from "./BottomNavbar";
|
|
35
37
|
export type { BottomNavbarProps } from "./BottomNavbar";
|
|
36
38
|
export { MainNavbar } from "./MainNavbar";
|
|
@@ -17,6 +17,7 @@ export { AppLayoutShell } from "./AppLayoutShell";
|
|
|
17
17
|
export { BottomActionsProvider, useBottomActionsContext, } from "./BottomActionsContext";
|
|
18
18
|
export { useBottomActions } from "./hooks/useBottomActions";
|
|
19
19
|
export { default as BottomActions } from "./BottomActions";
|
|
20
|
+
export { BottomChrome } from "./BottomChrome";
|
|
20
21
|
export { BottomNavbar } from "./BottomNavbar";
|
|
21
22
|
export { MainNavbar } from "./MainNavbar";
|
|
22
23
|
export { TitleBar } from "./TitleBar";
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
export interface LotterySlot {
|
|
8
8
|
slotNumber: number;
|
|
9
9
|
name: string;
|
|
10
|
+
/** Prize photo. Public — rendered in the prize collage on the lottery detail page. */
|
|
11
|
+
image?: string;
|
|
10
12
|
price: number;
|
|
11
13
|
weight: number;
|
|
12
14
|
isBooked: boolean;
|
|
@@ -18,6 +20,7 @@ export interface LotterySlot {
|
|
|
18
20
|
export interface ClientLotterySlot {
|
|
19
21
|
slotNumber: number;
|
|
20
22
|
name: string;
|
|
23
|
+
image?: string;
|
|
21
24
|
isBooked: boolean;
|
|
22
25
|
bookedByUserLotteryNumber?: number;
|
|
23
26
|
bookedByDisplayName?: string;
|
|
@@ -126,7 +126,11 @@ export function MediaPickerModal({ isOpen, onClose, onSelect, accept = "image/*"
|
|
|
126
126
|
const isImage = (f.contentType ?? "").startsWith("image/");
|
|
127
127
|
const isSelected = selectedExisting === f.downloadURL;
|
|
128
128
|
return (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setSelectedExisting(f.downloadURL), className: [
|
|
129
|
-
|
|
129
|
+
// `flex-col items-stretch` — without it the thumbnail and the
|
|
130
|
+
// filename lay out as row siblings inside `.appkit-button__content`
|
|
131
|
+
// and the square tile shrinks to a fraction of its width
|
|
132
|
+
// (Root Cause #68).
|
|
133
|
+
"flex-col items-stretch rounded-lg border overflow-hidden text-left transition p-[var(--appkit-space-0)] h-auto",
|
|
130
134
|
isSelected
|
|
131
135
|
? "border-primary-500 ring-2 ring-primary-300"
|
|
132
136
|
: "border-[var(--appkit-color-border)]",
|
|
@@ -116,8 +116,8 @@ export declare const conversationFirestoreSchema: z.ZodObject<{
|
|
|
116
116
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
117
117
|
}, z.ZodTypeAny, "passthrough">;
|
|
118
118
|
storeId: string;
|
|
119
|
-
storeName: string;
|
|
120
119
|
buyerId: string;
|
|
120
|
+
storeName: string;
|
|
121
121
|
lastMessageAt: string | Date | z.objectOutputType<{
|
|
122
122
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
123
123
|
}, z.ZodTypeAny, "passthrough">;
|
|
@@ -149,8 +149,8 @@ export declare const conversationFirestoreSchema: z.ZodObject<{
|
|
|
149
149
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
150
150
|
}, z.ZodTypeAny, "passthrough">;
|
|
151
151
|
storeId: string;
|
|
152
|
-
storeName: string;
|
|
153
152
|
buyerId: string;
|
|
153
|
+
storeName: string;
|
|
154
154
|
lastMessageAt: string | Date | z.objectInputType<{
|
|
155
155
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
156
156
|
}, z.ZodTypeAny, "passthrough">;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type OrderScope } from "../constants/order-scope";
|
|
3
|
+
export declare function OrderScopeTabs({ className }: {
|
|
4
|
+
className?: string;
|
|
5
|
+
}): React.JSX.Element;
|
|
6
|
+
export interface OrderScopeState {
|
|
7
|
+
scope: OrderScope;
|
|
8
|
+
/** Spread into `ListingViewConfig.buildExtraParams`'s return value. */
|
|
9
|
+
extraParams: {
|
|
10
|
+
orderScope: OrderScope;
|
|
11
|
+
};
|
|
12
|
+
/** Assign to `ListingViewConfig.renderAboveContent`. */
|
|
13
|
+
renderAboveContent: () => React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Same seam as `useAvailabilityScope`: `buildExtraParams`, not `filterKeys`.
|
|
17
|
+
* Registering the scope as a filter key would inflate the filter-drawer badge
|
|
18
|
+
* for a tab selection and let the drawer's Clear button silently reset it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useOrderScope(): OrderScopeState;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Tabs, TabsList, TabsTrigger } from "../../../ui";
|
|
4
|
+
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
|
+
import { TABLE_KEYS } from "../../../constants/table-keys";
|
|
6
|
+
import { ORDER_SCOPE_TABS, ORDER_SCOPE_VALUES, isOrderScope, } from "../constants/order-scope";
|
|
7
|
+
export function OrderScopeTabs({ className }) {
|
|
8
|
+
const table = useUrlTable();
|
|
9
|
+
const raw = table.get(TABLE_KEYS.ORDER_SCOPE);
|
|
10
|
+
const active = isOrderScope(raw) ? raw : ORDER_SCOPE_VALUES.ACTIVE;
|
|
11
|
+
return (_jsx(Tabs, { value: active,
|
|
12
|
+
// A single `set` — `useUrlTable.set` already resets the page, and a
|
|
13
|
+
// follow-up `setPage` would read stale searchParams and overwrite this
|
|
14
|
+
// update (Root Cause #13).
|
|
15
|
+
onChange: (next) => table.set(TABLE_KEYS.ORDER_SCOPE, next), className: className, children: _jsx(TabsList, { children: ORDER_SCOPE_TABS.map((tab) => (_jsx(TabsTrigger, { value: tab.id, children: tab.label }, tab.id))) }) }));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Same seam as `useAvailabilityScope`: `buildExtraParams`, not `filterKeys`.
|
|
19
|
+
* Registering the scope as a filter key would inflate the filter-drawer badge
|
|
20
|
+
* for a tab selection and let the drawer's Clear button silently reset it.
|
|
21
|
+
*/
|
|
22
|
+
export function useOrderScope() {
|
|
23
|
+
const table = useUrlTable();
|
|
24
|
+
const raw = table.get(TABLE_KEYS.ORDER_SCOPE);
|
|
25
|
+
const scope = isOrderScope(raw) ? raw : ORDER_SCOPE_VALUES.ACTIVE;
|
|
26
|
+
return {
|
|
27
|
+
scope,
|
|
28
|
+
extraParams: { orderScope: scope },
|
|
29
|
+
renderAboveContent: () => _jsx(OrderScopeTabs, {}),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { OrderStatus } from "../types/index";
|
|
2
|
+
import { OrderStatusValues } from "../schemas/firestore";
|
|
3
|
+
export declare const ORDER_SCOPE_VALUES: {
|
|
4
|
+
/** Still moving — someone (buyer, seller or admin) has something to do. */
|
|
5
|
+
readonly ACTIVE: "active";
|
|
6
|
+
/** Terminal — delivered, cancelled, refunded or returned. */
|
|
7
|
+
readonly CLOSED: "closed";
|
|
8
|
+
/** No lifecycle predicate. */
|
|
9
|
+
readonly ALL: "all";
|
|
10
|
+
};
|
|
11
|
+
export type OrderScope = (typeof ORDER_SCOPE_VALUES)[keyof typeof ORDER_SCOPE_VALUES];
|
|
12
|
+
export declare function isOrderScope(value: string): value is OrderScope;
|
|
13
|
+
/**
|
|
14
|
+
* The statuses a scope covers, or `undefined` for "all" (no filter).
|
|
15
|
+
*
|
|
16
|
+
* Emitted as a pipe-joined OR-group, which the Firebase Sieve adapter upgrades
|
|
17
|
+
* to a single Firestore "in" query — no fan-out, and it reuses the existing
|
|
18
|
+
* (status, createdAt) composite index.
|
|
19
|
+
*/
|
|
20
|
+
export declare function statusesForScope(scope: OrderScope): OrderStatus[] | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Tab definitions for the scope bar.
|
|
23
|
+
*
|
|
24
|
+
* NOT registered in `audit-filter-tab-enums.mjs`'s REGISTRY, and deliberately
|
|
25
|
+
* so — the same reasoning its header gives for `ADMIN_ORDER_PAYMENT_REVIEW_TABS`
|
|
26
|
+
* and `ADMIN_CART_OWNERSHIP_TABS`. These ids are a DERIVED grouping, not values
|
|
27
|
+
* any Firestore field holds, so there is no backing enum to compare them
|
|
28
|
+
* against. `STATUS_SCOPE` above is what keeps them honest instead, at compile
|
|
29
|
+
* time.
|
|
30
|
+
*/
|
|
31
|
+
export declare const ORDER_SCOPE_TABS: readonly {
|
|
32
|
+
id: OrderScope;
|
|
33
|
+
label: string;
|
|
34
|
+
}[];
|
|
35
|
+
/**
|
|
36
|
+
* Fold the lifecycle scope into a caller-supplied Sieve filter string.
|
|
37
|
+
*
|
|
38
|
+
* Shared by all three order list routes rather than written three times —
|
|
39
|
+
* partly for the usual reason, but mostly because of the guard: if `filters`
|
|
40
|
+
* already carries a `status==` clause, the scope must NOT add a second one.
|
|
41
|
+
* Two equality clauses on one field are AND-ed by Sieve, and no document can
|
|
42
|
+
* satisfy both, so the list would silently return zero rows (the failure mode
|
|
43
|
+
* documented in Root Cause #59). An explicit status wins; the scope only fills
|
|
44
|
+
* the gap when the caller named none.
|
|
45
|
+
*/
|
|
46
|
+
export declare function mergeOrderScopeFilter(filters: string | undefined | null, scopeParam: string | undefined | null): string | undefined;
|
|
47
|
+
/** Re-exported for callers that need the raw values alongside the scope. */
|
|
48
|
+
export { OrderStatusValues };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: Filtering orders meant picking one of seven individual statuses, on
|
|
3
|
+
* every order dashboard. The overwhelmingly common questions — "what still
|
|
4
|
+
* needs someone to act on it" and "what is finished" — had no answer at
|
|
5
|
+
* all, and the per-view status arrays had drifted from the real union
|
|
6
|
+
* anyway (`SELLER_ORDER_STATUS_TABS` omitted `confirmed`, a status the
|
|
7
|
+
* seller's own Update-Status dropdown could SET, so a seller could put an
|
|
8
|
+
* order into a state they could then never filter for).
|
|
9
|
+
*
|
|
10
|
+
* WHAT: A lifecycle scope — Active / Closed / All — that groups the nine real
|
|
11
|
+
* statuses. The buyer, seller and admin order lists all render the same
|
|
12
|
+
* three tabs, and the detailed status chips remain underneath as the
|
|
13
|
+
* drill-down.
|
|
14
|
+
*
|
|
15
|
+
* EXPORTS: ORDER_SCOPE_VALUES, OrderScope, isOrderScope, statusesForScope,
|
|
16
|
+
* ORDER_SCOPE_TABS
|
|
17
|
+
*
|
|
18
|
+
* @tag domain:orders
|
|
19
|
+
* @tag layer:constants
|
|
20
|
+
* @tag pattern:none
|
|
21
|
+
* @tag access:isomorphic
|
|
22
|
+
* @tag consumers:AdminOrdersView,SellerOrdersView,UserOrdersView,api/user/orders
|
|
23
|
+
* @tag sideEffects:none
|
|
24
|
+
*/
|
|
25
|
+
import { OrderStatusValues } from "../schemas/firestore";
|
|
26
|
+
export const ORDER_SCOPE_VALUES = {
|
|
27
|
+
/** Still moving — someone (buyer, seller or admin) has something to do. */
|
|
28
|
+
ACTIVE: "active",
|
|
29
|
+
/** Terminal — delivered, cancelled, refunded or returned. */
|
|
30
|
+
CLOSED: "closed",
|
|
31
|
+
/** No lifecycle predicate. */
|
|
32
|
+
ALL: "all",
|
|
33
|
+
};
|
|
34
|
+
export function isOrderScope(value) {
|
|
35
|
+
return Object.values(ORDER_SCOPE_VALUES).includes(value);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Every status belongs to exactly one scope.
|
|
39
|
+
*
|
|
40
|
+
* `Record<OrderStatus, …>` on purpose: adding a tenth status is then a COMPILE
|
|
41
|
+
* error rather than a status that silently belongs to neither tab and
|
|
42
|
+
* disappears from both (Root Cause #61).
|
|
43
|
+
*/
|
|
44
|
+
const STATUS_SCOPE = {
|
|
45
|
+
pending: "active",
|
|
46
|
+
confirmed: "active",
|
|
47
|
+
processing: "active",
|
|
48
|
+
shipped: "active",
|
|
49
|
+
// Deliberately ACTIVE: a return request is the seller's next action item,
|
|
50
|
+
// not a finished order. Grouping it under Closed would bury exactly the
|
|
51
|
+
// rows a seller most needs to see.
|
|
52
|
+
return_requested: "active",
|
|
53
|
+
delivered: "closed",
|
|
54
|
+
cancelled: "closed",
|
|
55
|
+
refunded: "closed",
|
|
56
|
+
returned: "closed",
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* The statuses a scope covers, or `undefined` for "all" (no filter).
|
|
60
|
+
*
|
|
61
|
+
* Emitted as a pipe-joined OR-group, which the Firebase Sieve adapter upgrades
|
|
62
|
+
* to a single Firestore "in" query — no fan-out, and it reuses the existing
|
|
63
|
+
* (status, createdAt) composite index.
|
|
64
|
+
*/
|
|
65
|
+
export function statusesForScope(scope) {
|
|
66
|
+
if (scope === ORDER_SCOPE_VALUES.ALL)
|
|
67
|
+
return undefined;
|
|
68
|
+
return Object.keys(STATUS_SCOPE).filter((status) => STATUS_SCOPE[status] === scope);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Tab definitions for the scope bar.
|
|
72
|
+
*
|
|
73
|
+
* NOT registered in `audit-filter-tab-enums.mjs`'s REGISTRY, and deliberately
|
|
74
|
+
* so — the same reasoning its header gives for `ADMIN_ORDER_PAYMENT_REVIEW_TABS`
|
|
75
|
+
* and `ADMIN_CART_OWNERSHIP_TABS`. These ids are a DERIVED grouping, not values
|
|
76
|
+
* any Firestore field holds, so there is no backing enum to compare them
|
|
77
|
+
* against. `STATUS_SCOPE` above is what keeps them honest instead, at compile
|
|
78
|
+
* time.
|
|
79
|
+
*/
|
|
80
|
+
export const ORDER_SCOPE_TABS = [
|
|
81
|
+
{ id: ORDER_SCOPE_VALUES.ACTIVE, label: "Active" },
|
|
82
|
+
{ id: ORDER_SCOPE_VALUES.CLOSED, label: "Closed" },
|
|
83
|
+
{ id: ORDER_SCOPE_VALUES.ALL, label: "All" },
|
|
84
|
+
];
|
|
85
|
+
/**
|
|
86
|
+
* Fold the lifecycle scope into a caller-supplied Sieve filter string.
|
|
87
|
+
*
|
|
88
|
+
* Shared by all three order list routes rather than written three times —
|
|
89
|
+
* partly for the usual reason, but mostly because of the guard: if `filters`
|
|
90
|
+
* already carries a `status==` clause, the scope must NOT add a second one.
|
|
91
|
+
* Two equality clauses on one field are AND-ed by Sieve, and no document can
|
|
92
|
+
* satisfy both, so the list would silently return zero rows (the failure mode
|
|
93
|
+
* documented in Root Cause #59). An explicit status wins; the scope only fills
|
|
94
|
+
* the gap when the caller named none.
|
|
95
|
+
*/
|
|
96
|
+
export function mergeOrderScopeFilter(filters, scopeParam) {
|
|
97
|
+
const existing = (filters ?? "").trim();
|
|
98
|
+
if (!scopeParam || !isOrderScope(scopeParam))
|
|
99
|
+
return existing || undefined;
|
|
100
|
+
if (/(^|,)\s*status\s*==/.test(existing))
|
|
101
|
+
return existing || undefined;
|
|
102
|
+
const statuses = statusesForScope(scopeParam);
|
|
103
|
+
if (!statuses?.length)
|
|
104
|
+
return existing || undefined;
|
|
105
|
+
// A pipe-joined OR-group on ONE field — the Firebase Sieve adapter upgrades
|
|
106
|
+
// it to a single Firestore "in" query rather than several.
|
|
107
|
+
const clause = `status==${statuses.join("|")}`;
|
|
108
|
+
return existing ? `${existing},${clause}` : clause;
|
|
109
|
+
}
|
|
110
|
+
/** Re-exported for callers that need the raw values alongside the scope. */
|
|
111
|
+
export { OrderStatusValues };
|