@mohasinac/appkit 4.30.19 → 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/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 -87
- package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +16 -5
- package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -40
- 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/categories/data.js +15 -4
- 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/live/index.d.ts +0 -1
- package/dist/_internal/server/features/live/index.js +0 -1
- package/dist/_internal/server/features/products/data.js +15 -5
- 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/site-settings/adapters.d.ts +2 -2
- package/dist/_internal/server/features/site-settings/adapters.js +8 -5
- 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/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/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/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.d.ts +37 -20
- package/dist/client.js +36 -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/extend.d.ts +0 -9
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/realtime-session.js +7 -0
- 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/messages.d.ts +0 -8
- package/dist/errors/messages.js +0 -9
- 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/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/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/auth/actions/index.d.ts +0 -1
- package/dist/features/auth/actions/index.js +0 -1
- 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/Guards.js +2 -1
- package/dist/features/auth/components/LoginForm.d.ts +0 -2
- package/dist/features/auth/components/LoginForm.js +1 -3
- package/dist/features/auth/components/RegisterForm.js +62 -19
- 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/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/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/schemas/bundle-form.d.ts +142 -0
- package/dist/features/categories/schemas/bundle-form.js +149 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +5 -4
- package/dist/features/classified/components/ClassifiedDetailPageView.js +37 -16
- 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/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/homepage/components/MarketplaceHomepageView.js +24 -5
- package/dist/features/homepage/hooks/useBlogArticles.js +1 -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/lottery/schemas/config-write.d.ts +9 -5
- package/dist/features/lottery/schemas/config-write.js +69 -9
- 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/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/ProductDetailActions.js +0 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +7 -3
- package/dist/features/products/components/ProductDetailPageView.js +8 -5
- 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/products.repository.d.ts +5 -1
- package/dist/features/products/repository/products.repository.js +23 -1
- 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 +8 -7
- 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 -24
- 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/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/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/SellerDigitalCodesView.d.ts +14 -7
- package/dist/features/seller/components/SellerDigitalCodesView.js +4 -126
- 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/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/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/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 +275 -29
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +48 -12
- package/dist/http/api-handler.js +2 -1
- package/dist/index.d.ts +24 -103
- package/dist/index.js +33 -141
- 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 +6 -3
- 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/db-firebase/admin.js +15 -1
- package/dist/providers/db-firebase/sieve.d.ts +22 -0
- 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/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/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
|
@@ -172,9 +172,17 @@ export const ACTIONS = {
|
|
|
172
172
|
iconKey: "message",
|
|
173
173
|
id: "product.make-offer",
|
|
174
174
|
label: "Make an offer",
|
|
175
|
-
|
|
175
|
+
/*
|
|
176
|
+
* Scope corrected 2026-08-31: this was `["classified"]`, but the button it
|
|
177
|
+
* labels (`MakeOfferButton`) mounts on every type whose capability says
|
|
178
|
+
* `canMakeOffer` — which is standard, art and stickers as well. On
|
|
179
|
+
* classified specifically it is the ONLY purchase path, and a seller who
|
|
180
|
+
* has not opted into haggling receives a request to buy at the asking
|
|
181
|
+
* price rather than a lower offer.
|
|
182
|
+
*/
|
|
183
|
+
description: "Propose a price to the seller, or request to buy at the asking price.",
|
|
176
184
|
kind: "secondary",
|
|
177
|
-
listingTypeScope: ["classified"],
|
|
185
|
+
listingTypeScope: ["standard", "classified", "art", "stickers"],
|
|
178
186
|
},
|
|
179
187
|
},
|
|
180
188
|
AUCTION: {
|
|
@@ -271,16 +279,15 @@ export const ACTIONS = {
|
|
|
271
279
|
listingTypeScope: ["prize-draw"],
|
|
272
280
|
},
|
|
273
281
|
},
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
},
|
|
282
|
+
/*
|
|
283
|
+
* `CLASSIFIED` held one entry, "contact-seller", whose description read
|
|
284
|
+
* "Open a conversations thread with the seller". It had ZERO consumers — the
|
|
285
|
+
* button on the classified PDP was hand-written — and the feature it named was
|
|
286
|
+
* deleted 2026-08-31. `PRODUCT["make-offer"]` below is the action that is
|
|
287
|
+
* actually wired (`MakeOfferButton`), and on a classified it is now the only
|
|
288
|
+
* purchase path, so a second bucket duplicating it would be exactly the drift
|
|
289
|
+
* Rule #7 exists to prevent.
|
|
290
|
+
*/
|
|
284
291
|
DIGITAL_CODE: {
|
|
285
292
|
"claim-code": {
|
|
286
293
|
iconKey: "confirm",
|
|
@@ -291,16 +298,12 @@ export const ACTIONS = {
|
|
|
291
298
|
listingTypeScope: ["digital-code"],
|
|
292
299
|
},
|
|
293
300
|
},
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
kind: "primary",
|
|
301
|
-
listingTypeScope: ["live"],
|
|
302
|
-
},
|
|
303
|
-
},
|
|
301
|
+
/*
|
|
302
|
+
* `LIVE.inquire` ("Open a conversation with the seller about a live-item
|
|
303
|
+
* listing") went the same way and for the same two reasons: no consumer, and
|
|
304
|
+
* the conversations feature is gone. It was never needed either — a live item
|
|
305
|
+
* has `canAddToCart: true`, so unlike classified it has a real checkout.
|
|
306
|
+
*/
|
|
304
307
|
BUNDLE: {
|
|
305
308
|
"buy-now": {
|
|
306
309
|
iconKey: "cart",
|
|
@@ -879,6 +882,26 @@ export const ACTIONS = {
|
|
|
879
882
|
confirmKind: "danger",
|
|
880
883
|
},
|
|
881
884
|
},
|
|
885
|
+
/*
|
|
886
|
+
* The alerts list rendered a bare `<Button variant="ghost">Delete</Button>`
|
|
887
|
+
* with a raw onClick — no ActionDef, so no confirmation, so one misplaced
|
|
888
|
+
* click silently removed an alert with nothing to undo it (Rule #7).
|
|
889
|
+
*/
|
|
890
|
+
"delete-analytics-alert": {
|
|
891
|
+
iconKey: "delete",
|
|
892
|
+
id: "store.delete-analytics-alert",
|
|
893
|
+
label: "Delete",
|
|
894
|
+
ariaLabel: "Delete this analytics alert",
|
|
895
|
+
description: "Permanently removes this alert rule. Past notifications are unaffected.",
|
|
896
|
+
kind: "danger",
|
|
897
|
+
permissions: ["seller", "admin"],
|
|
898
|
+
confirmation: {
|
|
899
|
+
title: "Delete this alert?",
|
|
900
|
+
body: "You will stop being notified when this rule matches. Alerts already sent are unaffected. This cannot be undone.",
|
|
901
|
+
confirmLabel: "Delete alert",
|
|
902
|
+
confirmKind: "danger",
|
|
903
|
+
},
|
|
904
|
+
},
|
|
882
905
|
"cancel-form": {
|
|
883
906
|
iconKey: "cancel",
|
|
884
907
|
id: "store.cancel-form",
|
|
@@ -1197,6 +1220,30 @@ export const ACTIONS = {
|
|
|
1197
1220
|
confirmKind: "danger",
|
|
1198
1221
|
},
|
|
1199
1222
|
},
|
|
1223
|
+
/*
|
|
1224
|
+
* A LOT, not the whole shipment.
|
|
1225
|
+
*
|
|
1226
|
+
* The lot row's delete button used `shipment.delete`, so the dialog asked
|
|
1227
|
+
* "Delete this shipment?" and warned about removing every lot — while the
|
|
1228
|
+
* handler deleted one lot. A confirmation that names the wrong object is
|
|
1229
|
+
* worse than none: it is either ignored, or it stops someone doing
|
|
1230
|
+
* something harmless.
|
|
1231
|
+
*/
|
|
1232
|
+
"delete-lot": {
|
|
1233
|
+
iconKey: "delete",
|
|
1234
|
+
id: "shipment.delete-lot",
|
|
1235
|
+
label: "Delete",
|
|
1236
|
+
ariaLabel: "Delete this lot and its items",
|
|
1237
|
+
description: "Permanently deletes this lot and every item in it.",
|
|
1238
|
+
kind: "danger",
|
|
1239
|
+
permissions: ["admin"],
|
|
1240
|
+
confirmation: {
|
|
1241
|
+
title: "Delete this lot?",
|
|
1242
|
+
body: "This permanently removes the lot and every item in it. The rest of the shipment is untouched. Items still linked to a product must be unlinked first.",
|
|
1243
|
+
confirmLabel: "Delete lot",
|
|
1244
|
+
confirmKind: "danger",
|
|
1245
|
+
},
|
|
1246
|
+
},
|
|
1200
1247
|
},
|
|
1201
1248
|
CATALOGUE: {
|
|
1202
1249
|
"list-item": {
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Classified listing rule —
|
|
2
|
+
* Classified listing rule — no cart, no checkout; the offer IS the purchase path.
|
|
3
3
|
*
|
|
4
|
-
* Classified listings (OLX / Facebook Marketplace pattern)
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Classified listings (OLX / Facebook Marketplace pattern) never reach cart or
|
|
5
|
+
* order creation. The buyer transacts through Make-an-Offer: the seller accepts,
|
|
6
|
+
* which produces a locked cart line in the offer lane, and checkout proceeds
|
|
7
|
+
* from there like any other accepted offer.
|
|
8
|
+
*
|
|
9
|
+
* A seller who has not set `allowOffers` still receives requests to buy at their
|
|
10
|
+
* asking price — see `resolveOfferBounds` in the offers config. Without that,
|
|
11
|
+
* unchecking one flag would make the listing untransactable.
|
|
7
12
|
*/
|
|
8
13
|
import type { ListingCheckoutRule } from "./types";
|
|
9
14
|
export declare const classifiedRule: ListingCheckoutRule;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { DEFAULT_LISTING_RULE } from "./_defaults";
|
|
2
2
|
export const classifiedRule = {
|
|
3
3
|
...DEFAULT_LISTING_RULE,
|
|
4
|
-
orderType: "standard", // placeholder — classified
|
|
4
|
+
orderType: "standard", // placeholder — a classified reaches order creation only via an accepted offer
|
|
5
5
|
purchaseable: false,
|
|
6
6
|
cartEligible: false,
|
|
7
|
-
|
|
7
|
+
/*
|
|
8
|
+
* Names the control the page actually has. It read 'Use "Contact Seller" to
|
|
9
|
+
* arrange a meetup' until 2026-08-31, and that button opened a conversations
|
|
10
|
+
* thread which no longer exists.
|
|
11
|
+
*/
|
|
12
|
+
cartIneligibleHint: "Make an offer to buy this — classified listings are arranged with the seller.",
|
|
8
13
|
requiresShippingAddress: false,
|
|
9
14
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The fields the matcher reads. Everything is optional but `label`. */
|
|
2
|
+
export interface NavSearchable {
|
|
3
|
+
label: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
keywords?: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* How well `item` answers `query`. `0` when it does not.
|
|
9
|
+
*
|
|
10
|
+
* The query is matched whole rather than tokenised: "prize draws" should find
|
|
11
|
+
* Prize Draws, and splitting on spaces would also match anything containing
|
|
12
|
+
* "draws" on its own, which for a two-word query is nearly everything.
|
|
13
|
+
*/
|
|
14
|
+
export declare function matchesNavQuery(item: NavSearchable, query: string): number;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: The sidebar search was `item.label.toLowerCase().includes(q)` — so it
|
|
3
|
+
* could only ever find a screen by the name it already shows you. Typing
|
|
4
|
+
* "refund" found nothing, because the screen is called "Payouts"; typing
|
|
5
|
+
* "postcode" found nothing, because the field is called PIN code; typing
|
|
6
|
+
* "maintenance" found nothing, because the toggle lives inside "Site
|
|
7
|
+
* Settings". A search that only matches what is already on screen is a
|
|
8
|
+
* filter, not a search.
|
|
9
|
+
* WHAT: One matcher over label + description + keywords, shared by the sidebar
|
|
10
|
+
* search and (in W7) the action index.
|
|
11
|
+
*
|
|
12
|
+
* ## Ranking, not just filtering
|
|
13
|
+
*
|
|
14
|
+
* A label hit and a keyword hit are not equally good answers. "orders" should
|
|
15
|
+
* put **Orders** first and "Orders & Finance" (a guide that mentions orders)
|
|
16
|
+
* second — so the matcher returns a SCORE and the caller sorts by it. Without
|
|
17
|
+
* that, alphabetical order decides, and the exact match can land ninth.
|
|
18
|
+
*
|
|
19
|
+
* EXPORTS: matchesNavQuery, type NavSearchable
|
|
20
|
+
*
|
|
21
|
+
* @tag domain:layout
|
|
22
|
+
* @tag layer:shared
|
|
23
|
+
* @tag pattern:none
|
|
24
|
+
* @tag access:isomorphic
|
|
25
|
+
* @tag consumers:useSidebarSearch,action index
|
|
26
|
+
* @tag sideEffects:none
|
|
27
|
+
*/
|
|
28
|
+
/** Higher is a better answer. `0` means no match at all. */
|
|
29
|
+
const SCORE = {
|
|
30
|
+
labelExact: 100,
|
|
31
|
+
labelPrefix: 80,
|
|
32
|
+
labelSubstring: 60,
|
|
33
|
+
keywordExact: 50,
|
|
34
|
+
keywordSubstring: 30,
|
|
35
|
+
description: 10,
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* How well `item` answers `query`. `0` when it does not.
|
|
39
|
+
*
|
|
40
|
+
* The query is matched whole rather than tokenised: "prize draws" should find
|
|
41
|
+
* Prize Draws, and splitting on spaces would also match anything containing
|
|
42
|
+
* "draws" on its own, which for a two-word query is nearly everything.
|
|
43
|
+
*/
|
|
44
|
+
export function matchesNavQuery(item, query) {
|
|
45
|
+
const q = query.trim().toLowerCase();
|
|
46
|
+
if (!q)
|
|
47
|
+
return 0;
|
|
48
|
+
const label = item.label.toLowerCase();
|
|
49
|
+
if (label === q)
|
|
50
|
+
return SCORE.labelExact;
|
|
51
|
+
if (label.startsWith(q))
|
|
52
|
+
return SCORE.labelPrefix;
|
|
53
|
+
if (label.includes(q))
|
|
54
|
+
return SCORE.labelSubstring;
|
|
55
|
+
for (const keyword of item.keywords ?? []) {
|
|
56
|
+
const k = keyword.toLowerCase();
|
|
57
|
+
if (k === q)
|
|
58
|
+
return SCORE.keywordExact;
|
|
59
|
+
if (k.includes(q) || q.includes(k))
|
|
60
|
+
return SCORE.keywordSubstring;
|
|
61
|
+
}
|
|
62
|
+
/*
|
|
63
|
+
* The description is matched LAST and scores lowest on purpose: it is a
|
|
64
|
+
* sentence, so it contains common words, and letting it outrank a keyword
|
|
65
|
+
* would mean "shipping" surfaced every screen whose description happens to
|
|
66
|
+
* mention shipping ahead of the one actually called Shipping.
|
|
67
|
+
*/
|
|
68
|
+
if (item.description && item.description.toLowerCase().includes(q)) {
|
|
69
|
+
return SCORE.description;
|
|
70
|
+
}
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
@@ -25,12 +25,48 @@ export interface SidebarNavItem {
|
|
|
25
25
|
badge?: number;
|
|
26
26
|
/**
|
|
27
27
|
* Stable nav-* slug used as the key in siteSettings.navConfig.
|
|
28
|
-
*
|
|
28
|
+
*
|
|
29
|
+
* 🛑 **Derived from the href, never hand-written** — `navItemId(portal, href)`.
|
|
30
|
+
* A hand-written id drifts from the route it names, and because it is also
|
|
31
|
+
* the `navConfig` key, a drifted id silently un-toggles the item: the admin
|
|
32
|
+
* hides "Payouts", the toggle writes a key nothing reads, and the entry
|
|
33
|
+
* stays.
|
|
34
|
+
*
|
|
35
|
+
* If absent, the item is always visible — no admin toggle and **no
|
|
36
|
+
* permission check**, which is why every admin item having no id meant
|
|
37
|
+
* `requiredPermission` had never once been enforced.
|
|
29
38
|
*/
|
|
30
39
|
id?: string;
|
|
31
40
|
/** RBAC permission key — item only shows to users with this permission. */
|
|
32
41
|
requiredPermission?: string;
|
|
42
|
+
/**
|
|
43
|
+
* One sentence on what this screen is FOR, in the user's words rather than
|
|
44
|
+
* the entity's. "Refund a buyer, or check why a payout is late" finds its
|
|
45
|
+
* screen; "Payouts" only finds itself.
|
|
46
|
+
*
|
|
47
|
+
* Consumed by the sidebar search and, in W7, by the action index.
|
|
48
|
+
*/
|
|
49
|
+
description?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Words someone might search for that are not in the label — synonyms
|
|
52
|
+
* ("postcode" for PIN), the old name of a renamed screen, the noun the rest
|
|
53
|
+
* of the industry uses.
|
|
54
|
+
*/
|
|
55
|
+
keywords?: string[];
|
|
56
|
+
/** Which dashboard this belongs to. Set by the helper alongside `id`. */
|
|
57
|
+
portal?: NavPortal;
|
|
33
58
|
}
|
|
59
|
+
/** The three dashboards a sidebar item can belong to. */
|
|
60
|
+
export type NavPortal = "admin" | "store" | "user";
|
|
61
|
+
/**
|
|
62
|
+
* The `navConfig` key for a nav item: `nav-{portal}-{slug-of-href}`.
|
|
63
|
+
*
|
|
64
|
+
* Derived rather than authored so an id cannot disagree with the route it
|
|
65
|
+
* points at. Locale prefixes and dynamic segments are stripped, so
|
|
66
|
+
* `/en/admin/orders` and `/admin/orders` produce the same key — otherwise a
|
|
67
|
+
* localised build would write a second set of toggles nobody can find.
|
|
68
|
+
*/
|
|
69
|
+
export declare function navItemId(portal: NavPortal, href: string): string;
|
|
34
70
|
/** Sidebar group with title + items + default-open state. */
|
|
35
71
|
export interface SidebarNavGroup {
|
|
36
72
|
title: string;
|
|
@@ -10,4 +10,22 @@
|
|
|
10
10
|
* 2. render-prop slots on each section (defined on the section component itself).
|
|
11
11
|
* 3. Section replacement via AppShell named slots.
|
|
12
12
|
*/
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The `navConfig` key for a nav item: `nav-{portal}-{slug-of-href}`.
|
|
15
|
+
*
|
|
16
|
+
* Derived rather than authored so an id cannot disagree with the route it
|
|
17
|
+
* points at. Locale prefixes and dynamic segments are stripped, so
|
|
18
|
+
* `/en/admin/orders` and `/admin/orders` produce the same key — otherwise a
|
|
19
|
+
* localised build would write a second set of toggles nobody can find.
|
|
20
|
+
*/
|
|
21
|
+
export function navItemId(portal, href) {
|
|
22
|
+
const path = href
|
|
23
|
+
.replace(/^https?:\/\/[^/]+/, "")
|
|
24
|
+
.replace(/^\/[a-z]{2}(-[A-Z]{2})?(?=\/)/, "")
|
|
25
|
+
.replace(/[?#].*$/, "")
|
|
26
|
+
.replace(/^\/+|\/+$/g, "")
|
|
27
|
+
.replace(/[^a-zA-Z0-9/-]+/g, "-")
|
|
28
|
+
.replace(/\//g, "-")
|
|
29
|
+
.toLowerCase();
|
|
30
|
+
return `nav-${portal}-${path || "root"}`;
|
|
31
|
+
}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* When those pages are built, change the entry here and all 40 call sites
|
|
26
26
|
* follow. That is the whole point of the indirection.
|
|
27
27
|
*/
|
|
28
|
-
import type { NotificationRelatedType } from "../../../../features/admin/schemas/firestore";
|
|
28
|
+
import type { NotificationRelatedType, NotificationType } from "../../../../features/admin/schemas/firestore";
|
|
29
29
|
/**
|
|
30
30
|
* Which portal the recipient reads this in.
|
|
31
31
|
*
|
|
@@ -45,3 +45,11 @@ export type NotificationAudience = "buyer" | "seller" | "admin";
|
|
|
45
45
|
* looking at.
|
|
46
46
|
*/
|
|
47
47
|
export declare function resolveNotificationActionUrl(relatedType: NotificationRelatedType | undefined, relatedId: string | undefined, audience: NotificationAudience): string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Which portal each notification type belongs to.
|
|
50
|
+
*
|
|
51
|
+
* `Record<NotificationType, …>`, so a new type cannot compile without an
|
|
52
|
+
* answer — the same discipline `typeToPrefsKey` needed after it silently
|
|
53
|
+
* omitted three types and ignored those users' opt-outs.
|
|
54
|
+
*/
|
|
55
|
+
export declare const TYPE_AUDIENCE: Record<NotificationType, NotificationAudience>;
|
|
@@ -109,3 +109,50 @@ export function resolveNotificationActionUrl(relatedType, relatedId, audience) {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
+
/*
|
|
113
|
+
* Moved here from `features/admin/actions/notification-actions.ts` 2026-08-31.
|
|
114
|
+
* It belongs beside the resolver it feeds, and being module-private in a
|
|
115
|
+
* `"use server"` file meant the SEED could not use it — so seeded notifications
|
|
116
|
+
* carried no `actionUrl` and rendered unclickable, the exact defect the central
|
|
117
|
+
* resolver was built to end.
|
|
118
|
+
*/
|
|
119
|
+
/**
|
|
120
|
+
* Which portal each notification type belongs to.
|
|
121
|
+
*
|
|
122
|
+
* `Record<NotificationType, …>`, so a new type cannot compile without an
|
|
123
|
+
* answer — the same discipline `typeToPrefsKey` needed after it silently
|
|
124
|
+
* omitted three types and ignored those users' opt-outs.
|
|
125
|
+
*/
|
|
126
|
+
export const TYPE_AUDIENCE = {
|
|
127
|
+
// Buyer-facing: the order lifecycle they are waiting on.
|
|
128
|
+
order_placed: "buyer",
|
|
129
|
+
order_confirmed: "buyer",
|
|
130
|
+
order_shipped: "buyer",
|
|
131
|
+
order_delivered: "buyer",
|
|
132
|
+
order_cancelled: "buyer",
|
|
133
|
+
refund_initiated: "buyer",
|
|
134
|
+
payment_review: "buyer",
|
|
135
|
+
emi_installment_due_soon: "buyer",
|
|
136
|
+
emi_installment_overdue: "buyer",
|
|
137
|
+
prize_won: "buyer",
|
|
138
|
+
prize_reveal_expired: "buyer",
|
|
139
|
+
product_available: "buyer",
|
|
140
|
+
bid_placed: "buyer",
|
|
141
|
+
bid_outbid: "buyer",
|
|
142
|
+
bid_won: "buyer",
|
|
143
|
+
bid_lost: "buyer",
|
|
144
|
+
offer_responded: "buyer",
|
|
145
|
+
offer_counter_accepted: "buyer",
|
|
146
|
+
offer_expired: "buyer",
|
|
147
|
+
review_replied: "buyer",
|
|
148
|
+
catalogue_images_stale: "buyer",
|
|
149
|
+
// Seller-facing.
|
|
150
|
+
auction_ended: "seller", // "your auction closed with no winner"
|
|
151
|
+
offer_received: "seller",
|
|
152
|
+
review_approved: "seller",
|
|
153
|
+
// Account-level, which lives in the user portal for everyone.
|
|
154
|
+
welcome: "buyer",
|
|
155
|
+
account_action: "buyer",
|
|
156
|
+
system: "buyer",
|
|
157
|
+
promotion: "buyer",
|
|
158
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ListingType } from "../../../../features/products/types/index";
|
|
1
2
|
/**
|
|
2
3
|
* The floor, as a percentage of the listed price, below which an offer is
|
|
3
4
|
* rejected — used whenever a listing does not set its own `minOfferPercent`.
|
|
@@ -28,3 +29,67 @@ export declare function minOfferAmount(listedPrice: number, percent?: number | n
|
|
|
28
29
|
* below the floor the seller has said they don't want to hear it.
|
|
29
30
|
*/
|
|
30
31
|
export declare function isOfferAmountValid(amount: number, listedPrice: number, percent?: number | null): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Is Make-an-Offer the only way to buy this listing type?
|
|
34
|
+
*
|
|
35
|
+
* True exactly when the type can be offered on but has no cart line — today
|
|
36
|
+
* that is `classified` alone (`canAddToCart: false`, `canMakeOffer: true`).
|
|
37
|
+
*
|
|
38
|
+
* Derived from the capability registry rather than testing for `"classified"`,
|
|
39
|
+
* so a future type that lands in the same shape inherits the rule instead of
|
|
40
|
+
* silently becoming unbuyable. That is not hypothetical: this function exists
|
|
41
|
+
* because classified's actual purchase path was a `conversations` chat thread,
|
|
42
|
+
* and when that feature was deleted the listing type would have been left with
|
|
43
|
+
* no way to transact at all.
|
|
44
|
+
*/
|
|
45
|
+
export declare function offerIsOnlyPurchasePath(listingType: ListingType): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* May a buyer send an offer on this listing at all?
|
|
48
|
+
*
|
|
49
|
+
* The two ordinary gates are the type's `canMakeOffer` capability and the
|
|
50
|
+
* seller's own `allowOffers` opt-in. The third clause is the point:
|
|
51
|
+
*
|
|
52
|
+
* 🛑 **`allowOffers` is an opt-in to NEGOTIATION, not to being contactable.**
|
|
53
|
+
* On a listing with no cart, unchecking it would mean the buyer has no way to
|
|
54
|
+
* transact whatsoever — the page would render a price and no path to it. So
|
|
55
|
+
* where the offer IS the purchase path, an offer is always permitted; what
|
|
56
|
+
* `allowOffers` then controls is whether the buyer may propose a price *below*
|
|
57
|
+
* the asking one. See `resolveOfferBounds`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function offersEnabledFor(listingType: ListingType, allowOffers?: boolean): boolean;
|
|
60
|
+
export interface OfferBounds {
|
|
61
|
+
/** Lowest legal amount, inclusive. */
|
|
62
|
+
min: number;
|
|
63
|
+
/** Highest legal amount, inclusive. */
|
|
64
|
+
max: number;
|
|
65
|
+
/**
|
|
66
|
+
* The offer is pinned to the asking price — the buyer is requesting to buy,
|
|
67
|
+
* not negotiating. True when the offer is the only purchase path and the
|
|
68
|
+
* seller has not opted into haggling.
|
|
69
|
+
*
|
|
70
|
+
* Callers use this for COPY, never for a second bounds calculation: `min`
|
|
71
|
+
* and `max` are already equal when it is true.
|
|
72
|
+
*/
|
|
73
|
+
isBuyRequest: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The amounts this listing will accept, as one answer for the form, the schema
|
|
77
|
+
* and the server.
|
|
78
|
+
*
|
|
79
|
+
* Two cases beyond the ordinary one, both driven by there being no cart:
|
|
80
|
+
*
|
|
81
|
+
* - **No haggling opted in** → `min === max === listedPrice`. A "request to buy".
|
|
82
|
+
* - **Haggling opted in** → the usual floor, but the ceiling is the asking price
|
|
83
|
+
* **inclusive**. On a cartable listing an offer at full price is rejected with
|
|
84
|
+
* "use Add to Cart instead", which on a classified is advice the buyer cannot
|
|
85
|
+
* take — there is no cart button to use. That rejection was already a dead end
|
|
86
|
+
* before this change.
|
|
87
|
+
*/
|
|
88
|
+
export declare function resolveOfferBounds(opts: {
|
|
89
|
+
listingType: ListingType;
|
|
90
|
+
listedPrice: number;
|
|
91
|
+
minOfferPercent?: number | null;
|
|
92
|
+
allowOffers?: boolean;
|
|
93
|
+
}): OfferBounds;
|
|
94
|
+
/** Whether `amount` falls within `bounds`. The one comparison, shared. */
|
|
95
|
+
export declare function isWithinOfferBounds(amount: number, bounds: OfferBounds): boolean;
|
|
@@ -11,15 +11,18 @@
|
|
|
11
11
|
* Same discipline as `resolveMinBid` in the auctions config next door.
|
|
12
12
|
*
|
|
13
13
|
* EXPORTS:
|
|
14
|
-
* DEFAULT_MIN_OFFER_PERCENT, minOfferAmount, isOfferAmountValid
|
|
14
|
+
* DEFAULT_MIN_OFFER_PERCENT, minOfferAmount, isOfferAmountValid,
|
|
15
|
+
* offerIsOnlyPurchasePath, offersEnabledFor, resolveOfferBounds,
|
|
16
|
+
* type OfferBounds
|
|
15
17
|
*
|
|
16
18
|
* @tag domain:offers,seller
|
|
17
19
|
* @tag layer:shared
|
|
18
20
|
* @tag pattern:none
|
|
19
21
|
* @tag access:isomorphic
|
|
20
|
-
* @tag consumers:offer-actions,MakeOfferButton,ProductDetailPageView,ProductForm,SellerProductShell
|
|
22
|
+
* @tag consumers:offer-actions,MakeOfferButton,ProductDetailPageView,ClassifiedDetailPageView,ProductForm,SellerProductShell
|
|
21
23
|
* @tag sideEffects:none
|
|
22
24
|
*/
|
|
25
|
+
import { canAddToCart, canMakeOffer, } from "../../listing-types/capabilities";
|
|
23
26
|
/**
|
|
24
27
|
* The floor, as a percentage of the listed price, below which an offer is
|
|
25
28
|
* rejected — used whenever a listing does not set its own `minOfferPercent`.
|
|
@@ -57,3 +60,71 @@ export function minOfferAmount(listedPrice, percent) {
|
|
|
57
60
|
export function isOfferAmountValid(amount, listedPrice, percent) {
|
|
58
61
|
return amount >= minOfferAmount(listedPrice, percent) && amount < listedPrice;
|
|
59
62
|
}
|
|
63
|
+
/* -------------------------------------------------------------------------
|
|
64
|
+
* Listings whose ONLY purchase path is an offer
|
|
65
|
+
* ---------------------------------------------------------------------- */
|
|
66
|
+
/**
|
|
67
|
+
* Is Make-an-Offer the only way to buy this listing type?
|
|
68
|
+
*
|
|
69
|
+
* True exactly when the type can be offered on but has no cart line — today
|
|
70
|
+
* that is `classified` alone (`canAddToCart: false`, `canMakeOffer: true`).
|
|
71
|
+
*
|
|
72
|
+
* Derived from the capability registry rather than testing for `"classified"`,
|
|
73
|
+
* so a future type that lands in the same shape inherits the rule instead of
|
|
74
|
+
* silently becoming unbuyable. That is not hypothetical: this function exists
|
|
75
|
+
* because classified's actual purchase path was a `conversations` chat thread,
|
|
76
|
+
* and when that feature was deleted the listing type would have been left with
|
|
77
|
+
* no way to transact at all.
|
|
78
|
+
*/
|
|
79
|
+
export function offerIsOnlyPurchasePath(listingType) {
|
|
80
|
+
return canMakeOffer(listingType) && !canAddToCart(listingType);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* May a buyer send an offer on this listing at all?
|
|
84
|
+
*
|
|
85
|
+
* The two ordinary gates are the type's `canMakeOffer` capability and the
|
|
86
|
+
* seller's own `allowOffers` opt-in. The third clause is the point:
|
|
87
|
+
*
|
|
88
|
+
* 🛑 **`allowOffers` is an opt-in to NEGOTIATION, not to being contactable.**
|
|
89
|
+
* On a listing with no cart, unchecking it would mean the buyer has no way to
|
|
90
|
+
* transact whatsoever — the page would render a price and no path to it. So
|
|
91
|
+
* where the offer IS the purchase path, an offer is always permitted; what
|
|
92
|
+
* `allowOffers` then controls is whether the buyer may propose a price *below*
|
|
93
|
+
* the asking one. See `resolveOfferBounds`.
|
|
94
|
+
*/
|
|
95
|
+
export function offersEnabledFor(listingType, allowOffers) {
|
|
96
|
+
if (!canMakeOffer(listingType))
|
|
97
|
+
return false;
|
|
98
|
+
return allowOffers === true || offerIsOnlyPurchasePath(listingType);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The amounts this listing will accept, as one answer for the form, the schema
|
|
102
|
+
* and the server.
|
|
103
|
+
*
|
|
104
|
+
* Two cases beyond the ordinary one, both driven by there being no cart:
|
|
105
|
+
*
|
|
106
|
+
* - **No haggling opted in** → `min === max === listedPrice`. A "request to buy".
|
|
107
|
+
* - **Haggling opted in** → the usual floor, but the ceiling is the asking price
|
|
108
|
+
* **inclusive**. On a cartable listing an offer at full price is rejected with
|
|
109
|
+
* "use Add to Cart instead", which on a classified is advice the buyer cannot
|
|
110
|
+
* take — there is no cart button to use. That rejection was already a dead end
|
|
111
|
+
* before this change.
|
|
112
|
+
*/
|
|
113
|
+
export function resolveOfferBounds(opts) {
|
|
114
|
+
const { listingType, listedPrice, minOfferPercent, allowOffers } = opts;
|
|
115
|
+
const onlyPath = offerIsOnlyPurchasePath(listingType);
|
|
116
|
+
if (onlyPath && allowOffers !== true) {
|
|
117
|
+
return { min: listedPrice, max: listedPrice, isBuyRequest: true };
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
min: minOfferAmount(listedPrice, minOfferPercent),
|
|
121
|
+
// `- 0.01` and not `- 1`: money is decimal rupees, and this is the exact
|
|
122
|
+
// ceiling `makeOfferFormSchema` has always enforced.
|
|
123
|
+
max: onlyPath ? listedPrice : Math.round((listedPrice - 0.01) * 100) / 100,
|
|
124
|
+
isBuyRequest: false,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** Whether `amount` falls within `bounds`. The one comparison, shared. */
|
|
128
|
+
export function isWithinOfferBounds(amount, bounds) {
|
|
129
|
+
return amount >= bounds.min && amount <= bounds.max;
|
|
130
|
+
}
|
|
@@ -13,7 +13,7 @@ export declare const createOrderSchema: z.ZodObject<{
|
|
|
13
13
|
quantity: number;
|
|
14
14
|
productId: string;
|
|
15
15
|
}>, "many">;
|
|
16
|
-
shippingAddress: z.ZodObject<{
|
|
16
|
+
shippingAddress: z.ZodEffects<z.ZodObject<{
|
|
17
17
|
fullName: z.ZodString;
|
|
18
18
|
phone: z.ZodString;
|
|
19
19
|
addressLine1: z.ZodString;
|
|
@@ -40,6 +40,24 @@ export declare const createOrderSchema: z.ZodObject<{
|
|
|
40
40
|
pincode: string;
|
|
41
41
|
country?: string | undefined;
|
|
42
42
|
addressLine2?: string | undefined;
|
|
43
|
+
}>, {
|
|
44
|
+
country: string;
|
|
45
|
+
fullName: string;
|
|
46
|
+
phone: string;
|
|
47
|
+
addressLine1: string;
|
|
48
|
+
city: string;
|
|
49
|
+
state: string;
|
|
50
|
+
pincode: string;
|
|
51
|
+
addressLine2?: string | undefined;
|
|
52
|
+
}, {
|
|
53
|
+
fullName: string;
|
|
54
|
+
phone: string;
|
|
55
|
+
addressLine1: string;
|
|
56
|
+
city: string;
|
|
57
|
+
state: string;
|
|
58
|
+
pincode: string;
|
|
59
|
+
country?: string | undefined;
|
|
60
|
+
addressLine2?: string | undefined;
|
|
43
61
|
}>;
|
|
44
62
|
paymentMethod: z.ZodEnum<["razorpay", "cod", "upi"]>;
|
|
45
63
|
couponCode: z.ZodOptional<z.ZodString>;
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { DEFAULT_COUNTRY, isValidPostalCode, postalLabelFor, } from "../../../../constants/geo/countries";
|
|
2
3
|
import { ORDER_CANCEL_REASON_MAX_LENGTH, ORDER_NOTE_MAX_LENGTH, ORDER_TRACKING_NUMBER_MAX_LENGTH, } from "./config";
|
|
4
|
+
/*
|
|
5
|
+
* The order's own SNAPSHOT of where it shipped, denormalised onto the order
|
|
6
|
+
* document at creation. Its field names (`pincode`) are stored on every
|
|
7
|
+
* existing order, so renaming them to match `AddressDocument` is a data
|
|
8
|
+
* migration rather than a refactor — and a snapshot deliberately does NOT
|
|
9
|
+
* follow the address it was copied from.
|
|
10
|
+
*
|
|
11
|
+
* The postal RULE is shared, which is the half that was wrong: `/^\d{6}$/`
|
|
12
|
+
* is India-only, on an order whose country is whatever the buyer chose.
|
|
13
|
+
*/
|
|
14
|
+
// audit-address-shape-ok: an order's frozen shipping snapshot, not the address entity
|
|
3
15
|
const addressSchema = z.object({
|
|
4
16
|
fullName: z.string().min(1),
|
|
5
17
|
phone: z.string().min(10).max(15),
|
|
@@ -7,8 +19,16 @@ const addressSchema = z.object({
|
|
|
7
19
|
addressLine2: z.string().optional(),
|
|
8
20
|
city: z.string().min(1),
|
|
9
21
|
state: z.string().min(1),
|
|
10
|
-
pincode: z.string().
|
|
11
|
-
country: z.string().default(
|
|
22
|
+
pincode: z.string().trim().min(1, "A postal code is required."),
|
|
23
|
+
country: z.string().default(DEFAULT_COUNTRY),
|
|
24
|
+
}).superRefine((v, ctx) => {
|
|
25
|
+
if (!isValidPostalCode(v.country, v.pincode)) {
|
|
26
|
+
ctx.addIssue({
|
|
27
|
+
code: z.ZodIssueCode.custom,
|
|
28
|
+
path: ["pincode"],
|
|
29
|
+
message: `That is not a valid ${postalLabelFor(v.country)}.`,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
12
32
|
});
|
|
13
33
|
export const createOrderSchema = z.object({
|
|
14
34
|
items: z.array(z.object({
|
|
@@ -25,9 +25,18 @@ export interface PublicSiteSettings {
|
|
|
25
25
|
payment: {
|
|
26
26
|
otpCheckoutThreshold?: number;
|
|
27
27
|
};
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Which listing types the site offers — the client needs this to avoid
|
|
30
|
+
* rendering a browse tab for a type nothing will return.
|
|
31
|
+
*
|
|
32
|
+
* Deliberately NOT the whole `listings` block: `categoryTypes` has no client
|
|
33
|
+
* reader. This was `featureFlags.listingTypes` until that group was deleted
|
|
34
|
+
* on 2026-08-29; the projection stayed narrow for the same reason it always
|
|
35
|
+
* was — a public payload carries what a client demonstrably reads, nothing
|
|
36
|
+
* more (Root Cause #70).
|
|
37
|
+
*/
|
|
38
|
+
listings: {
|
|
39
|
+
listingTypes?: NonNullable<SiteSettingsDocument["listings"]>["listingTypes"];
|
|
31
40
|
};
|
|
32
41
|
notificationChannels?: NotificationChannelConfig;
|
|
33
42
|
announcementBar?: SiteSettingsDocument["announcementBar"];
|