@mohasinac/appkit 4.30.18 → 4.31.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/digital-code/CodeRevealPanel.js +5 -2
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +3 -3
- package/dist/_internal/client/features/layout/useSidebarSearch.d.ts +21 -5
- package/dist/_internal/client/features/layout/useSidebarSearch.js +27 -6
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +23 -3
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +106 -86
- package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +16 -5
- package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -40
- package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +5 -2
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +11 -3
- package/dist/_internal/client/hand-mode/HandModeProvider.js +2 -0
- package/dist/_internal/server/features/auctions/actions.js +14 -3
- package/dist/_internal/server/features/auctions/service.js +1 -1
- package/dist/_internal/server/features/auth/permissions.d.ts +2 -0
- package/dist/_internal/server/features/auth/permissions.js +3 -2
- package/dist/_internal/server/features/blog/data.js +2 -2
- package/dist/_internal/server/features/blog/service.js +3 -3
- package/dist/_internal/server/features/brands/data.js +1 -1
- package/dist/_internal/server/features/brands/service.js +3 -3
- package/dist/_internal/server/features/bundles/actions.js +6 -1
- package/dist/_internal/server/features/bundles/data.js +21 -4
- package/dist/_internal/server/features/cart/data.js +3 -1
- package/dist/_internal/server/features/cart/service.js +13 -2
- package/dist/_internal/server/features/categories/data.js +26 -6
- package/dist/_internal/server/features/classified/index.d.ts +0 -1
- package/dist/_internal/server/features/classified/index.js +0 -1
- package/dist/_internal/server/features/digital-code/index.d.ts +0 -1
- package/dist/_internal/server/features/digital-code/index.js +0 -1
- package/dist/_internal/server/features/events/actions.js +17 -4
- package/dist/_internal/server/features/events/data.js +1 -1
- package/dist/_internal/server/features/events/service.js +1 -1
- package/dist/_internal/server/features/grouped/data.js +7 -2
- package/dist/_internal/server/features/item-requests/data.js +1 -1
- package/dist/_internal/server/features/live/index.d.ts +0 -1
- package/dist/_internal/server/features/live/index.js +0 -1
- package/dist/_internal/server/features/lottery/actions.js +2 -2
- package/dist/_internal/server/features/lottery/data.js +1 -1
- package/dist/_internal/server/features/orders/actions.js +7 -7
- package/dist/_internal/server/features/orders/data.d.ts +1 -1
- package/dist/_internal/server/features/orders/data.js +14 -2
- package/dist/_internal/server/features/orders/service.js +1 -1
- package/dist/_internal/server/features/pre-orders/service.js +1 -1
- package/dist/_internal/server/features/prize-draws/actions.js +1 -1
- package/dist/_internal/server/features/products/data.js +21 -6
- package/dist/_internal/server/features/products/list-public.d.ts +9 -0
- package/dist/_internal/server/features/products/list-public.js +95 -7
- package/dist/_internal/server/features/products/service.js +1 -1
- package/dist/_internal/server/features/promotions/actions.js +1 -1
- package/dist/_internal/server/features/promotions/data.js +1 -1
- package/dist/_internal/server/features/promotions/service.js +1 -1
- package/dist/_internal/server/features/reviews/data.d.ts +1 -1
- package/dist/_internal/server/features/reviews/data.js +6 -1
- package/dist/_internal/server/features/reviews/og-data.js +1 -1
- package/dist/_internal/server/features/shared/listing-data-factory.js +7 -2
- package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
- package/dist/_internal/server/features/site-settings/adapters.js +8 -5
- package/dist/_internal/server/features/stores/data.js +1 -1
- package/dist/_internal/server/features/sublisting-categories/data.js +1 -1
- package/dist/_internal/server/features/tester/visibility.d.ts +2 -1
- package/dist/_internal/server/features/tester/visibility.js +2 -2
- package/dist/_internal/server/functions/scheduled.d.ts +3 -1
- package/dist/_internal/server/functions/scheduled.js +27 -1
- package/dist/_internal/server/jobs/core/auctionSettlement.js +6 -1
- package/dist/_internal/server/jobs/core/hardBanCascade.js +4 -0
- package/dist/_internal/server/jobs/core/index.d.ts +2 -0
- package/dist/_internal/server/jobs/core/index.js +2 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +7 -15
- package/dist/_internal/server/jobs/core/pageViewPrune.d.ts +10 -0
- package/dist/_internal/server/jobs/core/pageViewPrune.js +44 -0
- package/dist/_internal/server/jobs/core/pageViewRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/core/pageViewRollup.js +87 -0
- package/dist/_internal/server/jobs/core/revenueRollup.js +7 -1
- package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/index.js +2 -0
- package/dist/_internal/server/jobs/handlers/pageViewPrune.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/pageViewPrune.js +2 -0
- package/dist/_internal/server/jobs/handlers/pageViewRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/pageViewRollup.js +2 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +69 -22
- package/dist/_internal/shared/checkout/rules/classified.rule.d.ts +9 -4
- package/dist/_internal/shared/checkout/rules/classified.rule.js +7 -2
- package/dist/_internal/shared/features/layout/matchesNavQuery.d.ts +14 -0
- package/dist/_internal/shared/features/layout/matchesNavQuery.js +72 -0
- package/dist/_internal/shared/features/layout/types.d.ts +37 -1
- package/dist/_internal/shared/features/layout/types.js +19 -1
- package/dist/_internal/shared/features/notifications/action-url.d.ts +9 -1
- package/dist/_internal/shared/features/notifications/action-url.js +47 -0
- package/dist/_internal/shared/features/offers/config.d.ts +65 -0
- package/dist/_internal/shared/features/offers/config.js +73 -2
- package/dist/_internal/shared/features/orders/schema.d.ts +19 -1
- package/dist/_internal/shared/features/orders/schema.js +22 -2
- package/dist/_internal/shared/features/site-settings/types.d.ts +12 -3
- package/dist/_internal/shared/listing-types/capabilities.d.ts +8 -2
- package/dist/_internal/shared/listing-types/capabilities.js +6 -1
- package/dist/_internal/shared/listing-types/classified/ctas.js +6 -1
- package/dist/_internal/shared/listing-types/feature-flags.d.ts +10 -5
- package/dist/_internal/shared/listing-types/feature-flags.js +11 -6
- package/dist/client/api/useApiQuery.js +17 -6
- package/dist/client.d.ts +38 -20
- package/dist/client.js +39 -14
- package/dist/configs/next.js +10 -2
- package/dist/constants/api-endpoints.d.ts +18 -54
- package/dist/constants/api-endpoints.js +6 -26
- package/dist/constants/field-names.d.ts +6 -24
- package/dist/constants/field-names.js +10 -27
- package/dist/constants/geo/countries.d.ts +50 -0
- package/dist/constants/geo/countries.js +129 -0
- package/dist/constants/geo/subdivisions.d.ts +13 -0
- package/dist/constants/geo/subdivisions.js +104 -0
- package/dist/constants/index.d.ts +2 -2
- package/dist/constants/index.js +2 -2
- package/dist/constants/table-keys.d.ts +24 -0
- package/dist/constants/table-keys.js +28 -0
- package/dist/contracts/client-realtime.d.ts +6 -0
- package/dist/contracts/extend.d.ts +0 -9
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/realtime-session.d.ts +12 -12
- package/dist/contracts/realtime-session.js +79 -77
- package/dist/core/browser.d.ts +1 -1
- package/dist/core/browser.js +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/site-config.d.ts +0 -3
- package/dist/core/site-config.js +0 -3
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/errors/messages.d.ts +0 -8
- package/dist/errors/messages.js +0 -9
- package/dist/features/about/components/DeveloperView.js +7 -2
- package/dist/features/about/components/PolicyPageView.js +9 -1
- package/dist/features/about/components/PublicProfileView.js +23 -5
- package/dist/features/about/schemas/index.d.ts +8 -8
- package/dist/features/account/components/AddressFilters.js +32 -11
- package/dist/features/account/components/AddressForm.d.ts +26 -25
- package/dist/features/account/components/AddressForm.js +81 -103
- package/dist/features/account/components/AddressesIndexListing.js +8 -4
- package/dist/features/account/components/NotificationBell.js +4 -3
- package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
- package/dist/features/account/components/UserOffersPanel.js +1 -1
- package/dist/features/account/components/UserSidebar.d.ts +3 -4
- package/dist/features/account/components/UserSupportView.js +89 -58
- package/dist/features/account/components/index.d.ts +1 -7
- package/dist/features/account/components/index.js +0 -3
- package/dist/features/account/hooks/useAddresses.d.ts +4 -3
- package/dist/features/account/hooks/useAddresses.js +4 -6
- package/dist/features/account/hooks/useCollapsedSections.d.ts +51 -10
- package/dist/features/account/hooks/useCollapsedSections.js +211 -38
- package/dist/features/account/hooks/useProfile.d.ts +8 -0
- package/dist/features/account/index.d.ts +0 -3
- package/dist/features/account/index.js +0 -3
- package/dist/features/account/schemas/index.d.ts +0 -92
- package/dist/features/account/schemas/index.js +42 -20
- package/dist/features/addresses/components/AddressLocationFields.d.ts +13 -0
- package/dist/features/addresses/components/AddressLocationFields.js +41 -0
- package/dist/features/addresses/hooks/usePostalLookup.d.ts +17 -0
- package/dist/features/addresses/hooks/usePostalLookup.js +133 -0
- package/dist/features/addresses/schemas/address-form.d.ts +423 -0
- package/dist/features/addresses/schemas/address-form.js +212 -0
- package/dist/features/addresses/schemas/index.d.ts +1 -0
- package/dist/features/addresses/schemas/index.js +8 -0
- package/dist/features/admin/actions/admin-read-actions.d.ts +1 -1
- package/dist/features/admin/actions/index.d.ts +0 -1
- package/dist/features/admin/actions/index.js +0 -1
- package/dist/features/admin/actions/notification-actions.js +1 -41
- package/dist/features/admin/components/AdminActionIndexView.d.ts +32 -0
- package/dist/features/admin/components/AdminActionIndexView.js +128 -0
- package/dist/features/admin/components/AdminAdEditorView.js +159 -163
- package/dist/features/admin/components/AdminAddressEditorView.js +97 -55
- package/dist/features/admin/components/AdminBrandEditorView.d.ts +15 -0
- package/dist/features/admin/components/AdminBrandEditorView.js +113 -77
- package/dist/features/admin/components/AdminBundleEditorView.d.ts +19 -9
- package/dist/features/admin/components/AdminBundleEditorView.js +136 -168
- package/dist/features/admin/components/AdminCategoryEditorView.js +120 -43
- package/dist/features/admin/components/AdminCouponEditorView.js +145 -109
- package/dist/features/admin/components/AdminEmployeeEditorView.js +125 -56
- package/dist/features/admin/components/AdminFaqEditorView.js +112 -58
- package/dist/features/admin/components/AdminFeatureEditorView.js +108 -68
- package/dist/features/admin/components/AdminFulfillmentView.js +5 -2
- package/dist/features/admin/components/AdminMediaView.js +3 -3
- package/dist/features/admin/components/AdminNavEditorView.js +69 -44
- package/dist/features/admin/components/AdminOrderEditorView.js +47 -10
- package/dist/features/admin/components/AdminPrizeDrawsView.js +16 -2
- package/dist/features/admin/components/AdminSidebar.d.ts +11 -7
- package/dist/features/admin/components/AdminSiteSettingsView.js +37 -37
- package/dist/features/admin/components/AdminStoreEditorView.js +75 -52
- package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +71 -30
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +95 -65
- package/dist/features/admin/components/BrandQuickCreateForm.d.ts +10 -0
- package/dist/features/admin/components/BrandQuickCreateForm.js +49 -26
- package/dist/features/admin/components/CategoryQuickCreateForm.d.ts +11 -0
- package/dist/features/admin/components/CategoryQuickCreateForm.js +48 -26
- package/dist/features/admin/components/index.d.ts +2 -2
- package/dist/features/admin/components/index.js +1 -1
- package/dist/features/admin/constants/site-settings-tabs.d.ts +120 -0
- package/dist/features/admin/constants/site-settings-tabs.js +152 -0
- package/dist/features/admin/index.d.ts +0 -3
- package/dist/features/admin/index.js +0 -2
- package/dist/features/admin/repository/analytics-rollup.repository.d.ts +35 -0
- package/dist/features/admin/repository/analytics-rollup.repository.js +30 -0
- package/dist/features/admin/schemas/admin-editor-forms.d.ts +56 -57
- package/dist/features/admin/schemas/admin-editor-forms.js +97 -54
- package/dist/features/admin/schemas/admin-ops-forms.d.ts +57 -5
- package/dist/features/admin/schemas/admin-ops-forms.js +104 -20
- package/dist/features/admin/schemas/admin-user-form.js +7 -0
- package/dist/features/admin/schemas/firestore.d.ts +48 -95
- package/dist/features/admin/schemas/firestore.js +15 -151
- package/dist/features/admin/schemas/nav-item-form.js +9 -2
- package/dist/features/admin/server.d.ts +0 -4
- package/dist/features/admin/server.js +0 -2
- package/dist/features/admin/types/index.d.ts +0 -7
- package/dist/features/analytics/repository/page-views.repository.d.ts +14 -0
- package/dist/features/analytics/repository/page-views.repository.js +24 -0
- package/dist/features/analytics/types.d.ts +1 -1
- package/dist/features/analytics/types.js +13 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +6 -2
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +3 -1
- package/dist/features/auth/actions/index.d.ts +0 -1
- package/dist/features/auth/actions/index.js +0 -1
- package/dist/features/auth/actions/profile-actions.js +4 -3
- package/dist/features/auth/actions/realtime-token-actions.js +1 -12
- package/dist/features/auth/auth-helpers.js +5 -7
- package/dist/features/auth/components/ForgotPasswordView.js +1 -1
- package/dist/features/auth/components/Guards.js +2 -1
- package/dist/features/auth/components/LoginForm.d.ts +0 -2
- package/dist/features/auth/components/LoginForm.js +2 -4
- package/dist/features/auth/components/RegisterForm.js +62 -19
- package/dist/features/auth/components/ResetPasswordView.js +1 -1
- package/dist/features/auth/hooks/useAuth.js +3 -0
- package/dist/features/auth/permissions/constants.d.ts +27 -1
- package/dist/features/auth/role-predicates.d.ts +75 -7
- package/dist/features/auth/role-predicates.js +28 -2
- package/dist/features/auth/schemas/firestore.d.ts +22 -0
- package/dist/features/auth/schemas/index.d.ts +1 -1
- package/dist/features/auth/schemas/index.js +44 -8
- package/dist/features/auth/types/index.d.ts +14 -1
- package/dist/features/blog/api/route.js +10 -2
- package/dist/features/blog/components/BlogIndexListing.js +1 -1
- package/dist/features/blog/hooks/useBlog.js +2 -2
- package/dist/features/blog/repository/blog.repository.js +3 -1
- package/dist/features/blog/schemas/index.d.ts +4 -0
- package/dist/features/blog/schemas/index.js +2 -0
- package/dist/features/blog/types/index.d.ts +1 -1
- package/dist/features/cart/actions/cart-actions.js +13 -4
- package/dist/features/cart/utils/pending-ops.js +1 -1
- package/dist/features/catalogue/components/CatalogueItemEditorView.d.ts +22 -0
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +112 -52
- package/dist/features/catalogue/schemas/validation.d.ts +4 -4
- package/dist/features/catalogue/schemas/validation.js +47 -10
- package/dist/features/categories/components/BrandDetailPageView.js +28 -12
- package/dist/features/categories/components/CategoryDetailPageView.js +37 -20
- package/dist/features/categories/repository/categories.repository.js +3 -1
- package/dist/features/categories/schemas/bundle-form.d.ts +142 -0
- package/dist/features/categories/schemas/bundle-form.js +149 -0
- package/dist/features/checkout/actions/checkout-value-otp-actions.js +10 -1
- package/dist/features/classified/components/ClassifiedContactSellerPanel.js +1 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +5 -4
- package/dist/features/classified/components/ClassifiedDetailPageView.js +47 -18
- package/dist/features/consultation/components/ConsultationForm.js +1 -1
- package/dist/features/consultation/schemas/index.js +34 -10
- package/dist/features/corporate/schemas/index.js +36 -15
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +4 -1
- package/dist/features/events/components/EventBanner.js +5 -2
- package/dist/features/events/components/EventOfferCard.js +3 -1
- package/dist/features/events/components/EventPollWidget.js +1 -1
- package/dist/features/events/components/SpinWheelView.js +3 -2
- package/dist/features/filters/FilterPanel.js +3 -3
- package/dist/features/filters/RangeFilter.d.ts +10 -0
- package/dist/features/filters/RangeFilter.js +41 -4
- package/dist/features/grouped/components/GroupedListingEditorView.js +2 -1
- package/dist/features/history/hooks/useHistory.js +29 -27
- package/dist/features/homepage/components/AdSlot.js +3 -1
- package/dist/features/homepage/components/GoogleReviewsSection.js +8 -2
- package/dist/features/homepage/components/MarketplaceHomepageView.js +58 -15
- package/dist/features/homepage/components/SocialFeedSection.js +4 -1
- package/dist/features/homepage/hooks/useBlogArticles.js +1 -1
- package/dist/features/homepage/lib/live-stats.js +8 -1
- package/dist/features/homepage/repository/carousel.repository.js +2 -1
- package/dist/features/layout/BottomActions.js +84 -11
- package/dist/features/layout/BottomActionsContext.d.ts +1 -6
- package/dist/features/layout/BottomActionsContext.js +1 -1
- package/dist/features/layout/hooks/useBottomActions.js +2 -2
- package/dist/features/layout/hooks/useFormBottomActions.d.ts +12 -1
- package/dist/features/layout/hooks/useFormBottomActions.js +12 -6
- package/dist/features/live/components/LiveItemDetailPageView.js +4 -1
- package/dist/features/lottery/schemas/config-write.d.ts +9 -5
- package/dist/features/lottery/schemas/config-write.js +69 -9
- package/dist/features/media/finalize.js +3 -1
- package/dist/features/media/modals/ImageEditor.js +1 -1
- package/dist/features/media/modals/VideoThumbnailSelector.js +5 -2
- package/dist/features/media/modals/VideoTrimModal.js +5 -2
- package/dist/features/media/upload/MediaUploadField.js +2 -2
- package/dist/features/messages/hooks/useConversation.js +18 -4
- package/dist/features/messages/hooks/useConversations.js +25 -8
- package/dist/features/orders/components/RefundRequestView.js +5 -2
- package/dist/features/orders/schemas/buyer-forms.d.ts +6 -3
- package/dist/features/orders/schemas/buyer-forms.js +31 -3
- package/dist/features/orders/schemas/index.d.ts +2 -2
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -1
- package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +4 -1
- package/dist/features/products/components/GroupSettingsPanel.js +3 -1
- package/dist/features/products/components/MakeOfferButton.d.ts +13 -2
- package/dist/features/products/components/MakeOfferButton.js +33 -11
- package/dist/features/products/components/PrizeDrawDetailPageView.js +4 -1
- package/dist/features/products/components/ProductDetailActions.js +0 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +7 -3
- package/dist/features/products/components/ProductDetailPageView.js +16 -7
- package/dist/features/products/components/ShareButton.js +3 -1
- package/dist/features/products/constants/action-defs.d.ts +1 -2
- package/dist/features/products/constants/action-defs.js +40 -43
- package/dist/features/products/repository/loadProductFeatures.js +10 -2
- package/dist/features/products/repository/products.repository.d.ts +5 -1
- package/dist/features/products/repository/products.repository.js +25 -2
- package/dist/features/products/schemas/firestore.d.ts +7 -3
- package/dist/features/products/schemas/product-features.validators.js +3 -0
- package/dist/features/promotions/components/CouponCard.js +11 -8
- package/dist/features/reviews/actions/review-actions.js +8 -1
- package/dist/features/reviews/components/ReviewDetailPageView.js +13 -3
- package/dist/features/reviews/components/ReviewDetailShell.js +3 -1
- package/dist/features/scams/actions/scam-actions.d.ts +9 -1
- package/dist/features/scams/actions/scam-actions.js +52 -12
- package/dist/features/scams/components/SellerTrustBadge.d.ts +1 -1
- package/dist/features/scams/components/SellerTrustBadge.js +13 -0
- package/dist/features/scams/repository/scammer.repository.js +2 -1
- package/dist/features/scams/schemas/firestore.d.ts +15 -1
- package/dist/features/scams/schemas/report-form.d.ts +5 -5
- package/dist/features/scams/schemas/report-form.js +18 -3
- package/dist/features/search/action-index/derive.d.ts +49 -0
- package/dist/features/search/action-index/derive.js +121 -0
- package/dist/features/search/action-index/repository.d.ts +42 -0
- package/dist/features/search/action-index/repository.js +100 -0
- package/dist/features/search/action-index/settings-entries.d.ts +41 -0
- package/dist/features/search/action-index/settings-entries.js +268 -0
- package/dist/features/search/action-index/types.d.ts +81 -0
- package/dist/features/search/action-index/types.js +102 -0
- package/dist/features/search/action-index/validate-entry.d.ts +34 -0
- package/dist/features/search/action-index/validate-entry.js +114 -0
- package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
- package/dist/features/search/hooks/useNavSuggestions.js +23 -3
- package/dist/features/search/repository/search.repository.js +3 -1
- package/dist/features/seller/actions/offer-actions.js +43 -19
- package/dist/features/seller/actions/seller-actions.js +29 -5
- package/dist/features/seller/components/CategoryChipPicker.d.ts +9 -0
- package/dist/features/seller/components/CategoryChipPicker.js +12 -0
- package/dist/features/seller/components/FulfillmentView.js +5 -2
- package/dist/features/seller/components/PhysicalLocationModal.js +1 -1
- package/dist/features/seller/components/PrintCenterView.js +3 -1
- package/dist/features/seller/components/SellerAddressesView.js +5 -1
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +83 -43
- package/dist/features/seller/components/SellerArtView.d.ts +14 -7
- package/dist/features/seller/components/SellerArtView.js +4 -119
- package/dist/features/seller/components/SellerBidsView.js +1 -1
- package/dist/features/seller/components/SellerClassifiedView.d.ts +14 -7
- package/dist/features/seller/components/SellerClassifiedView.js +4 -124
- package/dist/features/seller/components/SellerCouponEditorView.d.ts +22 -1
- package/dist/features/seller/components/SellerCouponEditorView.js +91 -45
- package/dist/features/seller/components/SellerCouponsView.js +1 -1
- package/dist/features/seller/components/SellerDigitalCodesView.d.ts +14 -7
- package/dist/features/seller/components/SellerDigitalCodesView.js +4 -126
- package/dist/features/seller/components/SellerGoogleReviewsView.js +2 -2
- package/dist/features/seller/components/SellerListingTypeView.d.ts +66 -0
- package/dist/features/seller/components/SellerListingTypeView.js +277 -0
- package/dist/features/seller/components/SellerLiveView.d.ts +14 -7
- package/dist/features/seller/components/SellerLiveView.js +4 -126
- package/dist/features/seller/components/SellerOrdersView.js +3 -3
- package/dist/features/seller/components/SellerPayoutMethodsView.js +2 -2
- package/dist/features/seller/components/SellerProductShell.d.ts +5 -0
- package/dist/features/seller/components/SellerProductShell.js +15 -5
- package/dist/features/seller/components/SellerProductsView.js +24 -10
- package/dist/features/seller/components/SellerReviewsView.js +47 -16
- package/dist/features/seller/components/SellerShippingConfigsView.js +5 -4
- package/dist/features/seller/components/SellerSidebar.d.ts +3 -6
- package/dist/features/seller/components/SellerStickersView.d.ts +14 -7
- package/dist/features/seller/components/SellerStickersView.js +4 -119
- package/dist/features/seller/components/SellerStoreCategoriesView.js +3 -2
- package/dist/features/seller/components/SellerSupportView.d.ts +31 -0
- package/dist/features/seller/components/SellerSupportView.js +100 -0
- package/dist/features/seller/components/index.d.ts +4 -8
- package/dist/features/seller/components/index.js +2 -4
- package/dist/features/seller/index.d.ts +0 -1
- package/dist/features/seller/index.js +0 -1
- package/dist/features/seller/schemas/coupon-form.d.ts +214 -0
- package/dist/features/seller/schemas/coupon-form.js +155 -23
- package/dist/features/seller/schemas/offer-forms.d.ts +16 -7
- package/dist/features/seller/schemas/offer-forms.js +28 -12
- package/dist/features/shell/FormShell.js +2 -2
- package/dist/features/shell/QuickFormDrawer.js +2 -2
- package/dist/features/shell/SectionForm.d.ts +19 -1
- package/dist/features/shell/SectionForm.js +104 -17
- package/dist/features/shell/build-sections.d.ts +23 -0
- package/dist/features/shell/build-sections.js +106 -8
- package/dist/features/shell/field-ui-meta.d.ts +44 -0
- package/dist/features/shell/field-ui-meta.js +14 -7
- package/dist/features/shell/form-sections.d.ts +46 -0
- package/dist/features/shell/form-sections.js +114 -0
- package/dist/features/shipments/components/AdminShipmentEditorView.js +87 -64
- package/dist/features/shipments/schemas/validation.d.ts +2 -2
- package/dist/features/shipments/schemas/validation.js +44 -13
- package/dist/features/store-extensions/schemas/custom-role-form.js +6 -2
- package/dist/features/store-extensions/schemas/listing-template-form.d.ts +7 -7
- package/dist/features/store-extensions/schemas/payout-method-form.js +14 -0
- package/dist/features/stores/actions/store-query-actions.js +3 -2
- package/dist/features/stores/api/[storeSlug]/auctions/route.js +3 -1
- package/dist/features/stores/api/[storeSlug]/products/route.js +3 -1
- package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +12 -3
- package/dist/features/support/repository/support.repository.d.ts +21 -0
- package/dist/features/support/repository/support.repository.js +63 -0
- package/dist/features/support/schemas/firestore.d.ts +27 -2
- package/dist/features/support/schemas/firestore.js +2 -0
- package/dist/features/support/schemas/ticket-create-form.js +7 -0
- package/dist/features/tester/schemas/checklist-item-form.d.ts +43 -0
- package/dist/features/tester/schemas/checklist-item-form.js +109 -0
- package/dist/features/tester/seed-data/blog-tester-seed-data.js +2 -2
- package/dist/features/tester/seed-data/categories-tester-seed-data.js +2 -2
- package/dist/features/tester/seed-data/events-tester-seed-data.js +5 -5
- package/dist/features/tester/seed-data/orders-tester-seed-data.js +2 -2
- package/dist/features/tester/seed-data/products-tester-seed-data.js +48 -49
- package/dist/features/tester/seed-data/stores-tester-seed-data.js +3 -3
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +291 -30
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +51 -14
- package/dist/features/wishlist/hooks/useWishlistToggle.js +8 -1
- package/dist/http/api-handler.js +2 -1
- package/dist/index.d.ts +25 -104
- package/dist/index.js +38 -143
- package/dist/jobs.d.ts +1 -1
- package/dist/jobs.js +1 -1
- package/dist/monitoring/browser.d.ts +0 -2
- package/dist/monitoring/browser.js +12 -1
- package/dist/monitoring/index.d.ts +0 -2
- package/dist/monitoring/index.js +0 -1
- package/dist/next/api/routeHandler.js +9 -4
- package/dist/next/index.d.ts +1 -2
- package/dist/next/index.js +0 -1
- package/dist/next/middleware/index.d.ts +0 -2
- package/dist/next/middleware/index.js +0 -1
- package/dist/next/routing/route-map.d.ts +8 -7
- package/dist/next/routing/route-map.js +10 -3
- package/dist/providers/auth-firebase/session.js +3 -1
- package/dist/providers/db-firebase/admin-app-lite.js +8 -8
- package/dist/providers/db-firebase/admin.js +27 -13
- package/dist/providers/db-firebase/browser-stub.d.ts +1 -11
- package/dist/providers/db-firebase/browser-stub.js +6 -11
- package/dist/providers/db-firebase/index.d.ts +0 -1
- package/dist/providers/db-firebase/index.js +0 -1
- package/dist/providers/db-firebase/rtdb-paths.d.ts +15 -6
- package/dist/providers/db-firebase/rtdb-paths.js +15 -6
- package/dist/providers/db-firebase/rtdb-url.d.ts +27 -0
- package/dist/providers/db-firebase/rtdb-url.js +50 -0
- package/dist/providers/db-firebase/sieve.d.ts +22 -0
- package/dist/providers/firebase-client/realtime.d.ts +20 -0
- package/dist/providers/firebase-client/realtime.js +34 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +1 -1
- package/dist/react/hooks/use-action-dispatch.js +8 -2
- package/dist/react/hooks/useFormStatePreservation.js +3 -1
- package/dist/react/hooks/useListingTypeFlags.d.ts +1 -1
- package/dist/react/hooks/useListingTypeFlags.js +1 -1
- package/dist/react/hooks/usePendingFilters.js +5 -3
- package/dist/react/hooks/usePendingTable.js +15 -3
- package/dist/react/hooks/useRealtimeEvent.js +20 -9
- package/dist/repositories/index.d.ts +0 -1
- package/dist/repositories/index.js +0 -1
- package/dist/schemas/registry.d.ts +197 -232
- package/dist/schemas/registry.js +5 -6
- package/dist/schemas/webhooks/razorpay.d.ts +50 -50
- package/dist/security/authorization.d.ts +9 -0
- package/dist/security/authorization.js +16 -2
- package/dist/security/browser.d.ts +1 -3
- package/dist/security/browser.js +5 -2
- package/dist/security/index.d.ts +1 -3
- package/dist/security/index.js +19 -2
- package/dist/security/pii-encrypt.js +16 -0
- package/dist/security/pii-schemas.d.ts +0 -14
- package/dist/security/pii-schemas.js +7 -15
- package/dist/seed/_helpers/category-forest.js +2 -2
- package/dist/seed/_helpers/media.d.ts +19 -0
- package/dist/seed/_helpers/media.js +75 -0
- package/dist/seed/blog-posts-seed-data.js +17 -17
- package/dist/seed/carousel-slides-seed-data.js +7 -7
- package/dist/seed/cart-seed-data.js +13 -13
- package/dist/seed/categories-seed-data.js +10 -10
- package/dist/seed/events-seed-data.js +24 -24
- package/dist/seed/factories/product.factory.js +4 -4
- package/dist/seed/factories/store.factory.js +3 -3
- package/dist/seed/factories/user.factory.js +2 -2
- package/dist/seed/grouped-listings-seed-data.js +11 -11
- package/dist/seed/homepage-sections-seed-data.js +2 -2
- package/dist/seed/index.d.ts +0 -1
- package/dist/seed/index.js +0 -1
- package/dist/seed/manifest.d.ts +1 -1
- package/dist/seed/manifest.js +0 -5
- package/dist/seed/notifications-seed-data.js +20 -1
- package/dist/seed/orders-seed-data.js +2 -2
- package/dist/seed/products-art-seed-data.js +16 -16
- package/dist/seed/products-auctions-seed-data.js +34 -34
- package/dist/seed/products-classifieds-seed-data.js +35 -37
- package/dist/seed/products-digital-codes-seed-data.js +20 -20
- package/dist/seed/products-live-items-seed-data.js +11 -11
- package/dist/seed/products-preorders-seed-data.js +13 -13
- package/dist/seed/products-prize-draws-seed-data.js +43 -43
- package/dist/seed/products-standard-seed-data.js +54 -54
- package/dist/seed/products-stickers-seed-data.js +15 -15
- package/dist/seed/reviews-seed-data.js +4 -4
- package/dist/seed/scammers-seed-data.js +3 -3
- package/dist/seed/site-settings-seed-data.js +11 -24
- package/dist/seed/store-extensions-seed-data.js +10 -10
- package/dist/seed/stores-seed-data.js +11 -11
- package/dist/seed/support-tickets-seed-data.d.ts +14 -0
- package/dist/seed/support-tickets-seed-data.js +20 -1
- package/dist/seed/users-seed-data.js +20 -20
- package/dist/server-entry.d.ts +0 -1
- package/dist/server-entry.js +0 -1
- package/dist/server.d.ts +9 -6
- package/dist/server.js +7 -13
- package/dist/styles.css +248 -68
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +2 -2
- package/dist/tokens/themes/default-light.js +2 -2
- package/dist/tokens/tokens.css +24 -4
- package/dist/ui/components/ActionRow.d.ts +50 -0
- package/dist/ui/components/ActionRow.js +53 -0
- package/dist/ui/components/ActionRow.style.css +145 -0
- package/dist/ui/components/Drawer.d.ts +10 -0
- package/dist/ui/components/Drawer.js +2 -1
- package/dist/ui/components/FilterDrawer.js +4 -1
- package/dist/ui/components/FilterDrawer.style.css +6 -12
- package/dist/ui/components/FormActionBar.js +14 -1
- package/dist/ui/components/FormActionBar.style.css +12 -11
- package/dist/ui/components/ListingLayout.js +8 -1
- package/dist/ui/components/ListingLayout.style.css +6 -13
- package/dist/ui/components/Modal.d.ts +15 -1
- package/dist/ui/components/Modal.js +17 -3
- package/dist/ui/components/QuickCreateModal.js +2 -2
- package/dist/ui/components/QuickCreateModal.style.css +78 -84
- package/dist/ui/components/RecordDetailModal.js +2 -2
- package/dist/ui/components/SideDrawer.d.ts +8 -0
- package/dist/ui/components/SideDrawer.js +2 -1
- package/dist/ui/components/SideDrawer.style.css +6 -3
- package/dist/ui/components/SideModal.d.ts +11 -1
- package/dist/ui/components/SideModal.js +3 -2
- package/dist/ui/components/SideModal.style.css +14 -0
- package/dist/ui/components/Typography.style.css +33 -24
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/components/toolbar-collapse.js +1 -2
- package/dist/ui/forms/FormShell.style.css +260 -311
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -1
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/schemas.d.ts +0 -19
- package/dist/validation/schemas.js +8 -19
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +3 -6
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: "Searching 'maintenance' should land me on the maintenance toggle" —
|
|
3
|
+
* and `Search.tsx` has had a **complete** quick-links path the whole
|
|
4
|
+
* time. Both modes are built: the inline filter already matches
|
|
5
|
+
* `link.keywords` as well as the label, the overlay renders a labelled
|
|
6
|
+
* band, the keyboard index arithmetic accounts for it, and
|
|
7
|
+
* `constants/search.ts` even holds the band's label string.
|
|
8
|
+
*
|
|
9
|
+
* `grep "quickLinks="` returns **zero**. The one real mount passes nine
|
|
10
|
+
* props and not that one. A finished feature with no input.
|
|
11
|
+
*
|
|
12
|
+
* The reason nothing passed it is that there was nothing to pass: no
|
|
13
|
+
* single list of what a user can DO existed. Nav lived in three arrays,
|
|
14
|
+
* quick actions in a registry each page ignored, and settings nowhere at
|
|
15
|
+
* all.
|
|
16
|
+
* WHAT: `ActionIndexEntry` — one shape for every navigable or actionable
|
|
17
|
+
* surface, and the type the search, the command palette and (D7) the
|
|
18
|
+
* admin control plane all read.
|
|
19
|
+
*
|
|
20
|
+
* ## Why `iconName` is a string
|
|
21
|
+
*
|
|
22
|
+
* The index is serialised — into a Firestore control document, through an API
|
|
23
|
+
* route, into React Query's cache. A `LucideIcon` component reference survives
|
|
24
|
+
* none of those. The renderer resolves the name; the index carries data.
|
|
25
|
+
*
|
|
26
|
+
* ## Why `href` and every permission field are CODE-owned
|
|
27
|
+
*
|
|
28
|
+
* D7 lets an admin rename, re-describe, retag, reorder and hide an entry —
|
|
29
|
+
* that is content. It does not let them repoint a built-in entry's route or
|
|
30
|
+
* relax its permission, because those are routing and access control. An
|
|
31
|
+
* admin-authored entry supplies its own `href`, and that one is validated at
|
|
32
|
+
* SAVE time (see the write route): an audit that runs on CI would let a bad
|
|
33
|
+
* href reach the sidebar and 404 until the next run.
|
|
34
|
+
*
|
|
35
|
+
* EXPORTS: ActionIndexEntry, ActionIndexKind, ActionIndexControl,
|
|
36
|
+
* type ActionIndexOverride
|
|
37
|
+
*
|
|
38
|
+
* @tag domain:search
|
|
39
|
+
* @tag layer:shared
|
|
40
|
+
* @tag pattern:registry
|
|
41
|
+
* @tag access:isomorphic
|
|
42
|
+
* @tag consumers:Search,CommandPalette,action-index admin screen
|
|
43
|
+
* @tag sideEffects:none
|
|
44
|
+
*/
|
|
45
|
+
export const ACTION_INDEX_COLLECTION = "actionIndex";
|
|
46
|
+
export const ACTION_INDEX_DOC_ID = "global";
|
|
47
|
+
/**
|
|
48
|
+
* Apply an admin's overrides to the static base.
|
|
49
|
+
*
|
|
50
|
+
* Order is load-bearing: override, then DROP disabled, then append `custom`.
|
|
51
|
+
* Dropping last would let an admin disable an entry and have their own
|
|
52
|
+
* replacement for it disappear too, since a custom entry may legitimately
|
|
53
|
+
* reuse a built-in id to supersede it.
|
|
54
|
+
*/
|
|
55
|
+
export function mergeActionIndex(base, control) {
|
|
56
|
+
if (!control)
|
|
57
|
+
return [...base];
|
|
58
|
+
const merged = [];
|
|
59
|
+
for (const entry of base) {
|
|
60
|
+
const override = control.entries?.[entry.id];
|
|
61
|
+
if (override?.enabled === false)
|
|
62
|
+
continue;
|
|
63
|
+
merged.push(override
|
|
64
|
+
? {
|
|
65
|
+
...entry,
|
|
66
|
+
...(override.label !== undefined ? { label: override.label } : {}),
|
|
67
|
+
...(override.description !== undefined
|
|
68
|
+
? { description: override.description }
|
|
69
|
+
: {}),
|
|
70
|
+
...(override.keywords !== undefined ? { keywords: override.keywords } : {}),
|
|
71
|
+
...(override.weight !== undefined ? { weight: override.weight } : {}),
|
|
72
|
+
}
|
|
73
|
+
: entry);
|
|
74
|
+
}
|
|
75
|
+
return merged.concat(control.custom ?? []);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Drop every entry the viewer may not reach.
|
|
79
|
+
*
|
|
80
|
+
* 🛑 Filtered SERVER-side, never in the browser. An entry the client is not
|
|
81
|
+
* allowed to act on is an entry it must not receive: the label alone is a site
|
|
82
|
+
* map of the admin panel, which is D8's whole point.
|
|
83
|
+
*/
|
|
84
|
+
export function projectActionIndexForViewer(entries, viewer) {
|
|
85
|
+
return entries.filter((entry) => {
|
|
86
|
+
if (entry.requiresTester && !viewer.isTester && !viewer.isAdmin)
|
|
87
|
+
return false;
|
|
88
|
+
if (entry.requiredRole && entry.requiredRole !== viewer.role && !viewer.isAdmin) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
/*
|
|
92
|
+
* An admin bypasses permission checks by design — `isAdminUser` short-
|
|
93
|
+
* circuits every one of them elsewhere, and an index that disagreed would
|
|
94
|
+
* hide screens the admin can plainly open from the sidebar.
|
|
95
|
+
*/
|
|
96
|
+
if (entry.requiredPermission && !viewer.isAdmin) {
|
|
97
|
+
if (!viewer.permissions?.includes(entry.requiredPermission))
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ActionIndexEntry } from "./types";
|
|
2
|
+
export interface ActionIndexValidationIssue {
|
|
3
|
+
field: "href" | "deepLink" | "id";
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ActionIndexValidationContext {
|
|
7
|
+
/**
|
|
8
|
+
* Every route the app actually serves, as paths without a locale prefix.
|
|
9
|
+
* Built from the route map by the caller — a runtime cannot walk `src/app`.
|
|
10
|
+
*/
|
|
11
|
+
knownRoutes: ReadonlySet<string>;
|
|
12
|
+
/** Anchors a page is known to render, keyed by route. */
|
|
13
|
+
knownAnchors?: Readonly<Record<string, ReadonlySet<string>>>;
|
|
14
|
+
/** Tab ids a route accepts, keyed by route. Absent = `?tab=` not checked. */
|
|
15
|
+
knownTabs?: Readonly<Record<string, ReadonlySet<string>>>;
|
|
16
|
+
/** Ids already in use — a duplicate silently shadows an existing entry. */
|
|
17
|
+
existingIds?: ReadonlySet<string>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Normalise an href to the form `knownRoutes` is keyed by: no origin, no
|
|
21
|
+
* locale segment, no query, no hash, no trailing slash.
|
|
22
|
+
*
|
|
23
|
+
* The locale strip matters — an admin pasting the URL out of their address bar
|
|
24
|
+
* gets `/en/admin/orders`, and rejecting that would be technically correct and
|
|
25
|
+
* useless.
|
|
26
|
+
*/
|
|
27
|
+
export declare function normaliseRoute(href: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* The three checks, at save time.
|
|
30
|
+
*
|
|
31
|
+
* Returns every issue rather than the first, so an admin fixing a bad entry
|
|
32
|
+
* does not discover its second problem only after fixing the first.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateActionIndexEntry(entry: Pick<ActionIndexEntry, "id" | "href" | "deepLink">, ctx: ActionIndexValidationContext): ActionIndexValidationIssue[];
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: `audit-action-index` runs on CI. An admin-authored entry (D7) is written
|
|
3
|
+
* at RUNTIME — so a bad href reaches the sidebar and the search, 404s for
|
|
4
|
+
* everyone, and stays there until the next audit run notices. The master
|
|
5
|
+
* plan's own words: without save-time validation, "admins can create
|
|
6
|
+
* entries" is a self-service 404 generator.
|
|
7
|
+
* WHAT: The same three checks the audit performs, as a function the write route
|
|
8
|
+
* calls before persisting, returning a 400 instead of a broken link.
|
|
9
|
+
*
|
|
10
|
+
* ## Why the route cannot just run the audit
|
|
11
|
+
*
|
|
12
|
+
* The audit reads FILES — it greps `settings-entries.ts` and the settings view
|
|
13
|
+
* for `id=` literals. A route has neither at runtime. So the checkable facts
|
|
14
|
+
* have to be passed in: the set of real routes, and the set of known anchors.
|
|
15
|
+
* That is the whole reason this is a function taking a context rather than a
|
|
16
|
+
* copy of the audit.
|
|
17
|
+
*
|
|
18
|
+
* ## What it deliberately does NOT check
|
|
19
|
+
*
|
|
20
|
+
* Whether the target page renders anything useful. A route that exists and an
|
|
21
|
+
* anchor that exists are provable from data the server has; "is this a sensible
|
|
22
|
+
* place to send someone" is not, and a validator that pretends otherwise would
|
|
23
|
+
* reject legitimate entries and teach admins to work around it.
|
|
24
|
+
*
|
|
25
|
+
* EXPORTS: validateActionIndexEntry, type ActionIndexValidationContext,
|
|
26
|
+
* type ActionIndexValidationIssue
|
|
27
|
+
*
|
|
28
|
+
* @tag domain:search
|
|
29
|
+
* @tag layer:shared
|
|
30
|
+
* @tag pattern:none
|
|
31
|
+
* @tag access:isomorphic
|
|
32
|
+
* @tag consumers:/api/admin/action-index,audit-action-index
|
|
33
|
+
* @tag sideEffects:none
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Normalise an href to the form `knownRoutes` is keyed by: no origin, no
|
|
37
|
+
* locale segment, no query, no hash, no trailing slash.
|
|
38
|
+
*
|
|
39
|
+
* The locale strip matters — an admin pasting the URL out of their address bar
|
|
40
|
+
* gets `/en/admin/orders`, and rejecting that would be technically correct and
|
|
41
|
+
* useless.
|
|
42
|
+
*/
|
|
43
|
+
export function normaliseRoute(href) {
|
|
44
|
+
return (href
|
|
45
|
+
.replace(/^https?:\/\/[^/]+/, "")
|
|
46
|
+
.replace(/^\/[a-z]{2}(-[A-Z]{2})?(?=\/)/, "")
|
|
47
|
+
.replace(/[?#].*$/, "")
|
|
48
|
+
.replace(/\/+$/, "") || "/");
|
|
49
|
+
}
|
|
50
|
+
/** A dynamic route matches by shape: `/admin/orders/[id]` accepts any id. */
|
|
51
|
+
function matchesKnownRoute(route, knownRoutes) {
|
|
52
|
+
if (knownRoutes.has(route))
|
|
53
|
+
return true;
|
|
54
|
+
const parts = route.split("/");
|
|
55
|
+
for (const known of knownRoutes) {
|
|
56
|
+
const knownParts = known.split("/");
|
|
57
|
+
if (knownParts.length !== parts.length)
|
|
58
|
+
continue;
|
|
59
|
+
const ok = knownParts.every((segment, i) => segment === parts[i] || /^\[.+\]$/.test(segment));
|
|
60
|
+
if (ok)
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The three checks, at save time.
|
|
67
|
+
*
|
|
68
|
+
* Returns every issue rather than the first, so an admin fixing a bad entry
|
|
69
|
+
* does not discover its second problem only after fixing the first.
|
|
70
|
+
*/
|
|
71
|
+
export function validateActionIndexEntry(entry, ctx) {
|
|
72
|
+
const issues = [];
|
|
73
|
+
if (ctx.existingIds?.has(entry.id)) {
|
|
74
|
+
issues.push({
|
|
75
|
+
field: "id",
|
|
76
|
+
message: `"${entry.id}" is already used. A duplicate id silently shadows the existing entry, and an override written for one would land on the other.`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const route = normaliseRoute(entry.href);
|
|
80
|
+
if (!entry.href.trim()) {
|
|
81
|
+
issues.push({ field: "href", message: "Enter the page this should open." });
|
|
82
|
+
}
|
|
83
|
+
else if (!route.startsWith("/")) {
|
|
84
|
+
issues.push({
|
|
85
|
+
field: "href",
|
|
86
|
+
message: "Start the link with / — an index entry points at a page on this site.",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else if (!matchesKnownRoute(route, ctx.knownRoutes)) {
|
|
90
|
+
issues.push({
|
|
91
|
+
field: "href",
|
|
92
|
+
message: `No page is served at ${route}. Check the path — a link that 404s is worse than no entry at all.`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (entry.deepLink) {
|
|
96
|
+
const tab = entry.deepLink.match(/[?&]tab=([^&#]+)/)?.[1];
|
|
97
|
+
const anchor = entry.deepLink.match(/#(.+)$/)?.[1];
|
|
98
|
+
const tabsForRoute = ctx.knownTabs?.[route];
|
|
99
|
+
if (tab && tabsForRoute && !tabsForRoute.has(tab)) {
|
|
100
|
+
issues.push({
|
|
101
|
+
field: "deepLink",
|
|
102
|
+
message: `${route} has no "${tab}" tab. The page would open on its default tab, which reads as the search having missed rather than as a broken link.`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const anchorsForRoute = ctx.knownAnchors?.[route];
|
|
106
|
+
if (anchor && anchorsForRoute && !anchorsForRoute.has(anchor)) {
|
|
107
|
+
issues.push({
|
|
108
|
+
field: "deepLink",
|
|
109
|
+
message: `${route} renders nothing with id="${anchor}". The link would scroll nowhere.`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return issues;
|
|
114
|
+
}
|
|
@@ -8,8 +8,12 @@ export interface NavSuggestionRecord {
|
|
|
8
8
|
/**
|
|
9
9
|
* W1-19 — wired to `/api/search/suggestions` 2026-05-23. Fetches up to 20
|
|
10
10
|
* matches (5 per resource type) and surfaces them as typeahead rows.
|
|
11
|
+
*
|
|
12
|
+
* `minChars` is trailing and defaulted because this hook is part of appkit's
|
|
13
|
+
* public API (`appkit/src/index.ts`); existing 2-argument callers keep working
|
|
14
|
+
* and inherit the gate.
|
|
11
15
|
*/
|
|
12
|
-
export declare function useNavSuggestions(query: string, selectedType?: string, debounceMs?: number): {
|
|
16
|
+
export declare function useNavSuggestions(query: string, selectedType?: string, debounceMs?: number, minChars?: number): {
|
|
13
17
|
suggestions: NavSuggestionRecord[];
|
|
14
18
|
isLoading: boolean;
|
|
15
19
|
};
|
|
@@ -20,11 +20,28 @@ const SUGGESTION_TYPE_PARAM_MAP = {
|
|
|
20
20
|
blog: "blog",
|
|
21
21
|
events: "event",
|
|
22
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* The shortest query that reaches the network.
|
|
25
|
+
*
|
|
26
|
+
* 🛑 Entity suggestions cost a real Firestore round trip — the route reads
|
|
27
|
+
* products, categories, blog posts and events. Below this length the result is
|
|
28
|
+
* dominated by whatever happens to sort first, so the request buys noise at the
|
|
29
|
+
* price of two round trips per typed word, on every page, for every visitor.
|
|
30
|
+
*
|
|
31
|
+
* This gate is for the ENTITY band only. Action/nav suggestions match an
|
|
32
|
+
* in-memory index and should stay available from the first character — typing
|
|
33
|
+
* "s" and seeing "Settings" is genuinely useful and costs nothing.
|
|
34
|
+
*/
|
|
35
|
+
const DEFAULT_MIN_CHARS = 3;
|
|
23
36
|
/**
|
|
24
37
|
* W1-19 — wired to `/api/search/suggestions` 2026-05-23. Fetches up to 20
|
|
25
38
|
* matches (5 per resource type) and surfaces them as typeahead rows.
|
|
39
|
+
*
|
|
40
|
+
* `minChars` is trailing and defaulted because this hook is part of appkit's
|
|
41
|
+
* public API (`appkit/src/index.ts`); existing 2-argument callers keep working
|
|
42
|
+
* and inherit the gate.
|
|
26
43
|
*/
|
|
27
|
-
export function useNavSuggestions(query, selectedType, debounceMs = 250) {
|
|
44
|
+
export function useNavSuggestions(query, selectedType, debounceMs = 250, minChars = DEFAULT_MIN_CHARS) {
|
|
28
45
|
const [suggestions, setSuggestions] = useState([]);
|
|
29
46
|
const [isLoading, setIsLoading] = useState(false);
|
|
30
47
|
const timerRef = useRef(null);
|
|
@@ -35,7 +52,10 @@ export function useNavSuggestions(query, selectedType, debounceMs = 250) {
|
|
|
35
52
|
if (abortRef.current)
|
|
36
53
|
abortRef.current.abort();
|
|
37
54
|
const trimmed = query.trim();
|
|
38
|
-
|
|
55
|
+
// Below the gate is a deliberate no-op, not a failure: clear the rows and
|
|
56
|
+
// report not-loading, so the dropdown shows its action band rather than a
|
|
57
|
+
// spinner that never resolves.
|
|
58
|
+
if (trimmed.length < minChars) {
|
|
39
59
|
setSuggestions([]);
|
|
40
60
|
setIsLoading(false);
|
|
41
61
|
return;
|
|
@@ -76,6 +96,6 @@ export function useNavSuggestions(query, selectedType, debounceMs = 250) {
|
|
|
76
96
|
if (abortRef.current)
|
|
77
97
|
abortRef.current.abort();
|
|
78
98
|
};
|
|
79
|
-
}, [query, selectedType, debounceMs]);
|
|
99
|
+
}, [query, selectedType, debounceMs, minChars]);
|
|
80
100
|
return { suggestions, isLoading };
|
|
81
101
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { SIEVE_OP, sieveFilter } from "../../../utils/sieve-builder";
|
|
2
|
+
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
1
3
|
/**
|
|
2
4
|
* SearchRepository
|
|
3
5
|
*
|
|
@@ -25,7 +27,7 @@ export class SearchRepository {
|
|
|
25
27
|
filterParts.push(`condition==${query.condition}`);
|
|
26
28
|
// SB1-G Phase 4 — canonical listingType discriminator.
|
|
27
29
|
if (query.listingType) {
|
|
28
|
-
filterParts.push(
|
|
30
|
+
filterParts.push(sieveFilter(PRODUCT_FIELDS.LISTING_TYPE, SIEVE_OP.EQ, query.listingType));
|
|
29
31
|
}
|
|
30
32
|
if (query.inStock === true)
|
|
31
33
|
filterParts.push("availableQuantity>0");
|
|
@@ -14,11 +14,10 @@ import { storeRepository } from "../../stores/repository/store.repository";
|
|
|
14
14
|
import { cartRepository } from "../../cart/repository/cart.repository";
|
|
15
15
|
import { maskOfferForSeller } from "../../../security";
|
|
16
16
|
import { ERROR_MESSAGES, AuthorizationError, ValidationError, NotFoundError, OFFER_ERROR_CODES, } from "../../../errors";
|
|
17
|
-
import { siteSettingsRepository } from "../../admin/repository/site-settings.repository";
|
|
18
17
|
import { canMakeOffer } from "../../../_internal/shared/listing-types/capabilities";
|
|
19
18
|
import { normalizeListingType } from "../../products/utils/listing-type";
|
|
20
19
|
import { OfferStatusValues } from "../schemas";
|
|
21
|
-
import { DEFAULT_MIN_OFFER_PERCENT,
|
|
20
|
+
import { DEFAULT_MIN_OFFER_PERCENT, offerIsOnlyPurchasePath, offersEnabledFor, resolveOfferBounds, } from "../../../_internal/shared/features/offers/config";
|
|
22
21
|
/**
|
|
23
22
|
* How long an accepted offer stays claimable at its locked price. Shared by the
|
|
24
23
|
* seller-accept path, the buyer-accepts-counter path, and the expiry sweep so
|
|
@@ -31,34 +30,59 @@ const ERR_NOT_AUTHORISED = "Not authorized";
|
|
|
31
30
|
export async function makeOffer(userId, userEmail, input) {
|
|
32
31
|
const { productId, offerAmount, buyerNote } = input;
|
|
33
32
|
/**
|
|
34
|
-
*
|
|
35
|
-
* `
|
|
36
|
-
*
|
|
37
|
-
*
|
|
33
|
+
* Two independent gates, checked in widening-to-narrowing order: the listing
|
|
34
|
+
* type's `canMakeOffer` capability, then the seller's own `allowOffers`
|
|
35
|
+
* opt-in.
|
|
36
|
+
*
|
|
37
|
+
* There was a third — the site-wide `featureFlags.offers` kill switch — which
|
|
38
|
+
* cost a Firestore singleton read on every offer attempt. It went with the
|
|
39
|
+
* rest of that group on 2026-08-29. The two that remain are the ones somebody
|
|
40
|
+
* actually sets: a per-type capability and a per-listing choice by the seller
|
|
41
|
+
* who owns the item.
|
|
42
|
+
*
|
|
43
|
+
* Both now go through `offersEnabledFor` / `resolveOfferBounds`, which the
|
|
44
|
+
* buyer's form reads too — the whole point of moving the rule into the shared
|
|
45
|
+
* config. A second copy here is how the form came to advertise a 50% floor
|
|
46
|
+
* this function then rejected (see the config's own header).
|
|
38
47
|
*/
|
|
39
|
-
const settings = await siteSettingsRepository.getSingleton().catch(() => null);
|
|
40
|
-
if (settings?.featureFlags?.offers === false) {
|
|
41
|
-
throw new ValidationError(ERROR_MESSAGES.OFFER.DISABLED, {
|
|
42
|
-
code: OFFER_ERROR_CODES.DISABLED,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
48
|
const product = await productRepository.findById(productId);
|
|
46
49
|
if (!product)
|
|
47
50
|
throw new NotFoundError(ERROR_MESSAGES.PRODUCT.NOT_FOUND);
|
|
51
|
+
const listingType = normalizeListingType(product);
|
|
48
52
|
// Some listing types cannot be negotiated at all — an auction already has
|
|
49
53
|
// bidding, a prize-draw sells fixed-price entries. This was never checked, so
|
|
50
54
|
// an offer could be (and in seed data was) attached to an auction, producing
|
|
51
55
|
// a cart line that claims `listingType: "standard"` in the offer lane.
|
|
52
|
-
if (!canMakeOffer(
|
|
56
|
+
if (!canMakeOffer(listingType)) {
|
|
53
57
|
throw new ValidationError("Offers aren't available for this kind of listing.");
|
|
54
58
|
}
|
|
55
|
-
if (!product.allowOffers)
|
|
59
|
+
if (!offersEnabledFor(listingType, product.allowOffers))
|
|
56
60
|
throw new ValidationError("This product does not accept offers.");
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
const bounds = resolveOfferBounds({
|
|
62
|
+
listingType,
|
|
63
|
+
listedPrice: product.price,
|
|
64
|
+
minOfferPercent: product.minOfferPercent,
|
|
65
|
+
allowOffers: product.allowOffers,
|
|
66
|
+
});
|
|
67
|
+
if (bounds.isBuyRequest) {
|
|
68
|
+
// No cart on this type and the seller has not opted into haggling, so the
|
|
69
|
+
// asking price is the only legal amount. Naming that plainly beats a
|
|
70
|
+
// "minimum offer is ₹X" message where X happens to equal the price.
|
|
71
|
+
if (offerAmount !== bounds.max)
|
|
72
|
+
throw new ValidationError(`This seller is not taking lower offers — send a request to buy at ₹${bounds.max}.`);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
if (offerAmount < bounds.min)
|
|
76
|
+
throw new ValidationError(`Minimum offer is ₹${bounds.min} (${product.minOfferPercent ?? DEFAULT_MIN_OFFER_PERCENT}% of listing price).`);
|
|
77
|
+
if (offerAmount > bounds.max)
|
|
78
|
+
throw new ValidationError(
|
|
79
|
+
// Deliberately NOT "use Add to Cart instead" unconditionally: on a
|
|
80
|
+
// listing whose only purchase path is an offer there is no cart button,
|
|
81
|
+
// so that advice named a control the page does not have.
|
|
82
|
+
offerIsOnlyPurchasePath(listingType)
|
|
83
|
+
? `Your offer cannot exceed the asking price of ₹${product.price}.`
|
|
84
|
+
: "Your offer must be below the listed price. Use Add to Cart instead.");
|
|
85
|
+
}
|
|
62
86
|
const offerCount = await offerRepository.countByBuyerAndProduct(userId, productId, product.updatedAt);
|
|
63
87
|
if (offerCount >= 3)
|
|
64
88
|
throw new ValidationError(ERROR_MESSAGES.OFFER.LIMIT_REACHED);
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* (src/actions/seller.actions.ts) alongside the other seller server actions.
|
|
10
10
|
*/
|
|
11
11
|
import { serverLogger } from "../../../monitoring";
|
|
12
|
-
import { isAdminUser,
|
|
12
|
+
import { isAdminUser, isStrictSellerUser, isTesterUser } from "../../auth/role-predicates";
|
|
13
13
|
import { ApiError, AuthorizationError, NotFoundError, ValidationError, } from "../../../errors";
|
|
14
14
|
import { userRepository } from "../../auth/repository/user.repository";
|
|
15
15
|
import { storeRepository } from "../../stores/repository/store.repository";
|
|
@@ -53,16 +53,40 @@ function maskAccountNumber(accountNumber) {
|
|
|
53
53
|
// --- Become Seller ------------------------------------------------------------
|
|
54
54
|
export async function becomeSeller(userId) {
|
|
55
55
|
const profile = await userRepository.findById(userId);
|
|
56
|
-
|
|
56
|
+
const isTester = isTesterUser(profile);
|
|
57
|
+
/*
|
|
58
|
+
* 🛑 `isStrictSellerUser`, not `isSellerUser`.
|
|
59
|
+
*
|
|
60
|
+
* `isSellerUser` now answers true for the `tester` role — testers own real
|
|
61
|
+
* stores and must clear every seller gate. But that makes it the WRONG test
|
|
62
|
+
* here: a brand-new tester has role `tester` from the moment they are
|
|
63
|
+
* flagged, so an early return keyed on it would send them straight back with
|
|
64
|
+
* `storeStatus: "pending"` and they would never reach the auto-approve below.
|
|
65
|
+
* The tester sandbox depends on that approval.
|
|
66
|
+
*
|
|
67
|
+
* A tester who HAS already onboarded is caught by the `storeStatus` clause,
|
|
68
|
+
* so this stays idempotent for them.
|
|
69
|
+
*/
|
|
70
|
+
const alreadyOnboarded = profile &&
|
|
71
|
+
(isStrictSellerUser(profile) ||
|
|
72
|
+
isAdminUser(profile) ||
|
|
73
|
+
(isTester && Boolean(profile.storeStatus)));
|
|
74
|
+
if (alreadyOnboarded) {
|
|
57
75
|
return {
|
|
58
76
|
alreadySeller: true,
|
|
59
77
|
storeStatus: profile.storeStatus ??
|
|
60
78
|
"pending",
|
|
61
79
|
};
|
|
62
80
|
}
|
|
63
|
-
const storeStatus =
|
|
81
|
+
const storeStatus = isTester ? "approved" : "pending";
|
|
64
82
|
await userRepository.update(userId, {
|
|
65
|
-
|
|
83
|
+
// 🛑 A tester must NOT be demoted to `seller` here. `tester` is a peer of
|
|
84
|
+
// `seller` in the hierarchy and already clears every seller gate
|
|
85
|
+
// (`isSellerUser` returns true for it), so overwriting the role would
|
|
86
|
+
// silently strip their tester identity — and with it the Tester Hub and
|
|
87
|
+
// the test-data visibility their whole programme depends on — the first
|
|
88
|
+
// time they opened a store.
|
|
89
|
+
...(isTester ? {} : { role: "seller" }),
|
|
66
90
|
storeStatus,
|
|
67
91
|
});
|
|
68
92
|
serverLogger.info("becomeSeller: application submitted", { userId, storeStatus });
|
|
@@ -83,7 +107,7 @@ export async function createStore(userId, userName, input) {
|
|
|
83
107
|
storeSlug = `${baseSlug.slice(0, 80 - suffix.length)}${suffix}`;
|
|
84
108
|
}
|
|
85
109
|
const profile = await userRepository.findById(userId);
|
|
86
|
-
const isTester = profile
|
|
110
|
+
const isTester = isTesterUser(profile);
|
|
87
111
|
const store = await storeRepository.create({
|
|
88
112
|
storeSlug,
|
|
89
113
|
ownerId: userId,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface CategoryChipPickerProps {
|
|
3
|
+
selected: string[];
|
|
4
|
+
onSelectedChange: (next: string[]) => void;
|
|
5
|
+
/** Copy under the control. Defaults to the coupon wording. */
|
|
6
|
+
emptyHint?: string;
|
|
7
|
+
allowCreate?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function CategoryChipPicker({ selected, onSelectedChange, emptyHint, allowCreate, }: CategoryChipPickerProps): React.JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { IconButton, Row, Span, Stack, Text } from "../../../ui";
|
|
4
|
+
import { CategoryInlineSelect } from "./CategoryInlineSelect";
|
|
5
|
+
export function CategoryChipPicker({ selected, onSelectedChange, emptyHint = "Leave empty to apply to every category.", allowCreate = false, }) {
|
|
6
|
+
const add = (id) => {
|
|
7
|
+
if (!id || selected.includes(id))
|
|
8
|
+
return;
|
|
9
|
+
onSelectedChange([...selected, id]);
|
|
10
|
+
};
|
|
11
|
+
return (_jsxs(Stack, { gap: "xs", children: [_jsx(CategoryInlineSelect, { value: "", onChange: add, allowCreate: allowCreate, placeholder: "Add a category\u2026" }), selected.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", padding: "t-2xs", children: selected.map((cid) => (_jsxs(Span, { layout: "inline-flex", gap: "xs", border: "strong", padding: "pill-sm", rounded: "full", surface: "muted", color: "primary", size: "xs", children: [cid, _jsx(IconButton, { "aria-label": `Remove ${cid}`, variant: "ghost", size: "sm", onClick: () => onSelectedChange(selected.filter((c) => c !== cid)), icon: "\u00D7" })] }, cid))) })), _jsx(Text, { size: "xs", color: "muted", children: emptyHint })] }));
|
|
12
|
+
}
|
|
@@ -37,12 +37,15 @@ export function FulfillmentView({ currentUserId, }) {
|
|
|
37
37
|
setOrders(res.orders ?? []);
|
|
38
38
|
}
|
|
39
39
|
catch (err) {
|
|
40
|
-
|
|
40
|
+
// An empty fulfilment queue is a normal, expected state, so a swallowed
|
|
41
|
+
// failure here reads as "nothing to pack" — matching the rest of this
|
|
42
|
+
// file, surface it instead.
|
|
43
|
+
showToast(normalizeError(err).message, "error");
|
|
41
44
|
}
|
|
42
45
|
finally {
|
|
43
46
|
setIsLoadingOrders(false);
|
|
44
47
|
}
|
|
45
|
-
}, []);
|
|
48
|
+
}, [showToast]);
|
|
46
49
|
useEffect(() => {
|
|
47
50
|
void fetchOrders();
|
|
48
51
|
}, [fetchOrders]);
|
|
@@ -22,7 +22,7 @@ export function PhysicalLocationModal({ count, onSave, onClose }) {
|
|
|
22
22
|
}
|
|
23
23
|
catch (e) {
|
|
24
24
|
void normalizeError(e);
|
|
25
|
-
setError(
|
|
25
|
+
setError("Failed to save location");
|
|
26
26
|
}
|
|
27
27
|
finally {
|
|
28
28
|
setSaving(false);
|
|
@@ -20,10 +20,12 @@ function ProductLabel({ product }) {
|
|
|
20
20
|
fontSize: 12,
|
|
21
21
|
margin: 8,
|
|
22
22
|
});
|
|
23
|
+
// JsBarcode throws on a value CODE128 cannot encode. The <svg> is left
|
|
24
|
+
// empty and the label still prints its title and price — there is no
|
|
25
|
+
// recovery beyond fixing the barcodeId on the product itself.
|
|
23
26
|
}
|
|
24
27
|
catch (_err) {
|
|
25
28
|
void normalizeError(_err);
|
|
26
|
-
// Invalid barcode value — render nothing
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
}, [product.barcodeId]);
|
|
@@ -146,5 +146,9 @@ export function SellerAddressesView({ apiBase = SELLER_ENDPOINTS.STORE_ADDRESSES
|
|
|
146
146
|
return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(StickyToolbar, { offset: "header", tone: "default", border: true, padding: "md", children: _jsxs(Row, { justify: "between", children: [_jsxs(Stack, { gap: "none", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", color: "primary", children: "Pickup Addresses" }), _jsx(Text, { size: "xs", color: "muted", className: "mt-0.5", children: "Manage your store's pickup and return locations" })] }), _jsxs(Row, { gap: "sm", align: "center", children: [_jsx(TextLink, { href: String(ROUTES.STORE.ADDRESSES_NEW), children: "Open full page \u2192" }), _jsxs(Button, { gap: "sm", size: "sm", onClick: openAdd, children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx(Span, { children: "Add Address" })] })] })] }) }), _jsxs(Div, { paddingX: "x-md-lg", className: "max-w-2xl", padding: "y-lg", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), isLoading ? (_jsx(Row, { justify: "center", padding: "y-4xl", children: _jsx(Div, { className: "h-6 w-6 animate-spin border-2 border-[var(--appkit-color-primary)] border-t-transparent", rounded: "full" }) })) : addresses.length === 0 ? (_jsxs(Stack, { border: "default", className: "border-2 border-dashed", padding: "y-4xl", align: "center", gap: "3", rounded: "xl", children: [_jsx(MapPin, { className: "h-8 w-8 text-[var(--appkit-color-text-muted)]" }), _jsx(Text, { size: "sm", color: "muted", children: "No pickup addresses yet" }), _jsx(Button, { size: "sm", variant: "outline", onClick: openAdd, children: "Add your first address" })] })) : (_jsxs(_Fragment, { children: [_jsxs(Row, { justify: "end", className: "mb-2", children: [_jsx(Button, { size: "sm", variant: listView === "table" ? "primary" : "ghost", onClick: () => setListView("table"), children: "Table" }), _jsx(Button, { size: "sm", variant: listView === "cards" ? "primary" : "ghost", onClick: () => setListView("cards"), children: "Cards" })] }), listView === "cards" ? (_jsx(Grid, { gap: "sm", children: addresses.map((addr) => (_jsx(Div, { className: deletingId === addr.id ? "opacity-50 pointer-events-none" : "", children: _jsx(AddressCard, { address: addr, onEdit: () => openEdit(addr), onDelete: () => handleDelete(addr) }) }, addr.id))) })) : (_jsx(Div, { className: `${__O.xAuto}`, rounded: "lg", border: "default", children: _jsxs(Table, { size: "sm", children: [_jsx(Thead, { surface: "muted", children: _jsxs(Tr, { children: [_jsx(Th, { className: "text-left", padding: "sm", weight: "semibold", children: "Label" }), _jsx(Th, { className: "text-left", padding: "sm", weight: "semibold", children: "Name" }), _jsx(Th, { className: "text-left", padding: "sm", weight: "semibold", children: "City" }), _jsx(Th, { className: "text-left", padding: "sm", weight: "semibold", children: "Phone" }), _jsx(Th, { className: "text-right", padding: "sm", weight: "semibold", children: "Actions" })] }) }), _jsx(Tbody, { children: addresses.map((addr) => {
|
|
147
147
|
const isBanned = addr.banStatus === "banned";
|
|
148
148
|
return (_jsxs(Tr, { className: `border-t border-[var(--appkit-color-border)] ${deletingId === addr.id ? "opacity-50" : ""}`, children: [_jsx(Td, { padding: "sm", children: _jsxs(Row, { gap: "xs", align: "center", children: [_jsx(Span, { children: addr.label }), isBanned && _jsx(Badge, { variant: "danger", size: "sm", children: "Banned" })] }) }), _jsx(Td, { padding: "sm", children: addr.fullName }), _jsxs(Td, { padding: "sm", children: [addr.city, ", ", addr.state] }), _jsx(Td, { className: "tabular-nums", padding: "sm", children: addr.phone }), _jsx(Td, { className: "text-right", padding: "sm", children: isBanned ? (_jsx(Span, { size: "xs", color: "muted", title: "Contact support to resolve address ban", children: "Contact support" })) : (_jsxs(Row, { justify: "end", gap: "xs", children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: () => openEdit(addr), children: ROW_ACTION_META[ROW_ACTION_ID.EDIT].label }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => handleDelete(addr), children: ROW_ACTION_META[ROW_ACTION_ID.DELETE].label })] })) })] }, addr.id));
|
|
149
|
-
}) })] }) }))] }))] }), _jsx(SideDrawer, { isOpen: drawerOpen, onClose: closeDrawer, title: editingId ? "Edit Address" : "Add Address", footer:
|
|
149
|
+
}) })] }) }))] }))] }), _jsx(SideDrawer, { isOpen: drawerOpen, onClose: closeDrawer, title: editingId ? "Edit Address" : "Add Address", footer:
|
|
150
|
+
/* Bare buttons — the SideDrawer footer slot supplies the ActionRow.
|
|
151
|
+
The `flex-1` these carried would have beaten its sizing under
|
|
152
|
+
`important: true`. */
|
|
153
|
+
_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", onClick: closeDrawer, children: "Cancel" }), _jsx(Button, { onClick: handleSave, disabled: saving, children: saving ? "Saving…" : editingId ? "Save Changes" : "Add Address" })] }), children: _jsxs(Stack, { gap: "md", padding: "y-2xs", children: [saveError && (_jsx(Div, { textSize: "sm", className: "border border-error/20", color: "error", surface: "danger-surface", padding: "inlineSm", rounded: "lg", children: saveError })), _jsx(FieldInput, { name: "label", label: "Label *", hint: "e.g. Warehouse, Shop, Home", type: "text", value: draft.label, onChange: setField("label"), placeholder: "Warehouse", maxLength: 60 }), _jsxs(Grid, { cols: 2, gap: "sm", children: [_jsx(FieldInput, { name: "fullName", label: "Full Name *", type: "text", value: draft.fullName, onChange: setField("fullName"), placeholder: "Ravi Kumar", maxLength: 100 }), _jsx(FieldInput, { name: "phone", label: "Phone *", type: "tel", value: draft.phone, onChange: setField("phone"), placeholder: "+91 98765 43210", maxLength: 20 })] }), _jsx(FieldInput, { name: "addressLine1", label: "Address Line 1 *", type: "text", value: draft.addressLine1, onChange: setField("addressLine1"), placeholder: "Shop 12, Main Market", maxLength: 200 }), _jsx(FieldInput, { name: "addressLine2", label: "Address Line 2", type: "text", value: draft.addressLine2, onChange: setField("addressLine2"), placeholder: "Building / Floor (optional)", maxLength: 200 }), _jsx(FieldInput, { name: "landmark", label: "Landmark", type: "text", value: draft.landmark, onChange: setField("landmark"), placeholder: "Near metro station (optional)", maxLength: 100 }), _jsxs(Grid, { cols: 2, gap: "sm", children: [_jsx(FieldInput, { name: "city", label: "City *", type: "text", value: draft.city, onChange: setField("city"), placeholder: "Mumbai", maxLength: 100 }), _jsx(FieldInput, { name: "state", label: "State *", type: "text", value: draft.state, onChange: setField("state"), placeholder: "Maharashtra", maxLength: 100 })] }), _jsxs(Grid, { cols: 2, gap: "sm", children: [_jsx(FieldInput, { name: "postalCode", label: "Postal Code *", type: "text", value: draft.postalCode, onChange: setField("postalCode"), placeholder: "400001", maxLength: 10 }), _jsx(FieldInput, { name: "country", label: "Country *", type: "text", value: draft.country, onChange: setField("country"), placeholder: "India", maxLength: 60 })] }), _jsx(FieldCheckbox, { name: "isDefault", label: "Set as default pickup address", checked: draft.isDefault, onChange: (c) => set("isDefault", c) })] }) }), deleteTargetAddr && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Address", message: `Delete address "${deleteTargetAddr.label}"? This cannot be undone.`, onConfirm: () => { deleteById(deleteTargetAddr.id); setDeleteTargetAddr(null); }, onClose: () => setDeleteTargetAddr(null), isDeleting: deletingId === deleteTargetAddr.id }))] }));
|
|
150
154
|
}
|