@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
|
@@ -13,22 +13,90 @@ type RoleCarrier = {
|
|
|
13
13
|
role?: string | null;
|
|
14
14
|
} | null | undefined;
|
|
15
15
|
export declare const isAdminUser: (input: RoleCarrier) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* True for sellers AND testers.
|
|
18
|
+
*
|
|
19
|
+
* A QA tester owns a real store — `becomeSeller`/`createStore` auto-approve it —
|
|
20
|
+
* so every seller-facing surface must admit them. Making `tester` a peer role
|
|
21
|
+
* of `seller` rather than a boolean is what removed the `isTester` flag; this
|
|
22
|
+
* predicate is where that peerage is expressed.
|
|
23
|
+
*/
|
|
16
24
|
export declare const isSellerUser: (input: RoleCarrier) => boolean;
|
|
25
|
+
/** Strictly the `seller` role — for the rare case that must EXCLUDE testers. */
|
|
26
|
+
export declare const isStrictSellerUser: (input: RoleCarrier) => boolean;
|
|
17
27
|
export declare const isModeratorUser: (input: RoleCarrier) => boolean;
|
|
18
28
|
export declare const isEmployeeUser: (input: RoleCarrier) => boolean;
|
|
19
29
|
export declare const isBuyerUser: (input: RoleCarrier) => boolean;
|
|
20
30
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
31
|
+
* ── Tester identity: role first, legacy booleans second ──────────────────────
|
|
32
|
+
*
|
|
33
|
+
* `isTester` / `canTestAdmin` are being replaced by the `tester` role and the
|
|
34
|
+
* `tester:admin-surfaces` permission. Both predicates below read the NEW shape
|
|
35
|
+
* first and fall back to the OLD one, which is what makes the code safe to ship
|
|
36
|
+
* before the data migration: a tester whose document still says
|
|
37
|
+
* `{ role: "seller", isTester: true }` keeps working unchanged, and one already
|
|
38
|
+
* migrated to `{ role: "tester" }` works too.
|
|
39
|
+
*
|
|
40
|
+
* ⛔ Do NOT delete the fallbacks until the backfill has run and been verified.
|
|
41
|
+
* Removing them early locks every existing tester out of the Tester Hub and out
|
|
42
|
+
* of the auto-approved store the QA program depends on.
|
|
27
43
|
*/
|
|
28
|
-
type
|
|
44
|
+
type TesterCarrier = {
|
|
29
45
|
role?: string | null;
|
|
30
46
|
isTester?: boolean;
|
|
31
47
|
canTestAdmin?: boolean;
|
|
48
|
+
permissions?: readonly string[] | null;
|
|
32
49
|
} | null | undefined;
|
|
50
|
+
/** The `tester` role, or the legacy `isTester` flag. */
|
|
51
|
+
export declare const isTesterUser: (input: TesterCarrier) => boolean;
|
|
52
|
+
/**
|
|
53
|
+
* May this tester reach `/admin/**`?
|
|
54
|
+
*
|
|
55
|
+
* The `tester:admin-surfaces` permission, or the legacy `canTestAdmin` flag.
|
|
56
|
+
* Granted through the same `permissions[]` array employees already use, so it
|
|
57
|
+
* is one grant mechanism rather than a bespoke boolean — and revoking it is a
|
|
58
|
+
* permission change, which the admin audit log already records.
|
|
59
|
+
*/
|
|
60
|
+
export declare const canTestAdminSurfaces: (input: TesterCarrier) => boolean;
|
|
61
|
+
/**
|
|
62
|
+
* True for real admins OR a tester cleared for admin surfaces. Used at the two
|
|
63
|
+
* RBAC chokepoints — the API route guard (`createRouteHandler`) and the admin
|
|
64
|
+
* layout guard (`makeAdminSectionLayout`) — so a cleared tester gets real
|
|
65
|
+
* read/write access to `/admin/**` without holding the admin role.
|
|
66
|
+
*
|
|
67
|
+
* Callers must supply the role/permissions from a live Firestore read; none of
|
|
68
|
+
* these are present in the session-cookie JWT claims (only `role` is).
|
|
69
|
+
*/
|
|
70
|
+
type EffectiveAdminCarrier = TesterCarrier;
|
|
33
71
|
export declare const isEffectiveAdminUser: (input: EffectiveAdminCarrier) => boolean;
|
|
72
|
+
/**
|
|
73
|
+
* ── Is this account blocked? ─────────────────────────────────────────────────
|
|
74
|
+
*
|
|
75
|
+
* 🛑 There are TWO ban fields on `UserDocument` and, until 2026-08-29, no ban
|
|
76
|
+
* path wrote the one every guard reads.
|
|
77
|
+
*
|
|
78
|
+
* `disabled` — required, and read by EVERY server-side guard:
|
|
79
|
+
* auth-server, api-handler, authorization.requireActiveUser,
|
|
80
|
+
* Guards.requireActiveAccount, SessionContext.
|
|
81
|
+
* Its only writers, `userRepository.disable()/enable()`, had
|
|
82
|
+
* ZERO callers.
|
|
83
|
+
* `isDisabled` — optional, and what `hardBanCascade` and the bulk-suspend
|
|
84
|
+
* route actually set, alongside the Firebase Auth record.
|
|
85
|
+
*
|
|
86
|
+
* The consequence was that **no ban terminated an existing session.** A hard
|
|
87
|
+
* ban set Firebase Auth `disabled: true`, which blocks a fresh login — and
|
|
88
|
+
* nothing else. Every authenticated request from a session cookie the user
|
|
89
|
+
* already held kept passing, because each guard consulted a Firestore field
|
|
90
|
+
* the ban never touched. Silent, and invisible to anyone testing a ban by
|
|
91
|
+
* logging out first.
|
|
92
|
+
*
|
|
93
|
+
* This predicate reads BOTH so already-banned accounts are enforced from the
|
|
94
|
+
* moment it ships, without waiting on a backfill. Every write path now sets
|
|
95
|
+
* both fields too; `isDisabled` is removed once the data is reconciled.
|
|
96
|
+
*/
|
|
97
|
+
type BanCarrier = {
|
|
98
|
+
disabled?: boolean | null;
|
|
99
|
+
isDisabled?: boolean | null;
|
|
100
|
+
} | null | undefined;
|
|
101
|
+
export declare const isAccountDisabled: (input: BanCarrier) => boolean;
|
|
34
102
|
export {};
|
|
@@ -2,8 +2,34 @@ function normalizeRole(input) {
|
|
|
2
2
|
return (input?.role ?? "user");
|
|
3
3
|
}
|
|
4
4
|
export const isAdminUser = (input) => normalizeRole(input) === "admin";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* True for sellers AND testers.
|
|
7
|
+
*
|
|
8
|
+
* A QA tester owns a real store — `becomeSeller`/`createStore` auto-approve it —
|
|
9
|
+
* so every seller-facing surface must admit them. Making `tester` a peer role
|
|
10
|
+
* of `seller` rather than a boolean is what removed the `isTester` flag; this
|
|
11
|
+
* predicate is where that peerage is expressed.
|
|
12
|
+
*/
|
|
13
|
+
export const isSellerUser = (input) => {
|
|
14
|
+
const role = normalizeRole(input);
|
|
15
|
+
return role === "seller" || role === "tester";
|
|
16
|
+
};
|
|
17
|
+
/** Strictly the `seller` role — for the rare case that must EXCLUDE testers. */
|
|
18
|
+
export const isStrictSellerUser = (input) => normalizeRole(input) === "seller";
|
|
6
19
|
export const isModeratorUser = (input) => normalizeRole(input) === "moderator";
|
|
7
20
|
export const isEmployeeUser = (input) => normalizeRole(input) === "employee";
|
|
8
21
|
export const isBuyerUser = (input) => normalizeRole(input) === "user";
|
|
9
|
-
|
|
22
|
+
/** The `tester` role, or the legacy `isTester` flag. */
|
|
23
|
+
export const isTesterUser = (input) => normalizeRole(input) === "tester" || input?.isTester === true;
|
|
24
|
+
/**
|
|
25
|
+
* May this tester reach `/admin/**`?
|
|
26
|
+
*
|
|
27
|
+
* The `tester:admin-surfaces` permission, or the legacy `canTestAdmin` flag.
|
|
28
|
+
* Granted through the same `permissions[]` array employees already use, so it
|
|
29
|
+
* is one grant mechanism rather than a bespoke boolean — and revoking it is a
|
|
30
|
+
* permission change, which the admin audit log already records.
|
|
31
|
+
*/
|
|
32
|
+
export const canTestAdminSurfaces = (input) => Boolean(input?.permissions?.includes("tester:admin-surfaces")) ||
|
|
33
|
+
input?.canTestAdmin === true;
|
|
34
|
+
export const isEffectiveAdminUser = (input) => isAdminUser(input) || (isTesterUser(input) && canTestAdminSurfaces(input));
|
|
35
|
+
export const isAccountDisabled = (input) => input?.disabled === true || input?.isDisabled === true;
|
|
@@ -140,6 +140,28 @@ export interface UserDocument extends BaseDocument {
|
|
|
140
140
|
* stay unique). Absent/missing IDs default to collapsed for a section the
|
|
141
141
|
* user has never interacted with. See useCollapsedSections. */
|
|
142
142
|
collapsedSections?: string[];
|
|
143
|
+
/**
|
|
144
|
+
* Which sections are OPEN, namespaced per surface: `scope -> openIds[]`.
|
|
145
|
+
*
|
|
146
|
+
* Supersedes `collapsedSections`, and the polarity flip is the point.
|
|
147
|
+
* Storing what is COLLAPSED cannot express "section 1 open by default"
|
|
148
|
+
* without a sentinel, and an unseen scope has to be guessed at; storing
|
|
149
|
+
* what is OPEN makes a never-seen scope default correctly *by absence*.
|
|
150
|
+
*
|
|
151
|
+
* Namespaced because the flat list was global: every page's ids shared one
|
|
152
|
+
* array and were kept apart only by a naming convention. The scope key is
|
|
153
|
+
* `"{portal}:{surface}"` — deliberately NOT a hash of the section ids,
|
|
154
|
+
* which would change the moment a section is added and silently reset
|
|
155
|
+
* every user's layout.
|
|
156
|
+
*
|
|
157
|
+
* `collapsedSections` is still written during the migration window; see
|
|
158
|
+
* `useSectionState`'s one-way dual-read.
|
|
159
|
+
*/
|
|
160
|
+
sectionState?: Record<string, string[]>;
|
|
161
|
+
/** Preferred density for list surfaces that offer a view switch.
|
|
162
|
+
* Was written by the route, the API type and useDataViewMode while being
|
|
163
|
+
* absent from this interface — declared 2026-08-29. */
|
|
164
|
+
dataViewMode?: "table" | "grid" | "list";
|
|
143
165
|
/** Which side CTA panels/drawers/sidebars dock to and where close buttons
|
|
144
166
|
* sit. Absent/"right" = default (right-docked). "left" = left-hand mode. */
|
|
145
167
|
handMode?: "left" | "right";
|
|
@@ -26,9 +26,9 @@ export declare const loginSchema: z.ZodObject<{
|
|
|
26
26
|
*/
|
|
27
27
|
export declare const registerPasswordSchema: z.ZodString;
|
|
28
28
|
export declare const registerSchema: z.ZodObject<{
|
|
29
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
29
30
|
email: z.ZodString;
|
|
30
31
|
password: z.ZodString;
|
|
31
|
-
displayName: z.ZodOptional<z.ZodString>;
|
|
32
32
|
}, "strip", z.ZodTypeAny, {
|
|
33
33
|
email: string;
|
|
34
34
|
password: string;
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
export * from "./firestore";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { ERROR_MESSAGES } from "../../../errors/messages";
|
|
4
|
+
import { annotate } from "../../shell/field-ui-meta";
|
|
4
5
|
// --- Form schemas -------------------------------------------------------------
|
|
6
|
+
/*
|
|
7
|
+
* The `annotate()` calls below add UI metadata to a WeakMap keyed on the schema
|
|
8
|
+
* object; they return the same schema and change nothing about parsing. That is
|
|
9
|
+
* what makes them safe on `registerSchema`, which the register ROUTE also
|
|
10
|
+
* parses — the server never reads the registry.
|
|
11
|
+
*
|
|
12
|
+
* Every auth form is a single required section. That is not a section in any
|
|
13
|
+
* interesting sense, and it is not meant to be: `sectionRequired` renders the
|
|
14
|
+
* panel open and non-collapsible, so the page looks exactly as it does today
|
|
15
|
+
* and gains the pinned mobile action bar, which is the whole reason to put a
|
|
16
|
+
* short form through <SectionForm> at all.
|
|
17
|
+
*/
|
|
5
18
|
/**
|
|
6
19
|
* Login form schema — use with react-hook-form + zodResolver.
|
|
7
20
|
*
|
|
@@ -10,8 +23,13 @@ import { ERROR_MESSAGES } from "../../../errors/messages";
|
|
|
10
23
|
* const form = useForm({ resolver: zodResolver(loginSchema) });
|
|
11
24
|
*/
|
|
12
25
|
export const loginSchema = z.object({
|
|
13
|
-
email: z.string().email(),
|
|
14
|
-
|
|
26
|
+
email: annotate(z.string().email(), {
|
|
27
|
+
section: "basics", sectionLabel: "Sign in", sectionRequired: true,
|
|
28
|
+
order: 1, row: "full", inputType: "email", label: "Email",
|
|
29
|
+
}),
|
|
30
|
+
password: annotate(z.string().min(6), {
|
|
31
|
+
section: "basics", order: 2, row: "full", inputType: "password", label: "Password",
|
|
32
|
+
}),
|
|
15
33
|
});
|
|
16
34
|
/**
|
|
17
35
|
* Matches the server-side complexity rule enforced in
|
|
@@ -27,16 +45,34 @@ export const registerPasswordSchema = z
|
|
|
27
45
|
.regex(/[a-z]/, ERROR_MESSAGES.PASSWORD.NO_LOWERCASE)
|
|
28
46
|
.regex(/[0-9]/, ERROR_MESSAGES.PASSWORD.NO_NUMBER);
|
|
29
47
|
export const registerSchema = z.object({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
displayName: annotate(z.string().min(1).optional(), {
|
|
49
|
+
section: "basics", sectionLabel: "Create your account", sectionRequired: true,
|
|
50
|
+
order: 1, row: "full", label: "Display name",
|
|
51
|
+
}),
|
|
52
|
+
email: annotate(z.string().email(), {
|
|
53
|
+
section: "basics", order: 2, row: "full", inputType: "email", label: "Email",
|
|
54
|
+
}),
|
|
55
|
+
password: annotate(registerPasswordSchema, {
|
|
56
|
+
section: "basics", order: 3, row: "full", inputType: "password", label: "Password",
|
|
57
|
+
help: "At least 8 characters, with an uppercase letter, a lowercase letter and a number.",
|
|
58
|
+
}),
|
|
33
59
|
});
|
|
34
60
|
export const forgotPasswordSchema = z.object({
|
|
35
|
-
email: z.string().email(),
|
|
61
|
+
email: annotate(z.string().email(), {
|
|
62
|
+
section: "basics", sectionLabel: "Reset your password", sectionRequired: true,
|
|
63
|
+
order: 1, row: "full", inputType: "email", label: "Email",
|
|
64
|
+
}),
|
|
36
65
|
});
|
|
37
66
|
export const resetPasswordSchema = z.object({
|
|
38
|
-
|
|
39
|
-
|
|
67
|
+
// Carried in the reset link, never typed. `derived` keeps it out of the
|
|
68
|
+
// rendered form; the schema still requires it, because the SUBMIT needs it.
|
|
69
|
+
token: annotate(z.string().min(1), {
|
|
70
|
+
section: "basics", order: 1, derived: true, tier: "t2-server",
|
|
71
|
+
}),
|
|
72
|
+
password: annotate(z.string().min(6), {
|
|
73
|
+
section: "basics", sectionLabel: "Choose a new password", sectionRequired: true,
|
|
74
|
+
order: 2, row: "full", inputType: "password", label: "New password",
|
|
75
|
+
}),
|
|
40
76
|
});
|
|
41
77
|
// --- Auth user schema ---------------------------------------------------------
|
|
42
78
|
export const userRoleSchema = z.enum([
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `tester` sits at the SAME level as `seller` (see ROLE_HIERARCHY in
|
|
3
|
+
* security/authorization.ts) because a QA tester owns a real store and must
|
|
4
|
+
* pass every seller gate — the Tester QA Program depends on it.
|
|
5
|
+
*
|
|
6
|
+
* It replaces the `isTester` boolean. The companion `canTestAdmin` boolean
|
|
7
|
+
* became the `tester:admin-surfaces` permission rather than a second role,
|
|
8
|
+
* because this hierarchy is LINEAR: there is no level that means
|
|
9
|
+
* "seller-level, plus read admin" without also meaning admin.
|
|
10
|
+
*
|
|
11
|
+
* Both booleans are still read during the migration window — see
|
|
12
|
+
* `isTesterUser` / `canTestAdminSurfaces` in ./role-predicates.
|
|
13
|
+
*/
|
|
14
|
+
export type UserRole = "user" | "seller" | "tester" | "moderator" | "employee" | "admin";
|
|
2
15
|
export interface AuthUser {
|
|
3
16
|
id: string;
|
|
4
17
|
email?: string;
|
|
@@ -25,7 +25,15 @@ export async function GET(request) {
|
|
|
25
25
|
try {
|
|
26
26
|
const url = new URL(request.url);
|
|
27
27
|
const page = numParam(url, "page", 1);
|
|
28
|
-
|
|
28
|
+
/*
|
|
29
|
+
* `pageSize` is canonical; `perPage` is still read because this is a public
|
|
30
|
+
* GET and a link built before 2026-08-31 carries the old spelling. Ignoring
|
|
31
|
+
* it would silently serve the default page size with a 200 — the shape of
|
|
32
|
+
* failure this codebase keeps paying for.
|
|
33
|
+
*/
|
|
34
|
+
const pageSize = url.searchParams.has("perPage")
|
|
35
|
+
? numParam(url, "perPage", 20)
|
|
36
|
+
: numParam(url, "pageSize", 20);
|
|
29
37
|
const sort = param(url, "sort") ?? "-publishedAt";
|
|
30
38
|
const parts = [];
|
|
31
39
|
const category = param(url, "category");
|
|
@@ -52,7 +60,7 @@ export async function GET(request) {
|
|
|
52
60
|
// `blogRepository.listAll` rather than the generic `db.getRepository(...)`:
|
|
53
61
|
// only the feature repository knows how to push a searchTxt clause down and
|
|
54
62
|
// AND-refine the remaining terms.
|
|
55
|
-
const result = await blogRepository.listAll({ filters, sorts: sort, page, pageSize
|
|
63
|
+
const result = await blogRepository.listAll({ filters, sorts: sort, page, pageSize }, q ? { search: q } : undefined);
|
|
56
64
|
const meta = {
|
|
57
65
|
total: result.total,
|
|
58
66
|
page: result.page,
|
|
@@ -106,7 +106,7 @@ export function BlogIndexListing({ initialData }) {
|
|
|
106
106
|
tags: table.get(TABLE_KEYS.TAGS) || undefined,
|
|
107
107
|
sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
|
|
108
108
|
page: table.getNumber(TABLE_KEYS.PAGE, 1),
|
|
109
|
-
|
|
109
|
+
pageSize: table.getNumber(TABLE_KEYS.PAGE_SIZE, PAGE_SIZE),
|
|
110
110
|
};
|
|
111
111
|
const { posts, totalPages, isLoading } = useBlogPosts(params, { initialData });
|
|
112
112
|
const currentPage = table.getNumber("page", 1);
|
|
@@ -11,8 +11,8 @@ export function useBlogPosts(params = {}, opts) {
|
|
|
11
11
|
searchParams.set("tags", params.tags);
|
|
12
12
|
if (params.page)
|
|
13
13
|
searchParams.set("page", String(params.page));
|
|
14
|
-
if (params.
|
|
15
|
-
searchParams.set("
|
|
14
|
+
if (params.pageSize)
|
|
15
|
+
searchParams.set("pageSize", String(params.pageSize));
|
|
16
16
|
if (params.sort)
|
|
17
17
|
searchParams.set("sort", params.sort);
|
|
18
18
|
if (params.featured !== undefined)
|
|
@@ -458,6 +458,8 @@ export declare const blogListParamsSchema: z.ZodObject<{
|
|
|
458
458
|
tags: z.ZodOptional<z.ZodString>;
|
|
459
459
|
q: z.ZodOptional<z.ZodString>;
|
|
460
460
|
page: z.ZodOptional<z.ZodNumber>;
|
|
461
|
+
pageSize: z.ZodOptional<z.ZodNumber>;
|
|
462
|
+
/** Accepted, never emitted — a link built before the 2026-08-31 rename. */
|
|
461
463
|
perPage: z.ZodOptional<z.ZodNumber>;
|
|
462
464
|
sort: z.ZodOptional<z.ZodString>;
|
|
463
465
|
featured: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -466,6 +468,7 @@ export declare const blogListParamsSchema: z.ZodObject<{
|
|
|
466
468
|
page?: number | undefined;
|
|
467
469
|
q?: string | undefined;
|
|
468
470
|
perPage?: number | undefined;
|
|
471
|
+
pageSize?: number | undefined;
|
|
469
472
|
category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
|
|
470
473
|
featured?: boolean | undefined;
|
|
471
474
|
tags?: string | undefined;
|
|
@@ -474,6 +477,7 @@ export declare const blogListParamsSchema: z.ZodObject<{
|
|
|
474
477
|
page?: number | undefined;
|
|
475
478
|
q?: string | undefined;
|
|
476
479
|
perPage?: number | undefined;
|
|
480
|
+
pageSize?: number | undefined;
|
|
477
481
|
category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
|
|
478
482
|
featured?: boolean | undefined;
|
|
479
483
|
tags?: string | undefined;
|
|
@@ -90,6 +90,8 @@ export const blogListParamsSchema = z.object({
|
|
|
90
90
|
tags: z.string().optional(),
|
|
91
91
|
q: z.string().optional(),
|
|
92
92
|
page: z.coerce.number().optional(),
|
|
93
|
+
pageSize: z.coerce.number().optional(),
|
|
94
|
+
/** Accepted, never emitted — a link built before the 2026-08-31 rename. */
|
|
93
95
|
perPage: z.coerce.number().optional(),
|
|
94
96
|
sort: z.string().optional(),
|
|
95
97
|
featured: z.coerce.boolean().optional(),
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add or edit an item in a user's personal catalogue.
|
|
3
|
+
*
|
|
4
|
+
* Fields come from `createCatalogueItemSchema`'s annotations. Three need a
|
|
5
|
+
* `renderers` entry: the photo list (a real uploader, not the generator's
|
|
6
|
+
* disabled placeholder), and the two taxonomy selects, which are searchable
|
|
7
|
+
* because 47 categories is well past the 5-option threshold.
|
|
8
|
+
*
|
|
9
|
+
* ## `categorySlugs` and `brandSlug` were declared, read, and never collected
|
|
10
|
+
*
|
|
11
|
+
* Both are inherited from `ProductDraftFields` and both are read by
|
|
12
|
+
* `product-from-item.ts`, which builds the marketplace listing when an item is
|
|
13
|
+
* promoted. The hand-rolled form had no control for either, so every promoted
|
|
14
|
+
* listing carried `categorySlugs: []` — and per the Category Tree rules a
|
|
15
|
+
* product with an empty chain is invisible on every category and brand page it
|
|
16
|
+
* should appear under. A schema field with a reader and no control is the
|
|
17
|
+
* inverse of a dead control, and just as silent.
|
|
18
|
+
*
|
|
19
|
+
* `mainImage` is `t1-derive` — the first photo, not a field anyone types. It
|
|
20
|
+
* renders as a read-only preview rather than a text box that could be desynced
|
|
21
|
+
* from the gallery it is supposed to mirror.
|
|
22
|
+
*/
|
|
1
23
|
import React from "react";
|
|
2
24
|
import type { CatalogueItemDocument } from "../schemas/firestore";
|
|
3
25
|
export interface CatalogueItemEditorViewProps {
|
|
@@ -1,60 +1,120 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Add or edit an item in a user's personal catalogue.
|
|
5
|
+
*
|
|
6
|
+
* Fields come from `createCatalogueItemSchema`'s annotations. Three need a
|
|
7
|
+
* `renderers` entry: the photo list (a real uploader, not the generator's
|
|
8
|
+
* disabled placeholder), and the two taxonomy selects, which are searchable
|
|
9
|
+
* because 47 categories is well past the 5-option threshold.
|
|
10
|
+
*
|
|
11
|
+
* ## `categorySlugs` and `brandSlug` were declared, read, and never collected
|
|
12
|
+
*
|
|
13
|
+
* Both are inherited from `ProductDraftFields` and both are read by
|
|
14
|
+
* `product-from-item.ts`, which builds the marketplace listing when an item is
|
|
15
|
+
* promoted. The hand-rolled form had no control for either, so every promoted
|
|
16
|
+
* listing carried `categorySlugs: []` — and per the Category Tree rules a
|
|
17
|
+
* product with an empty chain is invisible on every category and brand page it
|
|
18
|
+
* should appear under. A schema field with a reader and no control is the
|
|
19
|
+
* inverse of a dead control, and just as silent.
|
|
20
|
+
*
|
|
21
|
+
* `mainImage` is `t1-derive` — the first photo, not a field anyone types. It
|
|
22
|
+
* renders as a read-only preview rather than a text box that could be desynced
|
|
23
|
+
* from the gallery it is supposed to mirror.
|
|
24
|
+
*/
|
|
3
25
|
import React from "react";
|
|
4
26
|
import { useQueryClient } from "@tanstack/react-query";
|
|
5
27
|
import { useApiMutation } from "@mohasinac/appkit/client";
|
|
6
|
-
import {
|
|
7
|
-
import { FormErrorSummary } from "../../../ui/forms";
|
|
8
|
-
import { MediaUploadList } from "../../media/upload/MediaUploadList";
|
|
9
|
-
import { useMediaUpload } from "../../media";
|
|
28
|
+
import { ACCOUNT_ENDPOINTS, BRAND_ENDPOINTS, CATEGORY_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
29
|
import { apiClient } from "../../../http";
|
|
11
|
-
import {
|
|
30
|
+
import { Alert, PaginatedSelect, Stack } from "../../../ui";
|
|
31
|
+
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
|
|
32
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
33
|
+
import { FormShellContext, useFormShellState } from "../../../ui/forms/FormShell";
|
|
34
|
+
import { buildSectionsFromSchema } from "../../shell/build-sections";
|
|
35
|
+
import { SectionForm, useSectionFormNav } from "../../shell/SectionForm";
|
|
36
|
+
import { useMediaUpload } from "../../media";
|
|
37
|
+
import { MediaUploadList } from "../../media/upload/MediaUploadList";
|
|
12
38
|
import { createCatalogueItemSchema } from "../schemas/validation";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{
|
|
21
|
-
]
|
|
39
|
+
/** PUBLIC endpoints — this is a buyer surface; the admin ones would 403. */
|
|
40
|
+
async function loadOptionsFrom(endpoint, query, page) {
|
|
41
|
+
const params = new URLSearchParams({ page: String(page), pageSize: "25" });
|
|
42
|
+
if (query)
|
|
43
|
+
params.set("q", query);
|
|
44
|
+
const res = await apiClient.get(`${endpoint}?${params.toString()}`);
|
|
45
|
+
const data = res ?? {};
|
|
46
|
+
return {
|
|
47
|
+
items: (data.items ?? []).map((c) => ({ value: c.id, label: c.name })),
|
|
48
|
+
hasMore: data.hasMore ?? false,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const loadCategoryOptions = (q, p) => loadOptionsFrom(CATEGORY_ENDPOINTS.LIST, q, p);
|
|
52
|
+
const loadBrandOptions = (q, p) => loadOptionsFrom(BRAND_ENDPOINTS.LIST, q, p);
|
|
22
53
|
export function CatalogueItemEditorView({ item, onSaved }) {
|
|
23
54
|
const isCreate = !item;
|
|
24
55
|
const queryClient = useQueryClient();
|
|
25
56
|
const { upload } = useMediaUpload();
|
|
26
57
|
const uploadIndexRef = React.useRef(0);
|
|
27
|
-
const [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
58
|
+
const [form, setForm] = React.useState({
|
|
59
|
+
title: item?.title ?? "",
|
|
60
|
+
description: item?.description ?? "",
|
|
61
|
+
condition: item?.condition ?? "",
|
|
62
|
+
quantity: item?.quantity ?? 1,
|
|
63
|
+
price: item?.price ?? 0,
|
|
64
|
+
images: item?.images ?? [],
|
|
65
|
+
mainImage: item?.mainImage ?? item?.images?.[0] ?? "",
|
|
66
|
+
categorySlugs: item?.categorySlugs ?? [],
|
|
67
|
+
brandSlug: item?.brandSlug ?? "",
|
|
68
|
+
visibility: item?.visibility ?? "public",
|
|
69
|
+
});
|
|
70
|
+
const handleUpload = React.useCallback(async (file) => {
|
|
36
71
|
uploadIndexRef.current += 1;
|
|
37
72
|
return upload(file, "catalogue", true, {
|
|
38
73
|
type: "catalogue-image",
|
|
39
|
-
item: title || "item",
|
|
74
|
+
item: form.title || "item",
|
|
40
75
|
index: uploadIndexRef.current,
|
|
41
76
|
});
|
|
42
|
-
};
|
|
77
|
+
}, [upload, form.title]);
|
|
78
|
+
const sections = React.useMemo(() => buildSectionsFromSchema(createCatalogueItemSchema, {
|
|
79
|
+
renderers: {
|
|
80
|
+
images: ({ values, onChange }) => (_jsx(MediaUploadList, { label: "Photos", value: values.images.map((url) => ({ url, type: "image" })), onChange: (fields) => {
|
|
81
|
+
const urls = fields.map((f) => f.url);
|
|
82
|
+
// `mainImage` is derived, so it moves with the gallery rather
|
|
83
|
+
// than pointing at a photo the user has since removed.
|
|
84
|
+
onChange({ images: urls, mainImage: urls[0] ?? "" });
|
|
85
|
+
}, onUpload: handleUpload, accept: "image/*", maxItems: 8, helperText: "Photos older than 30 days must be refreshed before this item can be listed." })),
|
|
86
|
+
categorySlugs: ({ values, onChange }) => (
|
|
87
|
+
// Explicit `<string>`: with `multiple`, `value` is `V[]`, so an
|
|
88
|
+
// inferred V reads the array itself as the option type.
|
|
89
|
+
_jsx(PaginatedSelect, { multiple: true, value: values.categorySlugs, onChange: (v) => onChange({ categorySlugs: v ?? [] }), loadOptions: loadCategoryOptions, placeholder: "Search categories\u2026", searchPlaceholder: "Type a category name\u2026", noResultsText: "No categories found", ariaLabel: "Categories" })),
|
|
90
|
+
brandSlug: ({ values, onChange }) => (_jsx(PaginatedSelect, { value: values.brandSlug || null, onChange: (v) => onChange({ brandSlug: v ?? "" }), loadOptions: loadBrandOptions, placeholder: "Search brands\u2026", searchPlaceholder: "Type a brand name\u2026", noResultsText: "No brands found", ariaLabel: "Brand" })),
|
|
91
|
+
},
|
|
92
|
+
}), [handleUpload]);
|
|
93
|
+
const nav = useSectionFormNav(sections, form, { scope: "user:catalogue-item" });
|
|
94
|
+
const { shellCtx, setFieldError, clearErrors } = useFormShellState(createCatalogueItemSchema, {
|
|
95
|
+
sections: nav.sectionMeta,
|
|
96
|
+
onGoToSection: nav.goToSection,
|
|
97
|
+
fieldToSectionIndex: nav.fieldToSectionIndex,
|
|
98
|
+
});
|
|
43
99
|
const saveMutation = useApiMutation({
|
|
44
100
|
successMessage: isCreate ? "Added to your catalogue" : "Catalogue item updated",
|
|
101
|
+
errorMessage: isCreate ? "Could not add this item." : "Could not save your changes.",
|
|
45
102
|
mutationFn: async () => {
|
|
46
103
|
const payload = {
|
|
47
|
-
title,
|
|
48
|
-
description: description || undefined,
|
|
49
|
-
condition: condition || undefined,
|
|
50
|
-
price: Math.round(Number(price) * 100) / 100,
|
|
51
|
-
quantity: Number(quantity),
|
|
52
|
-
visibility,
|
|
53
|
-
images,
|
|
54
|
-
mainImage: images[0],
|
|
104
|
+
title: form.title,
|
|
105
|
+
description: form.description || undefined,
|
|
106
|
+
condition: form.condition || undefined,
|
|
107
|
+
price: Math.round(Number(form.price) * 100) / 100,
|
|
108
|
+
quantity: Number(form.quantity),
|
|
109
|
+
visibility: form.visibility,
|
|
110
|
+
images: form.images,
|
|
111
|
+
mainImage: form.images[0],
|
|
112
|
+
categorySlugs: form.categorySlugs,
|
|
113
|
+
brandSlug: form.brandSlug || undefined,
|
|
55
114
|
};
|
|
56
|
-
if (isCreate)
|
|
115
|
+
if (isCreate) {
|
|
57
116
|
return apiClient.post(ACCOUNT_ENDPOINTS.CATALOGUE, payload);
|
|
117
|
+
}
|
|
58
118
|
return apiClient.patch(ACCOUNT_ENDPOINTS.CATALOGUE_BY_ID(item.id), payload);
|
|
59
119
|
},
|
|
60
120
|
onSuccess: (saved) => {
|
|
@@ -62,21 +122,21 @@ export function CatalogueItemEditorView({ item, onSaved }) {
|
|
|
62
122
|
onSaved?.(saved.id);
|
|
63
123
|
},
|
|
64
124
|
});
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
125
|
+
const onSubmit = () => {
|
|
126
|
+
clearErrors();
|
|
127
|
+
const parsed = createCatalogueItemSchema.safeParse({
|
|
128
|
+
...form,
|
|
129
|
+
price: Math.round(Number(form.price) * 100) / 100,
|
|
130
|
+
quantity: Number(form.quantity),
|
|
131
|
+
brandSlug: form.brandSlug || undefined,
|
|
132
|
+
});
|
|
133
|
+
if (!parsed.success) {
|
|
134
|
+
applyZodIssues(parsed.error.issues, setFieldError);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
saveMutation.mutate();
|
|
138
|
+
};
|
|
139
|
+
return (_jsxs(Stack, { gap: "md", children: [item?.listingStatus && item.listingStatus !== "not_listed" && (_jsxs(Alert, { variant: "info", children: ["This item is ", item.listingStatus.replace(/_/g, " "), item.listingStatus === "rejected" && item.rejectionReason
|
|
140
|
+
? `: ${item.rejectionReason}`
|
|
141
|
+
: "."] })), _jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: form, onChange: (partial) => setForm((prev) => Object.assign({}, prev, partial)), onSubmit: onSubmit, schema: createCatalogueItemSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: saveMutation.isPending, submitLabel: isCreate ? "Add to Catalogue" : "Save Changes" })] })] }));
|
|
82
142
|
}
|
|
@@ -2,13 +2,13 @@ import { z } from "zod";
|
|
|
2
2
|
export declare const createCatalogueItemSchema: z.ZodObject<{
|
|
3
3
|
title: z.ZodString;
|
|
4
4
|
description: z.ZodOptional<z.ZodString>;
|
|
5
|
+
condition: z.ZodOptional<z.ZodEnum<["new", "like_new", "good", "fair", "poor", "used", "refurbished", "broken", "graded"]>>;
|
|
6
|
+
quantity: z.ZodDefault<z.ZodNumber>;
|
|
5
7
|
images: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
6
8
|
mainImage: z.ZodOptional<z.ZodString>;
|
|
7
|
-
condition: z.ZodOptional<z.ZodEnum<["new", "like_new", "good", "fair", "poor", "used", "refurbished", "broken", "graded"]>>;
|
|
8
9
|
categorySlugs: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
9
10
|
brandSlug: z.ZodOptional<z.ZodString>;
|
|
10
11
|
price: z.ZodOptional<z.ZodNumber>;
|
|
11
|
-
quantity: z.ZodDefault<z.ZodNumber>;
|
|
12
12
|
visibility: z.ZodDefault<z.ZodEnum<["public", "private"]>>;
|
|
13
13
|
}, "strip", z.ZodTypeAny, {
|
|
14
14
|
title: string;
|
|
@@ -36,13 +36,13 @@ export declare const createCatalogueItemSchema: z.ZodObject<{
|
|
|
36
36
|
export declare const updateCatalogueItemSchema: z.ZodObject<{
|
|
37
37
|
title: z.ZodOptional<z.ZodString>;
|
|
38
38
|
description: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
39
|
+
condition: z.ZodOptional<z.ZodOptional<z.ZodEnum<["new", "like_new", "good", "fair", "poor", "used", "refurbished", "broken", "graded"]>>>;
|
|
40
|
+
quantity: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
39
41
|
images: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
|
|
40
42
|
mainImage: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
41
|
-
condition: z.ZodOptional<z.ZodOptional<z.ZodEnum<["new", "like_new", "good", "fair", "poor", "used", "refurbished", "broken", "graded"]>>>;
|
|
42
43
|
categorySlugs: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
43
44
|
brandSlug: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
44
45
|
price: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
45
|
-
quantity: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
46
46
|
visibility: z.ZodOptional<z.ZodDefault<z.ZodEnum<["public", "private"]>>>;
|
|
47
47
|
}, "strip", z.ZodTypeAny, {
|
|
48
48
|
title?: string | undefined;
|