@mohasinac/appkit 4.62.0 → 4.63.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/.build-stamp.json +3 -3
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -29
- package/dist/_internal/client/features/layout/filterNavItems.d.ts +28 -0
- package/dist/_internal/client/features/layout/filterNavItems.js +35 -0
- package/dist/_internal/server/features/ads/booking.d.ts +118 -0
- package/dist/_internal/server/features/ads/booking.js +326 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +129 -9
- package/dist/_internal/server/features/checkout/actions.js +850 -79
- package/dist/_internal/server/features/checkout/expand-order-items.d.ts +1 -2
- package/dist/_internal/server/features/checkout/expand-order-items.js +32 -0
- package/dist/_internal/server/features/checkout/freeze-parts.d.ts +50 -0
- package/dist/_internal/server/features/checkout/freeze-parts.js +94 -0
- package/dist/_internal/server/features/checkout/value-otp-gate.d.ts +55 -0
- package/dist/_internal/server/features/checkout/value-otp-gate.js +60 -0
- package/dist/_internal/server/features/dashboard/attention.d.ts +128 -0
- package/dist/_internal/server/features/dashboard/attention.js +183 -0
- package/dist/_internal/server/features/dashboard/data.d.ts +47 -0
- package/dist/_internal/server/features/dashboard/data.js +82 -0
- package/dist/_internal/server/features/inventory/reservations.d.ts +99 -0
- package/dist/_internal/server/features/inventory/reservations.js +273 -0
- package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
- package/dist/_internal/server/features/invoicing/allocate.js +162 -0
- package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
- package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
- package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
- package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
- package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
- package/dist/_internal/server/features/invoicing/issue.js +202 -0
- package/dist/_internal/server/features/offers/destinations.d.ts +40 -0
- package/dist/_internal/server/features/offers/destinations.js +65 -0
- package/dist/_internal/server/features/orders/actions.d.ts +8 -1
- package/dist/_internal/server/features/orders/actions.js +81 -8
- package/dist/_internal/server/features/orders/adapters.js +54 -0
- package/dist/_internal/server/features/orders/amend-delivery.d.ts +56 -0
- package/dist/_internal/server/features/orders/amend-delivery.js +124 -0
- package/dist/_internal/server/features/orders/balance.d.ts +89 -0
- package/dist/_internal/server/features/orders/balance.js +256 -0
- package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
- package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
- package/dist/_internal/server/features/points/award.js +46 -4
- package/dist/_internal/server/features/pricing/actions.d.ts +58 -0
- package/dist/_internal/server/features/pricing/actions.js +144 -0
- package/dist/_internal/server/features/refunds/actions.js +26 -0
- package/dist/_internal/server/features/shipping/book-split.d.ts +86 -0
- package/dist/_internal/server/features/shipping/book-split.js +130 -0
- package/dist/_internal/server/features/shipping/book.d.ts +61 -0
- package/dist/_internal/server/features/shipping/book.js +176 -0
- package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
- package/dist/_internal/server/features/shipping/courier-block.js +264 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
- package/dist/_internal/server/features/site-settings/adapters.js +29 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
- package/dist/_internal/server/features/stores/adapters.js +65 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +95 -2
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +11 -2
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
- package/dist/_internal/server/jobs/core/index.d.ts +2 -1
- package/dist/_internal/server/jobs/core/index.js +2 -1
- package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
- package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
- package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +65 -0
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
- package/dist/_internal/server/jobs/core/reservationExpiry.d.ts +17 -0
- package/dist/_internal/server/jobs/core/reservationExpiry.js +40 -0
- package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
- package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +25 -2
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +1 -0
- package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
- package/dist/_internal/server/jobs/handlers/reservationExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
- package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +296 -0
- package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
- package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
- package/dist/_internal/shared/checkout/order-math.d.ts +174 -0
- package/dist/_internal/shared/checkout/order-math.js +159 -0
- package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
- package/dist/_internal/shared/checkout/refundable-base.js +13 -1
- package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
- package/dist/_internal/shared/checkout/rules/index.js +1 -1
- package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +18 -0
- package/dist/_internal/shared/checkout/rules/payment-gate.js +25 -0
- package/dist/_internal/shared/checkout/split.d.ts +75 -0
- package/dist/_internal/shared/checkout/split.js +120 -0
- package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
- package/dist/_internal/shared/content/disclaimers.js +217 -0
- package/dist/_internal/shared/content/published-values.d.ts +93 -0
- package/dist/_internal/shared/content/published-values.js +206 -0
- package/dist/_internal/shared/features/admin/integrations.d.ts +139 -0
- package/dist/_internal/shared/features/admin/integrations.js +263 -0
- package/dist/_internal/shared/features/dashboard/hub-sections.d.ts +72 -0
- package/dist/_internal/shared/features/dashboard/hub-sections.js +75 -0
- package/dist/_internal/shared/features/emi/choice.d.ts +98 -0
- package/dist/_internal/shared/features/emi/choice.js +69 -0
- package/dist/_internal/shared/features/emi/group.d.ts +84 -0
- package/dist/_internal/shared/features/emi/group.js +126 -0
- package/dist/_internal/shared/features/ledger/posting.d.ts +107 -0
- package/dist/_internal/shared/features/ledger/posting.js +165 -0
- package/dist/_internal/shared/features/ledger/reports.d.ts +76 -0
- package/dist/_internal/shared/features/ledger/reports.js +127 -0
- package/dist/_internal/shared/features/ledger/rules.d.ts +170 -0
- package/dist/_internal/shared/features/ledger/rules.js +221 -0
- package/dist/_internal/shared/features/offers/coordination.d.ts +122 -0
- package/dist/_internal/shared/features/offers/coordination.js +80 -0
- package/dist/_internal/shared/features/orders/delivery-snapshot.d.ts +114 -0
- package/dist/_internal/shared/features/orders/delivery-snapshot.js +116 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
- package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
- package/dist/_internal/shared/features/payouts/pay-period.d.ts +91 -0
- package/dist/_internal/shared/features/payouts/pay-period.js +104 -0
- package/dist/_internal/shared/features/points/accepts.d.ts +29 -0
- package/dist/_internal/shared/features/points/accepts.js +49 -0
- package/dist/_internal/shared/features/pricing/rate-registry.d.ts +150 -0
- package/dist/_internal/shared/features/pricing/rate-registry.js +246 -0
- package/dist/_internal/shared/features/shipping/parcel.d.ts +123 -0
- package/dist/_internal/shared/features/shipping/parcel.js +139 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
- package/dist/_internal/shared/features/site-settings/fees.d.ts +16 -8
- package/dist/_internal/shared/features/site-settings/fees.js +12 -8
- package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
- package/dist/_internal/shared/fees/calculator.d.ts +33 -1
- package/dist/_internal/shared/fees/calculator.js +32 -2
- package/dist/_internal/shared/fees/explain.d.ts +81 -0
- package/dist/_internal/shared/fees/explain.js +101 -0
- package/dist/_internal/shared/history/index.d.ts +1 -1
- package/dist/_internal/shared/history/index.js +1 -1
- package/dist/_internal/shared/history/types.d.ts +7 -1
- package/dist/_internal/shared/history/types.js +25 -0
- package/dist/client.d.ts +21 -0
- package/dist/client.js +43 -0
- package/dist/constants/api-endpoints.d.ts +114 -0
- package/dist/constants/api-endpoints.js +55 -0
- package/dist/constants/field-names.d.ts +42 -0
- package/dist/constants/field-names.js +52 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/shipping.d.ts +45 -0
- package/dist/contracts/shipping.js +15 -0
- package/dist/features/about/components/AboutView.js +1 -1
- package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
- package/dist/features/account/components/UserOrdersView.js +47 -35
- package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
- package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
- package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
- package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
- package/dist/features/admin/components/AdminInvoicingView.js +65 -0
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +33 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +81 -3
- package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
- package/dist/features/admin/components/AdminOrdersView.js +86 -5
- package/dist/features/admin/components/OwnerQuickActions.js +24 -2
- package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
- package/dist/features/admin/constants/filter-tabs.js +6 -0
- package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
- package/dist/features/admin/hooks/useAdminListingData.js +16 -0
- package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
- package/dist/features/admin/schemas/firestore.d.ts +62 -0
- package/dist/features/admin/schemas/firestore.js +5 -0
- package/dist/features/ads/schemas/booking-write.d.ts +120 -0
- package/dist/features/ads/schemas/booking-write.js +75 -0
- package/dist/features/ads/schemas/creative-form.d.ts +18 -0
- package/dist/features/ads/schemas/creative-form.js +51 -0
- package/dist/features/ads/schemas/firestore.d.ts +140 -0
- package/dist/features/ads/schemas/firestore.js +109 -0
- package/dist/features/auctions/repository/bid.repository.js +3 -1
- package/dist/features/audit-log/schemas/firestore.d.ts +140 -0
- package/dist/features/audit-log/schemas/firestore.js +89 -0
- package/dist/features/auth/actions/profile-actions.d.ts +69 -1
- package/dist/features/auth/actions/profile-actions.js +62 -1
- package/dist/features/auth/permissions/constants.js +13 -0
- package/dist/features/auth/schemas/firestore.d.ts +16 -2
- package/dist/features/auth/schemas/firestore.js +2 -1
- package/dist/features/blog/actions/blog-actions.d.ts +6 -0
- package/dist/features/blog/actions/blog-actions.js +17 -2
- package/dist/features/blog/api/route.js +4 -1
- package/dist/features/blog/columns/index.js +5 -0
- package/dist/features/blog/components/BlogIndexPageView.js +2 -1
- package/dist/features/blog/repository/blog.repository.d.ts +4 -0
- package/dist/features/blog/repository/blog.repository.js +5 -0
- package/dist/features/blog/schemas/blog-form.d.ts +3 -0
- package/dist/features/blog/schemas/blog-form.js +1 -0
- package/dist/features/blog/schemas/firestore.d.ts +38 -1
- package/dist/features/blog/schemas/firestore.js +23 -0
- package/dist/features/blog/schemas/index.d.ts +6 -0
- package/dist/features/blog/schemas/index.js +2 -0
- package/dist/features/blog/types/index.d.ts +2 -0
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +25 -1
- package/dist/features/cart/components/CartPriceBreakdown.js +29 -6
- package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
- package/dist/features/cart/components/PointsTenderPicker.js +15 -0
- package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
- package/dist/features/cart/components/index.d.ts +2 -0
- package/dist/features/cart/components/index.js +1 -0
- package/dist/features/cart/repository/cart.repository.d.ts +33 -0
- package/dist/features/cart/repository/cart.repository.js +47 -0
- package/dist/features/cart/schemas/firestore.d.ts +116 -0
- package/dist/features/contact/email.d.ts +27 -10
- package/dist/features/contact/email.js +20 -3
- package/dist/features/dashboard/components/DashboardHubView.d.ts +83 -0
- package/dist/features/dashboard/components/DashboardHubView.js +56 -0
- package/dist/features/dashboard/components/DashboardSectionsClient.d.ts +23 -0
- package/dist/features/dashboard/components/DashboardSectionsClient.js +66 -0
- package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.d.ts +55 -0
- package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.js +175 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +62 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +108 -0
- package/dist/features/dispute-reasons/schemas/firestore.d.ts +62 -0
- package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
- package/dist/features/faq/components/FAQAccordion.js +29 -4
- package/dist/features/faq/components/FAQPageContent.js +2 -2
- package/dist/features/gateway-events/schemas/index.d.ts +2 -2
- package/dist/features/homepage/schemas/index.d.ts +8 -8
- package/dist/features/inventory/schemas/stock-reservation.d.ts +114 -0
- package/dist/features/inventory/schemas/stock-reservation.js +103 -0
- package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
- package/dist/features/invoicing/schemas/firestore.js +45 -0
- package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
- package/dist/features/invoicing/utils/financial-year.js +53 -0
- package/dist/features/jobs/actions/enqueue-job.js +18 -1
- package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
- package/dist/features/jobs/repository/jobs.repository.js +84 -1
- package/dist/features/jobs/schemas/firestore.d.ts +80 -0
- package/dist/features/jobs/schemas/firestore.js +37 -0
- package/dist/features/layout/AppLayoutShell.d.ts +9 -1
- package/dist/features/layout/AppLayoutShell.js +12 -4
- package/dist/features/layout/BottomActions.js +2 -1
- package/dist/features/layout/BottomNavbar.d.ts +10 -2
- package/dist/features/layout/SideActionDock.js +52 -2
- package/dist/features/layout/TitleBarLayout.js +13 -4
- package/dist/features/layout/TitleBarLayout.style.css +59 -0
- package/dist/features/layout/useCtaReveal.d.ts +16 -1
- package/dist/features/layout/useCtaReveal.js +17 -26
- package/dist/features/ledger/repository/journal.repository.d.ts +110 -0
- package/dist/features/ledger/repository/journal.repository.js +312 -0
- package/dist/features/ledger/schemas/expenses.d.ts +87 -0
- package/dist/features/ledger/schemas/expenses.js +27 -0
- package/dist/features/ledger/schemas/firestore.d.ts +183 -0
- package/dist/features/ledger/schemas/firestore.js +69 -0
- package/dist/features/lottery/schemas/zod.d.ts +6 -6
- package/dist/features/media/MediaImage.js +22 -1
- package/dist/features/media/upload/ImageUpload.js +1 -1
- package/dist/features/obligations/constants/policies.js +24 -1
- package/dist/features/orders/components/BalancePaymentPanel.d.ts +12 -0
- package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
- package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
- package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
- package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
- package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
- package/dist/features/orders/components/RaiseDisputeForm.js +119 -0
- package/dist/features/orders/components/RefundRequestView.js +19 -1
- package/dist/features/orders/components/index.d.ts +4 -0
- package/dist/features/orders/components/index.js +2 -0
- package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
- package/dist/features/orders/constants/order-lane-tabs.js +88 -0
- package/dist/features/orders/repository/orders.repository.d.ts +85 -1
- package/dist/features/orders/repository/orders.repository.js +205 -6
- package/dist/features/orders/schemas/balance.d.ts +40 -0
- package/dist/features/orders/schemas/balance.js +47 -0
- package/dist/features/orders/schemas/firestore.d.ts +398 -1
- package/dist/features/orders/schemas/firestore.js +43 -0
- package/dist/features/orders/schemas/index.d.ts +56 -8
- package/dist/features/orders/schemas/index.js +12 -0
- package/dist/features/orders/types/index.d.ts +47 -0
- package/dist/features/orders/utils/courier-block.d.ts +25 -0
- package/dist/features/orders/utils/courier-block.js +23 -0
- package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
- package/dist/features/payment-cases/schemas/firestore.js +1 -0
- package/dist/features/payments/repository/payout.repository.js +3 -2
- package/dist/features/payments/schemas/firestore.d.ts +92 -1
- package/dist/features/payments/schemas/firestore.js +23 -0
- package/dist/features/pricing/components/AdminPricingView.d.ts +13 -0
- package/dist/features/pricing/components/AdminPricingView.js +157 -0
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
- package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
- package/dist/features/products/components/ProductDetailPageView.js +2 -2
- package/dist/features/products/schemas/firestore.d.ts +26 -1
- package/dist/features/products/schemas/firestore.js +2 -0
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
- package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
- package/dist/features/promotions/actions/coupon-stacking.js +21 -2
- package/dist/features/promotions/components/CouponCard.js +13 -0
- package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
- package/dist/features/promotions/constants/coupon-help.js +9 -1
- package/dist/features/promotions/schemas/firestore.d.ts +24 -0
- package/dist/features/promotions/schemas/firestore.js +4 -0
- package/dist/features/promotions/types/index.d.ts +19 -1
- package/dist/features/reviews/repository/reviews.repository.d.ts +14 -0
- package/dist/features/reviews/repository/reviews.repository.js +16 -0
- package/dist/features/scams/components/ScamProfileView.js +2 -2
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/scams/repository/scammer.repository.js +2 -1
- package/dist/features/scams/schemas/firestore.d.ts +2 -1
- package/dist/features/scams/schemas/firestore.js +2 -1
- package/dist/features/search/action-index/tab-entries.js +1 -1
- package/dist/features/seller/actions/offer-actions.js +56 -0
- package/dist/features/seller/actions/seller-actions.d.ts +1 -1
- package/dist/features/seller/actions/seller-actions.js +37 -3
- package/dist/features/seller/components/DispatchForm.d.ts +11 -0
- package/dist/features/seller/components/DispatchForm.js +190 -0
- package/dist/features/seller/components/SellerOrdersView.js +221 -27
- package/dist/features/seller/components/index.d.ts +1 -0
- package/dist/features/seller/components/index.js +1 -0
- package/dist/features/seller/hooks/useSellerListingData.js +25 -0
- package/dist/features/seller/repository/offer.repository.js +4 -3
- package/dist/features/seller/schemas/firestore.d.ts +23 -2
- package/dist/features/seller/schemas/firestore.js +3 -2
- package/dist/features/seller/schemas/index.d.ts +4 -4
- package/dist/features/seller/schemas/order-forms.d.ts +61 -4
- package/dist/features/seller/schemas/order-forms.js +41 -8
- package/dist/features/shipments/schemas/firestore.d.ts +25 -0
- package/dist/features/shipments/schemas/validation.d.ts +4 -4
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +82 -0
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +145 -0
- package/dist/features/shipping-boxes/schemas/firestore.d.ts +80 -0
- package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
- package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
- package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
- package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
- package/dist/features/stores/repository/store.repository.d.ts +18 -0
- package/dist/features/stores/repository/store.repository.js +27 -1
- package/dist/features/stores/schemas/firestore.d.ts +112 -0
- package/dist/features/stores/schemas/firestore.js +20 -0
- package/dist/features/stores/schemas/index.d.ts +6 -6
- package/dist/features/stores/types/index.d.ts +19 -0
- package/dist/features/support/repository/support.repository.js +72 -6
- package/dist/features/support/schemas/firestore.d.ts +17 -0
- package/dist/features/support/schemas/index.d.ts +8 -8
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +33 -1
- package/dist/next/routing/route-map.d.ts +112 -0
- package/dist/next/routing/route-map.js +63 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
- package/dist/providers/shipping-shiprocket/index.js +84 -1
- package/dist/react/contexts/SessionContext.d.ts +17 -1
- package/dist/schemas/registry.d.ts +69 -34
- package/dist/schemas/sieve.d.ts +2 -2
- package/dist/security/pii-schemas.d.ts +1 -1
- package/dist/security/pii-schemas.js +45 -0
- package/dist/server-entry.d.ts +2 -2
- package/dist/server-entry.js +8 -1
- package/dist/server.d.ts +36 -1
- package/dist/server.js +183 -1
- package/dist/styles.css +412 -80
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +1 -0
- package/dist/tokens/themes/default-light.js +1 -0
- package/dist/tokens/themes/required.d.ts +1 -1
- package/dist/tokens/themes/required.js +1 -0
- package/dist/tokens/tokens.css +15 -0
- package/dist/ui/components/Button.d.ts +10 -1
- package/dist/ui/components/Button.js +25 -3
- package/dist/ui/components/Chart.d.ts +63 -0
- package/dist/ui/components/Chart.js +169 -0
- package/dist/ui/components/Chart.style.css +214 -0
- package/dist/ui/components/Disclaimer.d.ts +19 -0
- package/dist/ui/components/Disclaimer.js +83 -0
- package/dist/ui/components/OverflowRow.js +54 -2
- package/dist/ui/components/OverflowRow.style.css +38 -0
- package/dist/ui/components/RichTextEditor.js +99 -8
- package/dist/ui/components/chart-spec.d.ts +68 -0
- package/dist/ui/components/chart-spec.js +140 -0
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/components/surface-tokens.d.ts +1 -1
- package/dist/ui/components/surface-tokens.js +9 -0
- package/dist/ui/components/tabs-overflow.js +64 -11
- package/dist/ui/components/toolbar-collapse.d.ts +13 -0
- package/dist/ui/components/toolbar-collapse.js +10 -1
- package/dist/ui/index.d.ts +8 -0
- package/dist/ui/index.js +9 -0
- package/dist/ui/rich-text/RichText.d.ts +10 -1
- package/dist/ui/rich-text/RichText.js +33 -2
- package/dist/ui/rich-text/RichText.style.css +83 -79
- package/dist/ui/rich-text/RichTextRenderer.d.ts +30 -1
- package/dist/ui/rich-text/RichTextRenderer.js +34 -2
- package/dist/ui/rich-text/chart-blocks.d.ts +62 -0
- package/dist/ui/rich-text/chart-blocks.js +87 -0
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +1834 -1822
|
@@ -23,6 +23,20 @@ export declare const JobStatusValues: {
|
|
|
23
23
|
readonly PROCESSING: "processing";
|
|
24
24
|
readonly DONE: "done";
|
|
25
25
|
readonly FAILED: "failed";
|
|
26
|
+
/**
|
|
27
|
+
* Phase 8 — attempts exhausted. The dead-letter state.
|
|
28
|
+
*
|
|
29
|
+
* 🛑 `failed` and `dead` are DIFFERENT facts and conflating them is what
|
|
30
|
+
* made a failed job terminal with no signal. `failed` now means "this
|
|
31
|
+
* attempt failed, and a retry is scheduled or the failure was permanent";
|
|
32
|
+
* `dead` means "we have stopped trying" and is the only one that opens a
|
|
33
|
+
* case.
|
|
34
|
+
*
|
|
35
|
+
* A reader that treats every non-`done` as hopeless loses that distinction —
|
|
36
|
+
* which is why the retention sweep excludes `dead` while an open case
|
|
37
|
+
* references it: the row is the evidence.
|
|
38
|
+
*/
|
|
39
|
+
readonly DEAD: "dead";
|
|
26
40
|
};
|
|
27
41
|
export type JobStatus = (typeof JobStatusValues)[keyof typeof JobStatusValues];
|
|
28
42
|
export interface JobResult {
|
|
@@ -52,7 +66,55 @@ export interface JobDocument extends BaseDocument {
|
|
|
52
66
|
error?: string;
|
|
53
67
|
startedAt?: Date;
|
|
54
68
|
finishedAt?: Date;
|
|
69
|
+
/**
|
|
70
|
+
* 1-based. Absent means 1 — every job written before this field is a first
|
|
71
|
+
* attempt, which is both true and what every reader should assume.
|
|
72
|
+
*/
|
|
73
|
+
attempt?: number;
|
|
74
|
+
/** Defaults to `JOB_MAX_ATTEMPTS`; a jobType may lower it. */
|
|
75
|
+
maxAttempts?: number;
|
|
76
|
+
/**
|
|
77
|
+
* When a retry becomes eligible.
|
|
78
|
+
*
|
|
79
|
+
* 🛑 A retry is a **NEW DOCUMENT**, not a mutation of this one, because
|
|
80
|
+
* `onJobCreated` fires on CREATE — re-running a job by flipping its status
|
|
81
|
+
* back to `pending` would never wake the trigger, and the job would sit
|
|
82
|
+
* `pending` forever looking scheduled. So this field lives on the NEW
|
|
83
|
+
* document and a sweep is what moves it from scheduled to running.
|
|
84
|
+
*/
|
|
85
|
+
nextAttemptAt?: Date;
|
|
86
|
+
/**
|
|
87
|
+
* The document this is a retry OF — present on every attempt after the
|
|
88
|
+
* first, and absent on the original.
|
|
89
|
+
*
|
|
90
|
+
* 🛑 Points at the ORIGINAL, not at the immediately previous attempt. A
|
|
91
|
+
* flat chain means "show me every attempt of this job" is one equality
|
|
92
|
+
* query, where a linked list would need N reads to walk; and the question
|
|
93
|
+
* anyone actually asks is "did this job eventually work", which is about the
|
|
94
|
+
* original.
|
|
95
|
+
*/
|
|
96
|
+
parentJobId?: string;
|
|
97
|
+
/** Why this attempt failed, kept per attempt so a pattern is visible. */
|
|
98
|
+
lastError?: string;
|
|
55
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Attempts before a job is declared dead.
|
|
102
|
+
*
|
|
103
|
+
* Three, with `JOB_BACKOFF_MS` below: a transient fault usually clears in
|
|
104
|
+
* minutes, and a fourth attempt an hour later is far more likely to be the
|
|
105
|
+
* same permanent fault again than a different outcome.
|
|
106
|
+
*/
|
|
107
|
+
export declare const JOB_MAX_ATTEMPTS = 3;
|
|
108
|
+
/**
|
|
109
|
+
* Delay before attempt 2 and attempt 3.
|
|
110
|
+
*
|
|
111
|
+
* 🛑 Indexed by `attempt - 1`, so the array length must be at least
|
|
112
|
+
* `JOB_MAX_ATTEMPTS - 1`. The last value is reused if it is shorter, rather
|
|
113
|
+
* than producing `undefined` — a `nextAttemptAt` of `Invalid Date` would make
|
|
114
|
+
* the sweep's `<= now` comparison false forever and the retry would never run,
|
|
115
|
+
* which is the silent-stall shape this whole phase exists to remove.
|
|
116
|
+
*/
|
|
117
|
+
export declare const JOB_BACKOFF_MS: readonly [number, number];
|
|
56
118
|
export declare const JOB_FIELDS: {
|
|
57
119
|
readonly JOB_TYPE: "jobType";
|
|
58
120
|
readonly STATUS: "status";
|
|
@@ -63,10 +125,28 @@ export declare const JOB_FIELDS: {
|
|
|
63
125
|
readonly FINISHED_AT: "finishedAt";
|
|
64
126
|
readonly CREATED_AT: "createdAt";
|
|
65
127
|
readonly UPDATED_AT: "updatedAt";
|
|
128
|
+
readonly ATTEMPT: "attempt";
|
|
129
|
+
readonly MAX_ATTEMPTS: "maxAttempts";
|
|
130
|
+
readonly NEXT_ATTEMPT_AT: "nextAttemptAt";
|
|
131
|
+
readonly PARENT_JOB_ID: "parentJobId";
|
|
66
132
|
readonly STATUS_VALUES: {
|
|
67
133
|
readonly PENDING: "pending";
|
|
68
134
|
readonly PROCESSING: "processing";
|
|
69
135
|
readonly DONE: "done";
|
|
70
136
|
readonly FAILED: "failed";
|
|
137
|
+
/**
|
|
138
|
+
* Phase 8 — attempts exhausted. The dead-letter state.
|
|
139
|
+
*
|
|
140
|
+
* 🛑 `failed` and `dead` are DIFFERENT facts and conflating them is what
|
|
141
|
+
* made a failed job terminal with no signal. `failed` now means "this
|
|
142
|
+
* attempt failed, and a retry is scheduled or the failure was permanent";
|
|
143
|
+
* `dead` means "we have stopped trying" and is the only one that opens a
|
|
144
|
+
* case.
|
|
145
|
+
*
|
|
146
|
+
* A reader that treats every non-`done` as hopeless loses that distinction —
|
|
147
|
+
* which is why the retention sweep excludes `dead` while an open case
|
|
148
|
+
* references it: the row is the evidence.
|
|
149
|
+
*/
|
|
150
|
+
readonly DEAD: "dead";
|
|
71
151
|
};
|
|
72
152
|
};
|
|
@@ -21,7 +21,39 @@ export const JobStatusValues = {
|
|
|
21
21
|
PROCESSING: "processing",
|
|
22
22
|
DONE: "done",
|
|
23
23
|
FAILED: "failed",
|
|
24
|
+
/**
|
|
25
|
+
* Phase 8 — attempts exhausted. The dead-letter state.
|
|
26
|
+
*
|
|
27
|
+
* 🛑 `failed` and `dead` are DIFFERENT facts and conflating them is what
|
|
28
|
+
* made a failed job terminal with no signal. `failed` now means "this
|
|
29
|
+
* attempt failed, and a retry is scheduled or the failure was permanent";
|
|
30
|
+
* `dead` means "we have stopped trying" and is the only one that opens a
|
|
31
|
+
* case.
|
|
32
|
+
*
|
|
33
|
+
* A reader that treats every non-`done` as hopeless loses that distinction —
|
|
34
|
+
* which is why the retention sweep excludes `dead` while an open case
|
|
35
|
+
* references it: the row is the evidence.
|
|
36
|
+
*/
|
|
37
|
+
DEAD: "dead",
|
|
24
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Attempts before a job is declared dead.
|
|
41
|
+
*
|
|
42
|
+
* Three, with `JOB_BACKOFF_MS` below: a transient fault usually clears in
|
|
43
|
+
* minutes, and a fourth attempt an hour later is far more likely to be the
|
|
44
|
+
* same permanent fault again than a different outcome.
|
|
45
|
+
*/
|
|
46
|
+
export const JOB_MAX_ATTEMPTS = 3;
|
|
47
|
+
/**
|
|
48
|
+
* Delay before attempt 2 and attempt 3.
|
|
49
|
+
*
|
|
50
|
+
* 🛑 Indexed by `attempt - 1`, so the array length must be at least
|
|
51
|
+
* `JOB_MAX_ATTEMPTS - 1`. The last value is reused if it is shorter, rather
|
|
52
|
+
* than producing `undefined` — a `nextAttemptAt` of `Invalid Date` would make
|
|
53
|
+
* the sweep's `<= now` comparison false forever and the retry would never run,
|
|
54
|
+
* which is the silent-stall shape this whole phase exists to remove.
|
|
55
|
+
*/
|
|
56
|
+
export const JOB_BACKOFF_MS = [5 * 60 * 1000, 30 * 60 * 1000];
|
|
25
57
|
export const JOB_FIELDS = {
|
|
26
58
|
JOB_TYPE: "jobType",
|
|
27
59
|
STATUS: "status",
|
|
@@ -32,5 +64,10 @@ export const JOB_FIELDS = {
|
|
|
32
64
|
FINISHED_AT: "finishedAt",
|
|
33
65
|
CREATED_AT: "createdAt",
|
|
34
66
|
UPDATED_AT: "updatedAt",
|
|
67
|
+
// Phase 8 retry/DLQ. `NEXT_ATTEMPT_AT` is the sweep's query field.
|
|
68
|
+
ATTEMPT: "attempt",
|
|
69
|
+
MAX_ATTEMPTS: "maxAttempts",
|
|
70
|
+
NEXT_ATTEMPT_AT: "nextAttemptAt",
|
|
71
|
+
PARENT_JOB_ID: "parentJobId",
|
|
35
72
|
STATUS_VALUES: JobStatusValues,
|
|
36
73
|
};
|
|
@@ -94,6 +94,14 @@ export interface AppLayoutShellProps {
|
|
|
94
94
|
userWishlistHref?: string;
|
|
95
95
|
/** Href for the user settings page — auto-generates PROFILE section in sidebar. */
|
|
96
96
|
userSettingsHref?: string;
|
|
97
|
+
/**
|
|
98
|
+
* Href for the user's OWN reviews list — the target of the "Reviews" stat.
|
|
99
|
+
*
|
|
100
|
+
* The stat pointed at `${profileHref}#reviews` before, an anchor the profile
|
|
101
|
+
* page does not render, so the count linked to the top of an unrelated page.
|
|
102
|
+
* Omitting this prop hides the tile rather than guessing at a route.
|
|
103
|
+
*/
|
|
104
|
+
userReviewsHref?: string;
|
|
97
105
|
/** Locale switcher node rendered at the bottom of the sidebar. */
|
|
98
106
|
sidebarLocaleSlot?: React.ReactNode;
|
|
99
107
|
/** When true, shows theme toggle button in the sidebar footer. */
|
|
@@ -148,4 +156,4 @@ export interface AppLayoutShellProps {
|
|
|
148
156
|
};
|
|
149
157
|
};
|
|
150
158
|
}
|
|
151
|
-
export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, messagesHref, unreadMessageCount, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, showFooter, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, showBottomNav, contentClassName, lightBackground, darkBackground, }: AppLayoutShellProps): React.JSX.Element;
|
|
159
|
+
export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, messagesHref, unreadMessageCount, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, showFooter, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, userReviewsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, showBottomNav, contentClassName, lightBackground, darkBackground, }: AppLayoutShellProps): React.JSX.Element;
|
|
@@ -17,6 +17,7 @@ const ROLE_DOT_BG_CLASS = {
|
|
|
17
17
|
import { useTheme } from "../../_internal/client/theme/index.js";
|
|
18
18
|
import { useAuth } from "../../react/contexts/SessionContext.js";
|
|
19
19
|
import { useVisualViewportInset } from "../../react/hooks/useVisualViewportInset.js";
|
|
20
|
+
import { useListingTypeFlags } from "../../react/hooks/useListingTypeFlags.js";
|
|
20
21
|
import { isBuyerUser } from "../auth/role-predicates.js";
|
|
21
22
|
import { NavbarWithSettings } from "./NavbarWithSettings.js";
|
|
22
23
|
import SideActionDock from "./SideActionDock.js";
|
|
@@ -135,13 +136,20 @@ function SidebarUserHeader({ user, onClose, profileHref, }) {
|
|
|
135
136
|
function SidebarGuestHeader({ sidebarTitle, onClose, }) {
|
|
136
137
|
return (_jsxs(Row, { align: "center", justify: "between", children: [_jsx(Div, { textWeight: "semibold", textSize: "sm", color: "primary", children: sidebarTitle }), _jsx("button", { type: "button", "aria-label": "Close menu", onClick: onClose, className: "rounded-full p-[var(--appkit-space-2)] hover:bg-surface-hover hover:text-zinc-900 text-[var(--appkit-color-text-muted)] dark:hover:text-zinc-100 transition-all hover:rotate-90", children: _jsx("svg", { className: "w-4 h-4 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }));
|
|
137
138
|
}
|
|
138
|
-
function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions, user, activeHref, profileHref, userOrdersHref, userWishlistHref, userSettingsHref, adminHref, storeHref, sellerHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, theme, activeThemeName, toggleTheme, onLogout, onAfterLogout, }) {
|
|
139
|
+
function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions, user, activeHref, profileHref, userOrdersHref, userWishlistHref, userSettingsHref, userReviewsHref, adminHref, storeHref, sellerHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, theme, activeThemeName, toggleTheme, onLogout, onAfterLogout, }) {
|
|
139
140
|
const hasLegacyItems = sidebarItems.length > 0;
|
|
140
141
|
const hasSections = !!(sidebarSections && sidebarSections.length > 0);
|
|
141
142
|
const isAuthenticated = !!user;
|
|
142
143
|
const role = user?.role ?? "user";
|
|
143
144
|
const isAdminOrSeller = role === "admin" || role === "seller";
|
|
144
145
|
const resolvedStoreHref = storeHref ?? sellerHref;
|
|
146
|
+
/*
|
|
147
|
+
* Defaults to ENABLED while site settings are loading, matching the hook's
|
|
148
|
+
* own contract — hiding the tile on first paint and popping it in a moment
|
|
149
|
+
* later is worse than showing it to the handful of installs that have
|
|
150
|
+
* auctions switched off.
|
|
151
|
+
*/
|
|
152
|
+
const auctionsEnabled = useListingTypeFlags().isEnabled("auction");
|
|
145
153
|
const labels = {
|
|
146
154
|
sectionTitle: sidebarProfileLabels?.sectionTitle ?? "Profile",
|
|
147
155
|
profile: sidebarProfileLabels?.profile ?? "My Profile",
|
|
@@ -177,10 +185,10 @@ function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions,
|
|
|
177
185
|
action.variant === "outline"
|
|
178
186
|
? "border hover:bg-surface-hover border-[var(--appkit-color-border)] text-[var(--appkit-color-text)]"
|
|
179
187
|
: "bg-primary text-white hover:bg-primary-600 dark:bg-primary dark:hover:bg-primary-600 btn-glow",
|
|
180
|
-
].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsx(CollapsibleNavGroup, { title: labels.sectionTitle, hasActive: !!activeProfileLink, children: _jsxs(Ul, { spacing: "2xs", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: activeProfileLink?.href === profileHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === profileHref ? "page" : undefined, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: activeProfileLink?.href === userOrdersHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userOrdersHref ? "page" : undefined, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: activeProfileLink?.href === userWishlistHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userWishlistHref ? "page" : undefined, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: activeProfileLink?.href === userSettingsHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userSettingsHref ? "page" : undefined, children: labels.settings }) }))] }) })), isAuthenticated && user?.stats && (_jsxs(Div, { layout: "grid", gap: "2", className: "grid-cols-2", children: [user.stats.totalOrders != null && userOrdersHref && (_jsxs(TextLink, { href: userOrdersHref, variant: "none", className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.totalOrders }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Orders" })] })), user.stats.reviewsCount != null && (_jsxs(TextLink, { href:
|
|
188
|
+
].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsx(CollapsibleNavGroup, { title: labels.sectionTitle, hasActive: !!activeProfileLink, children: _jsxs(Ul, { spacing: "2xs", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: activeProfileLink?.href === profileHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === profileHref ? "page" : undefined, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: activeProfileLink?.href === userOrdersHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userOrdersHref ? "page" : undefined, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: activeProfileLink?.href === userWishlistHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userWishlistHref ? "page" : undefined, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: activeProfileLink?.href === userSettingsHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userSettingsHref ? "page" : undefined, children: labels.settings }) }))] }) })), isAuthenticated && user?.stats && (_jsxs(Div, { layout: "grid", gap: "2", className: "grid-cols-2", children: [user.stats.totalOrders != null && userOrdersHref && (_jsxs(TextLink, { href: userOrdersHref, variant: "none", className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.totalOrders }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Orders" })] })), user.stats.reviewsCount != null && userReviewsHref && (_jsxs(TextLink, { href: userReviewsHref, variant: "none", className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.reviewsCount }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Reviews" })] })), auctionsEnabled && user.stats.auctionsWon != null && userOrdersHref && (_jsxs(TextLink, { href: `${userOrdersHref}?orderType=auction`, variant: "none", className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.auctionsWon }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Auctions Won" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref || resolvedStoreHref) && (_jsx(CollapsibleNavGroup, { title: labels.dashboardSectionTitle, hasActive: !!activeDashboardLink, children: _jsxs(Ul, { spacing: "2xs", children: [adminHref && role === "admin" && (_jsx(Li, { children: _jsx(TextLink, { href: adminHref, variant: "none", className: activeDashboardLink?.href === adminHref ? navItemActiveClass : navItemClass, "aria-current": activeDashboardLink?.href === adminHref ? "page" : undefined, children: labels.adminDashboard }) })), resolvedStoreHref && isAdminOrSeller && (_jsx(Li, { children: _jsx(TextLink, { href: resolvedStoreHref, variant: "none", className: activeDashboardLink?.href === resolvedStoreHref ? navItemActiveClass : navItemClass, "aria-current": activeDashboardLink?.href === resolvedStoreHref ? "page" : undefined, children: labels.storeDashboard }) }))] }) })), normalizedSections.map((section, sectionIndex) => (_jsx(CollapsibleSidebarSection, { section: section, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass, activeHref: activeHref }, `sidebar-section-${sectionIndex}`))), (sidebarLocaleSlot || showThemeToggleInSidebar || (isAuthenticated && onLogout)) && (_jsxs(Stack, { border: "default", className: "border-t border-[var(--appkit-color-border-subtle)]", padding: "t-md", gap: "3", children: [sidebarLocaleSlot, showThemeToggleInSidebar && (_jsxs("button", { type: "button", onClick: toggleTheme, className: "flex w-full items-center justify-end gap-[var(--appkit-space-3)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)] transition-colors hover:bg-primary-surface hover:text-primary-800 dark:hover:text-primary-200", children: [_jsx(Span, { "aria-hidden": "true", children: theme === "dark" ? "☀️" : "🌙" }), activeThemeName] })), isAuthenticated && onLogout && (_jsx("button", { type: "button", onClick: () => { onLogout(); onAfterLogout(); }, className: CLS_LOGOUT_BTN, children: labels.logout }))] }))] }));
|
|
181
189
|
}
|
|
182
190
|
// ─── Main component ────────────────────────────────────────────────────────────
|
|
183
|
-
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, messagesHref, unreadMessageCount, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, showFooter = true, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, showBottomNav = true, contentClassName, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
|
|
191
|
+
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, messagesHref, unreadMessageCount, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, showFooter = true, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, userReviewsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, showBottomNav = true, contentClassName, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
|
|
184
192
|
// QueryClientProvider used to be created here so AppLayoutShell could stand
|
|
185
193
|
// alone. The real cause of the 2026-06-10/11 "No QueryClient set" prod crash
|
|
186
194
|
// turned out to be a peer-dep duplicate of @tanstack/query-core under
|
|
@@ -228,7 +236,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
228
236
|
// by its countdown alone got no clearance). That mirror is gone: BottomChrome
|
|
229
237
|
// measures the real tier and publishes --bottom-chrome-height, and the footer
|
|
230
238
|
// reserves space from that. Nothing needs to guess any more.
|
|
231
|
-
const sidebarContent = (_jsx(SidebarContent, { sidebarItems: sidebarItems, sidebarSections: sidebarSections, sidebarPrimaryActions: sidebarPrimaryActions, user: user, activeHref: pathname ?? "", profileHref: profileHref, userOrdersHref: userOrdersHref, userWishlistHref: userWishlistHref, userSettingsHref: userSettingsHref, adminHref: adminHref, storeHref: storeHref, sellerHref: sellerHref, sidebarLocaleSlot: sidebarLocaleSlot, showThemeToggleInSidebar: showThemeToggleInSidebar, sidebarProfileLabels: sidebarProfileLabels, theme: theme, activeThemeName: activeTheme.name, toggleTheme: toggleTheme, onLogout: onLogout, onAfterLogout: () => setSidebarOpen(false) }));
|
|
239
|
+
const sidebarContent = (_jsx(SidebarContent, { sidebarItems: sidebarItems, sidebarSections: sidebarSections, sidebarPrimaryActions: sidebarPrimaryActions, user: user, activeHref: pathname ?? "", profileHref: profileHref, userOrdersHref: userOrdersHref, userWishlistHref: userWishlistHref, userSettingsHref: userSettingsHref, userReviewsHref: userReviewsHref, adminHref: adminHref, storeHref: storeHref, sellerHref: sellerHref, sidebarLocaleSlot: sidebarLocaleSlot, showThemeToggleInSidebar: showThemeToggleInSidebar, sidebarProfileLabels: sidebarProfileLabels, theme: theme, activeThemeName: activeTheme.name, toggleTheme: toggleTheme, onLogout: onLogout, onAfterLogout: () => setSidebarOpen(false) }));
|
|
232
240
|
const normalizedLightBackground = {
|
|
233
241
|
type: lightBackground.type,
|
|
234
242
|
value: lightBackground.value,
|
|
@@ -233,7 +233,8 @@ export default function BottomActions() {
|
|
|
233
233
|
* own reveal — bulk mode is a state the user just entered deliberately, and
|
|
234
234
|
* making them scroll to act on it would be a puzzle.
|
|
235
235
|
*/
|
|
236
|
-
const revealed = useCtaReveal(hasContent && !isBulkMode, ctaAnchorId)
|
|
236
|
+
const { revealed: ctaRevealed } = useCtaReveal(hasContent && !isBulkMode, ctaAnchorId);
|
|
237
|
+
const revealed = ctaRevealed || isBulkMode;
|
|
237
238
|
const isVisible = hasContent && revealed;
|
|
238
239
|
// A panel left open after its content is withdrawn (bulk mode taking over, or
|
|
239
240
|
// the page clearing it) would sit there as an empty sheet.
|
|
@@ -32,12 +32,20 @@ export interface BottomNavbarUser {
|
|
|
32
32
|
};
|
|
33
33
|
zoom: number;
|
|
34
34
|
} | null;
|
|
35
|
+
/**
|
|
36
|
+
* The account holder's own activity AS A BUYER, derived live by
|
|
37
|
+
* `/api/user/profile`. `undefined` on a member means the count could not be
|
|
38
|
+
* taken — render nothing, never 0.
|
|
39
|
+
*
|
|
40
|
+
* 🛑 `itemsSold` and `rating` were removed, not forgotten. `itemsSold` is a
|
|
41
|
+
* SELLER number and had no business in the account holder's own profile
|
|
42
|
+
* block (see `AppLayoutShell`); `rating` had no writer anywhere in either
|
|
43
|
+
* repo, so its `!= null` guard meant the tile could never render.
|
|
44
|
+
*/
|
|
35
45
|
stats?: {
|
|
36
46
|
totalOrders?: number;
|
|
37
47
|
auctionsWon?: number;
|
|
38
|
-
itemsSold?: number;
|
|
39
48
|
reviewsCount?: number;
|
|
40
|
-
rating?: number;
|
|
41
49
|
} | null;
|
|
42
50
|
}
|
|
43
51
|
export interface BottomNavbarProps {
|
|
@@ -54,7 +54,7 @@ import { useClickOutside } from "../../react/index.js";
|
|
|
54
54
|
import { useHandMode } from "../../_internal/client/hand-mode/index.js";
|
|
55
55
|
import { useCtaReveal } from "./useCtaReveal.js";
|
|
56
56
|
import { useIsDesktop } from "./useIsDesktop.js";
|
|
57
|
-
import { ActionRow, Div, IconButton, Row, Span, Stack, Text, Button } from "../../ui/index.js";
|
|
57
|
+
import { ActionRow, Div, ICON_SIZE, IconButton, Row, Span, Stack, Text, Button } from "../../ui/index.js";
|
|
58
58
|
const PANEL_SURFACE = "bg-[color-mix(in_srgb,var(--appkit-color-bg)_94%,transparent)] backdrop-blur-md border border-[var(--appkit-color-border)]";
|
|
59
59
|
const FLEX_CENTER = "flex items-center justify-center";
|
|
60
60
|
const CLS_COUNT_BADGE = "bg-error-solid text-error-on-solid";
|
|
@@ -143,8 +143,40 @@ export default function SideActionDock() {
|
|
|
143
143
|
!!infoLabel ||
|
|
144
144
|
!!secondaryLabel;
|
|
145
145
|
// Rule 3 — a selection is its own reveal.
|
|
146
|
-
const revealed = useCtaReveal(hasContent && !isBulkMode, ctaAnchorId)
|
|
146
|
+
const { revealed: ctaRevealed, hasAnchor, scrollToAnchor, } = useCtaReveal(hasContent && !isBulkMode, ctaAnchorId);
|
|
147
|
+
const revealed = ctaRevealed || isBulkMode;
|
|
147
148
|
const isVisible = isDesktop && hasContent && revealed;
|
|
149
|
+
/*
|
|
150
|
+
* ── Rule 4 — a page with a REAL Buy button gets a way BACK to it ─────────
|
|
151
|
+
*
|
|
152
|
+
* On a detail page the dock was a second copy of the CTA you had just
|
|
153
|
+
* scrolled past, which is the one thing rule 2 says a floating control must
|
|
154
|
+
* not be: `useCtaReveal` already means "you have met the Buy button and left
|
|
155
|
+
* it", so re-offering the purchase mid-screen answers a question the user
|
|
156
|
+
* did not ask. Taking them back to the real control answers the one they
|
|
157
|
+
* did — and it keeps the purchase decision on the page's own CTA, beside
|
|
158
|
+
* the price, the variant and the stock state that qualify it.
|
|
159
|
+
*
|
|
160
|
+
* 🛑 Gated on `hasAnchor`, NOT on a route or a listing type. A bulk screen
|
|
161
|
+
* marks no anchor, so it keeps the centred dock carrying its actions — which
|
|
162
|
+
* is correct, because there was never an inline control there to return to.
|
|
163
|
+
* `ctaAnchorProps` has exactly one caller today (`ProductDetailPageView`),
|
|
164
|
+
* so this branch is opt-in by construction and ~70 admin bulk bars cannot
|
|
165
|
+
* reach it.
|
|
166
|
+
*
|
|
167
|
+
* It sits in the bottom-right cluster above `BackToTop` and shares that
|
|
168
|
+
* control's bottom-tier `calc()` verbatim, so the two stack rather than
|
|
169
|
+
* overlap — and rule 1's objection (a bottom-anchored bar reads as a browser
|
|
170
|
+
* download bar) does not apply to a single round puck in the corner that is
|
|
171
|
+
* already where the scroll affordance lives.
|
|
172
|
+
*
|
|
173
|
+
* 🛑 The RETURN for this branch is deliberately deferred to just above the
|
|
174
|
+
* main one. An early return here would sit above this component's remaining
|
|
175
|
+
* `useEffect`s, so the hook order would differ between a detail page and a
|
|
176
|
+
* bulk screen — React's "rendered fewer hooks than expected" crash, and the
|
|
177
|
+
* kind that only shows up on the page nobody opened during review.
|
|
178
|
+
*/
|
|
179
|
+
const isBackToCta = isDesktop && hasAnchor && !isBulkMode;
|
|
148
180
|
// Rule 1 — opposite the sidebar, which docks to the hand-mode side.
|
|
149
181
|
const dockSideClass = hand === "left" ? "right-0" : "left-0";
|
|
150
182
|
const panelAlignClass = hand === "left" ? "items-end" : "items-start";
|
|
@@ -216,6 +248,24 @@ export default function SideActionDock() {
|
|
|
216
248
|
const fabLabel = isBulkMode
|
|
217
249
|
? `${bulk?.selectedCount ?? 0} selected — bulk actions`
|
|
218
250
|
: (primaryAction?.label ?? infoLabel ?? "Page actions");
|
|
251
|
+
// Rule 4's deferred return — below every hook, for the reason given above.
|
|
252
|
+
if (isBackToCta) {
|
|
253
|
+
if (!ctaRevealed)
|
|
254
|
+
return null;
|
|
255
|
+
return (_jsx(Div, { className: [
|
|
256
|
+
"fixed pointer-events-auto z-[var(--appkit-z-back-to-top)]",
|
|
257
|
+
hand === "left" ? "left-4" : "right-4",
|
|
258
|
+
/*
|
|
259
|
+
* `BackToTop`'s own expression verbatim — including its `4rem`
|
|
260
|
+
* default for `--bottom-nav-height`, not the `0px` the dock's other
|
|
261
|
+
* branch would imply — plus 3.5rem to clear that control's touch
|
|
262
|
+
* box and its gap. Copying the expression rather than inventing one
|
|
263
|
+
* is what keeps the two stacked when the tier changes height.
|
|
264
|
+
*/
|
|
265
|
+
"bottom-[calc(var(--keyboard-inset-height,0px)+var(--bottom-nav-height,4rem)+var(--bottom-chrome-height,0px)+var(--appkit-space-4)+3.5rem)]",
|
|
266
|
+
"transition-[bottom] duration-300 ease-out",
|
|
267
|
+
].join(" "), "data-section": "back-to-cta", children: _jsxs(Button, { type: "button", variant: "primary", size: "sm", onClick: scrollToAnchor, "aria-label": `Back to ${primaryAction?.label ?? "the buy button"}`, children: [_jsx(ChevronUp, { "aria-hidden": "true", size: ICON_SIZE.sm }), `Back to ${primaryAction?.label ?? "buy"}`] }) }));
|
|
268
|
+
}
|
|
219
269
|
return (_jsxs(Div, { ref: containerRef, role: "toolbar", "aria-label": isBulkMode ? "Bulk actions" : "Page actions", className: [
|
|
220
270
|
"fixed z-[var(--appkit-z-overlay)] pointer-events-auto flex flex-col",
|
|
221
271
|
panelAlignClass,
|
|
@@ -23,6 +23,15 @@ const iconBtn = "flex items-center justify-center w-11 h-11 rounded-lg text-[var
|
|
|
23
23
|
* `important: true`, so the utility would beat the unlayered rule and the badge
|
|
24
24
|
* would silently stop mirroring. The vertical `-top-0.5` is hand-neutral and stays. */
|
|
25
25
|
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";
|
|
26
|
+
/**
|
|
27
|
+
* Applied to the GLYPH, never the link, when the icon is carrying something.
|
|
28
|
+
*
|
|
29
|
+
* On the glyph so the count badge stays put: animating the link would swing
|
|
30
|
+
* the number with it, which turns a readable badge into a moving target. The
|
|
31
|
+
* keyframes and the `prefers-reduced-motion` opt-out live in
|
|
32
|
+
* `TitleBarLayout.style.css`.
|
|
33
|
+
*/
|
|
34
|
+
const nudgeWhenCarrying = (count) => count > 0 ? "appkit-nav-icon--has-items" : "";
|
|
26
35
|
/**
|
|
27
36
|
* TitleBarLayout — generic top sticky title-bar shell.
|
|
28
37
|
*
|
|
@@ -50,13 +59,13 @@ export function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, s
|
|
|
50
59
|
// Dedicated notification bell — rendered immediately before the wishlist
|
|
51
60
|
// icon (per layout request). Falls back to invisible when no href is set;
|
|
52
61
|
// the count badge mirrors the wishlist/cart pattern.
|
|
53
|
-
const notificationsEl = notificationsHref ? (_jsxs(Link, { href: notificationsHref, "aria-label": `Notifications${unreadNotificationCount > 0 ? `, ${unreadNotificationCount} unread` : ""}`, className: `relative ${iconBtn}`, children: [_jsx(Icon, { name: "notification", size: "lg" }), unreadNotificationCount > 0 && (_jsx(Span, { className: countBadge, children: unreadNotificationCount > 99 ? "99+" : unreadNotificationCount }))] })) : null;
|
|
62
|
+
const notificationsEl = notificationsHref ? (_jsxs(Link, { href: notificationsHref, "aria-label": `Notifications${unreadNotificationCount > 0 ? `, ${unreadNotificationCount} unread` : ""}`, className: `relative ${iconBtn}`, children: [_jsx(Icon, { name: "notification", size: "lg", className: nudgeWhenCarrying(unreadNotificationCount) }), unreadNotificationCount > 0 && (_jsx(Span, { className: countBadge, children: unreadNotificationCount > 99 ? "99+" : unreadNotificationCount }))] })) : null;
|
|
54
63
|
// D9 — chat. Same shape as the notification bell: absent entirely when no
|
|
55
64
|
// href is supplied (the consumer only passes one when
|
|
56
65
|
// `siteSettings.chat.enabled === true`), never rendered-but-disabled.
|
|
57
|
-
const messagesEl = messagesHref ? (_jsxs(Link, { href: messagesHref, "aria-label": `Messages${unreadMessageCount > 0 ? `, ${unreadMessageCount} unread` : ""}`, className: `relative ${iconBtn}`, children: [_jsx(Icon, { name: "message", size: "lg" }), unreadMessageCount > 0 && (_jsx(Span, { className: countBadge, children: unreadMessageCount > 99 ? "99+" : unreadMessageCount }))] })) : null;
|
|
58
|
-
const wishlistEl = wishlistHref ? (_jsxs(Link, { href: wishlistHref, "aria-label": `Wishlist${wishlistCount > 0 ? `, ${wishlistCount} items` : ""}`, className: `relative ${iconBtn}`, "data-tour": "nav-wishlist", children: [_jsx(Icon, { name: "wishlist", size: "lg" }), wishlistCount > 0 && (_jsx(Span, { className: countBadge, children: wishlistCount > 9 ? "9+" : wishlistCount }))] })) : null;
|
|
59
|
-
const cartEl = cartHref ? (_jsxs(Link, { href: cartHref, "aria-label": `Cart${cartCount > 0 ? `, ${cartCount} items` : ""}`, className: `relative ${iconBtn}`, "data-tour": "nav-cart", children: [_jsx(Icon, { name: "cart", size: "lg" }), cartCount > 0 && (_jsx(Span, { className: countBadge, children: cartCount > 9 ? "9+" : cartCount }))] })) : null;
|
|
66
|
+
const messagesEl = messagesHref ? (_jsxs(Link, { href: messagesHref, "aria-label": `Messages${unreadMessageCount > 0 ? `, ${unreadMessageCount} unread` : ""}`, className: `relative ${iconBtn}`, children: [_jsx(Icon, { name: "message", size: "lg", className: nudgeWhenCarrying(unreadMessageCount) }), unreadMessageCount > 0 && (_jsx(Span, { className: countBadge, children: unreadMessageCount > 99 ? "99+" : unreadMessageCount }))] })) : null;
|
|
67
|
+
const wishlistEl = wishlistHref ? (_jsxs(Link, { href: wishlistHref, "aria-label": `Wishlist${wishlistCount > 0 ? `, ${wishlistCount} items` : ""}`, className: `relative ${iconBtn}`, "data-tour": "nav-wishlist", children: [_jsx(Icon, { name: "wishlist", size: "lg", className: nudgeWhenCarrying(wishlistCount) }), wishlistCount > 0 && (_jsx(Span, { className: countBadge, children: wishlistCount > 9 ? "9+" : wishlistCount }))] })) : null;
|
|
68
|
+
const cartEl = cartHref ? (_jsxs(Link, { href: cartHref, "aria-label": `Cart${cartCount > 0 ? `, ${cartCount} items` : ""}`, className: `relative ${iconBtn}`, "data-tour": "nav-cart", children: [_jsx(Icon, { name: "cart", size: "lg", className: nudgeWhenCarrying(cartCount) }), cartCount > 0 && (_jsx(Span, { className: countBadge, children: cartCount > 9 ? "9+" : cartCount }))] })) : null;
|
|
60
69
|
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;
|
|
61
70
|
const authButtonsEl = !user && (loginHref || registerHref) ? (
|
|
62
71
|
// Mirrors with TB1 so Register (the primary CTA) keeps the outer, more
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Title-bar icons that are carrying something.
|
|
3
|
+
*
|
|
4
|
+
* The count badge already states the fact, but it is a 16px dot in the corner
|
|
5
|
+
* of a dense bar — easy to miss on a phone, and it never changes once drawn.
|
|
6
|
+
* A small periodic nudge is what makes "you have three things in your cart"
|
|
7
|
+
* noticeable without adding a second place to read the number.
|
|
8
|
+
*
|
|
9
|
+
* 🛑 It is a PERIODIC nudge, not a loop. The movement lasts about half a
|
|
10
|
+
* second at the END of a 10s cycle and the icon is still for the other ~9.5s,
|
|
11
|
+
* so it reads as an occasional glance-catcher rather than a permanent
|
|
12
|
+
* animation sitting in the chrome of every page. A continuously-moving element
|
|
13
|
+
* in a fixed header is the kind of thing people learn to tune out — or leave.
|
|
14
|
+
*
|
|
15
|
+
* 🛑 The percentages and the duration are ONE number, not two. The keyframe
|
|
16
|
+
* stops are a fraction of the cycle, so changing `10s` alone silently
|
|
17
|
+
* stretches the nudge itself — at 20s the same stops would swing the bell over
|
|
18
|
+
* a full second, which reads as a glitch rather than a nudge. Rescale the
|
|
19
|
+
* stops with it: the window is 4.8% here, which is ~480ms of a 10s cycle.
|
|
20
|
+
*
|
|
21
|
+
* The pivot is near the TOP of the glyph so the motion reads as a bell swing
|
|
22
|
+
* rather than a wobble around the middle, which is what the notification bell
|
|
23
|
+
* wants and what the cart and wishlist tolerate.
|
|
24
|
+
*/
|
|
25
|
+
@keyframes appkit-nav-icon-nudge {
|
|
26
|
+
0%,
|
|
27
|
+
95.2%,
|
|
28
|
+
100% {
|
|
29
|
+
transform: rotate(0deg);
|
|
30
|
+
}
|
|
31
|
+
96.1% {
|
|
32
|
+
transform: rotate(-12deg);
|
|
33
|
+
}
|
|
34
|
+
97.3% {
|
|
35
|
+
transform: rotate(9deg);
|
|
36
|
+
}
|
|
37
|
+
98.5% {
|
|
38
|
+
transform: rotate(-5deg);
|
|
39
|
+
}
|
|
40
|
+
99.4% {
|
|
41
|
+
transform: rotate(2deg);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.appkit-nav-icon--has-items {
|
|
46
|
+
transform-origin: 50% 15%;
|
|
47
|
+
animation: appkit-nav-icon-nudge 10s ease-in-out infinite;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/*
|
|
51
|
+
* 🛑 Honoured, not approximated. This is decoration carrying no information
|
|
52
|
+
* the badge does not already carry, so for a reader who has asked for less
|
|
53
|
+
* motion the correct amount is none — not a shorter or gentler version.
|
|
54
|
+
*/
|
|
55
|
+
@media (prefers-reduced-motion: reduce) {
|
|
56
|
+
.appkit-nav-icon--has-items {
|
|
57
|
+
animation: none;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -21,4 +21,19 @@
|
|
|
21
21
|
* page that owns it mounts AFTER this dock — the dock lives in the app shell
|
|
22
22
|
* and the CTA arrives with the route.
|
|
23
23
|
*/
|
|
24
|
-
export
|
|
24
|
+
export interface CtaRevealState {
|
|
25
|
+
/** Should the floating dock / bar be on screen? */
|
|
26
|
+
revealed: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Does this page mark an inline CTA at all?
|
|
29
|
+
*
|
|
30
|
+
* The discriminator between a DETAIL page (a real Buy button exists, so the
|
|
31
|
+
* floating control's job is to take you back to it) and a BULK screen (no
|
|
32
|
+
* Buy button was ever there, so the floating control IS the only affordance
|
|
33
|
+
* and must carry the actions itself).
|
|
34
|
+
*/
|
|
35
|
+
hasAnchor: boolean;
|
|
36
|
+
/** Scroll the inline CTA back into view. A no-op when there is no anchor. */
|
|
37
|
+
scrollToAnchor: () => void;
|
|
38
|
+
}
|
|
39
|
+
export declare function useCtaReveal(enabled?: boolean, anchorId?: string): CtaRevealState;
|
|
@@ -1,29 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
3
|
import { CTA_ANCHOR_ATTR } from "./cta-anchor.js";
|
|
4
|
-
/**
|
|
5
|
-
* useCtaReveal — "has the user met the Buy button and scrolled past it?"
|
|
6
|
-
*
|
|
7
|
-
* Returns true when the floating action dock should be on screen:
|
|
8
|
-
*
|
|
9
|
-
* - the page marks NO CTA anchor → true immediately (nothing to wait
|
|
10
|
-
* for; an admin bulk bar has no Buy button)
|
|
11
|
-
* - the page marks one, not yet seen → false (the inline CTA is doing the
|
|
12
|
-
* job; a second copy of it beside itself is noise)
|
|
13
|
-
* - seen, and now out of view → true
|
|
14
|
-
* - seen, and scrolled back into view → false again
|
|
15
|
-
*
|
|
16
|
-
* 🛑 `IntersectionObserver`, never a scroll-position threshold. The bar this
|
|
17
|
-
* replaces used `window.scrollY > 400`, which is a guess about where a CTA sits
|
|
18
|
-
* — wrong on a short page (the dock appears while the real button is still on
|
|
19
|
-
* screen) and wrong on a long gallery (it appears before the CTA is reached).
|
|
20
|
-
* Observing the element itself is the same rule stated exactly, and it costs no
|
|
21
|
-
* scroll handler.
|
|
22
|
-
*
|
|
23
|
-
* The anchor is looked up on an interval-free `MutationObserver` because the
|
|
24
|
-
* page that owns it mounts AFTER this dock — the dock lives in the app shell
|
|
25
|
-
* and the CTA arrives with the route.
|
|
26
|
-
*/
|
|
27
4
|
export function useCtaReveal(enabled = true, anchorId) {
|
|
28
5
|
const [anchor, setAnchor] = useState(null);
|
|
29
6
|
const [anchorExists, setAnchorExists] = useState(false);
|
|
@@ -74,9 +51,23 @@ export function useCtaReveal(enabled = true, anchorId) {
|
|
|
74
51
|
io.observe(anchor);
|
|
75
52
|
return () => io.disconnect();
|
|
76
53
|
}, [anchor]);
|
|
54
|
+
/*
|
|
55
|
+
* Re-read at CLICK time rather than closing over `anchor`. A client-side
|
|
56
|
+
* navigation swaps the page under a dock that never unmounts, and the
|
|
57
|
+
* element captured when the effect last ran may already be detached — a
|
|
58
|
+
* `scrollIntoView` on a detached node silently does nothing.
|
|
59
|
+
*/
|
|
60
|
+
const scrollToAnchor = () => {
|
|
61
|
+
if (typeof document === "undefined")
|
|
62
|
+
return;
|
|
63
|
+
const el = anchorId
|
|
64
|
+
? document.getElementById(anchorId)
|
|
65
|
+
: document.querySelector(`[${CTA_ANCHOR_ATTR}]`);
|
|
66
|
+
el?.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
67
|
+
};
|
|
77
68
|
if (!enabled)
|
|
78
|
-
return false;
|
|
69
|
+
return { revealed: false, hasAnchor: anchorExists, scrollToAnchor };
|
|
79
70
|
if (!anchorExists)
|
|
80
|
-
return true;
|
|
81
|
-
return seen && !inView;
|
|
71
|
+
return { revealed: true, hasAnchor: false, scrollToAnchor };
|
|
72
|
+
return { revealed: seen && !inView, hasAnchor: true, scrollToAnchor };
|
|
82
73
|
}
|