@mohasinac/appkit 4.30.19 → 4.31.1
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 +7 -2
- package/dist/next/index.js +36 -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
|
@@ -7,7 +7,7 @@ import { isChannelHealthy, withChannelRetry } from "../../../_internal/server/no
|
|
|
7
7
|
import { enqueueJob } from "../../jobs/actions/enqueue-job";
|
|
8
8
|
import { serverLogger } from "../../../monitoring";
|
|
9
9
|
import { decryptPii } from "../../../security/index";
|
|
10
|
-
import { resolveNotificationActionUrl, } from "../../../_internal/shared/features/notifications/action-url";
|
|
10
|
+
import { resolveNotificationActionUrl, TYPE_AUDIENCE, } from "../../../_internal/shared/features/notifications/action-url";
|
|
11
11
|
import { renderNotificationEmail } from "../../email/notification-templates";
|
|
12
12
|
/**
|
|
13
13
|
* A notification's `actionUrl` is a site-relative path — correct for the
|
|
@@ -100,46 +100,6 @@ export async function listNotifications(userId, limit = 20) {
|
|
|
100
100
|
export async function getUnreadNotificationCount(userId) {
|
|
101
101
|
return notificationRepository.getUnreadCount(userId);
|
|
102
102
|
}
|
|
103
|
-
/**
|
|
104
|
-
* Which portal each notification type belongs to.
|
|
105
|
-
*
|
|
106
|
-
* `Record<NotificationType, …>`, so a new type cannot compile without an
|
|
107
|
-
* answer — the same discipline `typeToPrefsKey` needed after it silently
|
|
108
|
-
* omitted three types and ignored those users' opt-outs.
|
|
109
|
-
*/
|
|
110
|
-
const TYPE_AUDIENCE = {
|
|
111
|
-
// Buyer-facing: the order lifecycle they are waiting on.
|
|
112
|
-
order_placed: "buyer",
|
|
113
|
-
order_confirmed: "buyer",
|
|
114
|
-
order_shipped: "buyer",
|
|
115
|
-
order_delivered: "buyer",
|
|
116
|
-
order_cancelled: "buyer",
|
|
117
|
-
refund_initiated: "buyer",
|
|
118
|
-
payment_review: "buyer",
|
|
119
|
-
emi_installment_due_soon: "buyer",
|
|
120
|
-
emi_installment_overdue: "buyer",
|
|
121
|
-
prize_won: "buyer",
|
|
122
|
-
prize_reveal_expired: "buyer",
|
|
123
|
-
product_available: "buyer",
|
|
124
|
-
bid_placed: "buyer",
|
|
125
|
-
bid_outbid: "buyer",
|
|
126
|
-
bid_won: "buyer",
|
|
127
|
-
bid_lost: "buyer",
|
|
128
|
-
offer_responded: "buyer",
|
|
129
|
-
offer_counter_accepted: "buyer",
|
|
130
|
-
offer_expired: "buyer",
|
|
131
|
-
review_replied: "buyer",
|
|
132
|
-
catalogue_images_stale: "buyer",
|
|
133
|
-
// Seller-facing.
|
|
134
|
-
auction_ended: "seller", // "your auction closed with no winner"
|
|
135
|
-
offer_received: "seller",
|
|
136
|
-
review_approved: "seller",
|
|
137
|
-
// Account-level, which lives in the user portal for everyone.
|
|
138
|
-
welcome: "buyer",
|
|
139
|
-
account_action: "buyer",
|
|
140
|
-
system: "buyer",
|
|
141
|
-
promotion: "buyer",
|
|
142
|
-
};
|
|
143
103
|
/**
|
|
144
104
|
* Create an in-app notification and fan out to enabled external channels.
|
|
145
105
|
*
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdminActionIndexView — the one control plane for "what can a user do here,
|
|
3
|
+
* and where is it" (D7).
|
|
4
|
+
*
|
|
5
|
+
* ## It absorbs two editors that reached nothing
|
|
6
|
+
*
|
|
7
|
+
* `/admin/navigation` (CRUD over a Firestore collection) and
|
|
8
|
+
* `/admin/settings/navigation` (toggles over `siteSettings.navConfig`, seeded
|
|
9
|
+
* from an 11-item hardcoded array) both existed, and **neither one's data
|
|
10
|
+
* reached any sidebar** — because `filterNavItems` short-circuits on a missing
|
|
11
|
+
* `id` and no nav item had one until W6. Two admin screens editing settings
|
|
12
|
+
* nothing read.
|
|
13
|
+
*
|
|
14
|
+
* This screen lists every entry with its kind, portal, route, deep link and
|
|
15
|
+
* required permission, and the toggle here does reach the sidebar.
|
|
16
|
+
*
|
|
17
|
+
* ## What an admin may change, and what they may not
|
|
18
|
+
*
|
|
19
|
+
* Label, description, keywords, weight and enabled — content. Not `href`, not
|
|
20
|
+
* `kind`, not any permission field: repointing a built-in route is routing,
|
|
21
|
+
* and relaxing a permission is access control. An admin-authored entry brings
|
|
22
|
+
* its own href and is validated at SAVE time, because the audit runs on CI and
|
|
23
|
+
* a runtime-written bad link would 404 until the next run.
|
|
24
|
+
*/
|
|
25
|
+
import React from "react";
|
|
26
|
+
import type { StackedViewShellProps } from "../../../ui";
|
|
27
|
+
export interface AdminActionIndexViewProps extends Omit<StackedViewShellProps, "sections"> {
|
|
28
|
+
labels?: {
|
|
29
|
+
title?: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export declare function AdminActionIndexView({ labels }: AdminActionIndexViewProps): React.JSX.Element;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* AdminActionIndexView — the one control plane for "what can a user do here,
|
|
5
|
+
* and where is it" (D7).
|
|
6
|
+
*
|
|
7
|
+
* ## It absorbs two editors that reached nothing
|
|
8
|
+
*
|
|
9
|
+
* `/admin/navigation` (CRUD over a Firestore collection) and
|
|
10
|
+
* `/admin/settings/navigation` (toggles over `siteSettings.navConfig`, seeded
|
|
11
|
+
* from an 11-item hardcoded array) both existed, and **neither one's data
|
|
12
|
+
* reached any sidebar** — because `filterNavItems` short-circuits on a missing
|
|
13
|
+
* `id` and no nav item had one until W6. Two admin screens editing settings
|
|
14
|
+
* nothing read.
|
|
15
|
+
*
|
|
16
|
+
* This screen lists every entry with its kind, portal, route, deep link and
|
|
17
|
+
* required permission, and the toggle here does reach the sidebar.
|
|
18
|
+
*
|
|
19
|
+
* ## What an admin may change, and what they may not
|
|
20
|
+
*
|
|
21
|
+
* Label, description, keywords, weight and enabled — content. Not `href`, not
|
|
22
|
+
* `kind`, not any permission field: repointing a built-in route is routing,
|
|
23
|
+
* and relaxing a permission is access control. An admin-authored entry brings
|
|
24
|
+
* its own href and is validated at SAVE time, because the audit runs on CI and
|
|
25
|
+
* a runtime-written bad link would 404 until the next run.
|
|
26
|
+
*/
|
|
27
|
+
import React from "react";
|
|
28
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
29
|
+
import { useApiMutation } from "@mohasinac/appkit/client";
|
|
30
|
+
import { Alert, Badge, Row, Span, Stack, Text, Toggle, useToast } from "../../../ui";
|
|
31
|
+
import { apiClient } from "../../../http";
|
|
32
|
+
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
33
|
+
import { DataListingView } from "./DataListingView";
|
|
34
|
+
const ENDPOINT = ADMIN_ENDPOINTS.ACTION_INDEX;
|
|
35
|
+
export function AdminActionIndexView({ labels = {} }) {
|
|
36
|
+
const queryClient = useQueryClient();
|
|
37
|
+
const { showToast } = useToast();
|
|
38
|
+
const query = useQuery({
|
|
39
|
+
queryKey: ["admin", "action-index"],
|
|
40
|
+
queryFn: () => apiClient.get(ENDPOINT),
|
|
41
|
+
staleTime: 60000,
|
|
42
|
+
});
|
|
43
|
+
const payload = query.data?.data;
|
|
44
|
+
const overrides = payload?.control?.entries ?? {};
|
|
45
|
+
const toggleMutation = useApiMutation({
|
|
46
|
+
errorMessage: "Could not update that entry.",
|
|
47
|
+
mutationFn: async (vars) => apiClient.patch(ENDPOINT, {
|
|
48
|
+
entryId: vars.entryId,
|
|
49
|
+
override: { ...overrides[vars.entryId], enabled: vars.enabled },
|
|
50
|
+
}),
|
|
51
|
+
onSuccess: () => {
|
|
52
|
+
showToast("Saved.", "success");
|
|
53
|
+
void queryClient.invalidateQueries({ queryKey: ["admin", "action-index"] });
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
/*
|
|
57
|
+
* Ordered by weight, then label — the same ranking `matchesNavQuery` gives
|
|
58
|
+
* the search, so the admin reading this list sees entries in the order a
|
|
59
|
+
* user would meet them.
|
|
60
|
+
*/
|
|
61
|
+
const rows = React.useMemo(() => [...(payload?.entries ?? [])]
|
|
62
|
+
.sort((a, b) => (b.weight ?? 0) - (a.weight ?? 0) || a.label.localeCompare(b.label))
|
|
63
|
+
.map((entry) => ({
|
|
64
|
+
id: entry.id,
|
|
65
|
+
primary: entry.label,
|
|
66
|
+
secondary: entry.description ?? entry.sectionPath ?? "",
|
|
67
|
+
kind: entry.kind,
|
|
68
|
+
portal: entry.portal,
|
|
69
|
+
href: `${entry.href}${entry.deepLink ?? ""}`,
|
|
70
|
+
permission: entry.requiredPermission ?? "—",
|
|
71
|
+
enabled: overrides[entry.id]?.enabled !== false,
|
|
72
|
+
})), [payload?.entries, overrides]);
|
|
73
|
+
const sync = payload?.sync;
|
|
74
|
+
return (_jsxs(Stack, { gap: "md", children: [sync && sync.orphanedOverrides.length > 0 && (_jsxs(Alert, { variant: "warning", title: "Overrides with no entry", children: [sync.orphanedOverrides.length, " saved override", sync.orphanedOverrides.length === 1 ? "" : "s", " point at entries the code no longer has (", sync.orphanedOverrides.slice(0, 4).join(", "), sync.orphanedOverrides.length > 4 ? "…" : "", "). They are ignored \u2014 clear them when convenient."] })), _jsx(DataListingView, { config: {
|
|
75
|
+
portal: "admin",
|
|
76
|
+
title: labels.title ?? "Action index",
|
|
77
|
+
subtitle: "Every page, setting and toggle a user can reach — and what the search finds them by.",
|
|
78
|
+
search: {
|
|
79
|
+
placeholder: "Search by label, description or keyword…",
|
|
80
|
+
mode: "partial",
|
|
81
|
+
fields: ["label", "description", "keywords"],
|
|
82
|
+
commit: "debounce",
|
|
83
|
+
},
|
|
84
|
+
emptyLabel: "No entries match.",
|
|
85
|
+
queryKey: ["admin", "action-index", "listing"],
|
|
86
|
+
endpoint: ENDPOINT,
|
|
87
|
+
filterKeys: ["portal", "kind"],
|
|
88
|
+
defaultSort: "",
|
|
89
|
+
/*
|
|
90
|
+
* 🛑 No sort dropdown, on purpose.
|
|
91
|
+
*
|
|
92
|
+
* This endpoint returns ONE document — the whole index, ~380 entries
|
|
93
|
+
* — and does not sort. Offering a dropdown here would be Root Cause
|
|
94
|
+
* #63 exactly: an option whose field the query never applies, so the
|
|
95
|
+
* control reorders nothing and says nothing about it. The rows are
|
|
96
|
+
* ordered by weight then label below, which is the ranking the
|
|
97
|
+
* search itself uses.
|
|
98
|
+
*/
|
|
99
|
+
sortOptions: [],
|
|
100
|
+
mapRows: () => rows,
|
|
101
|
+
getTotal: () => rows.length,
|
|
102
|
+
buildFilters: () => undefined,
|
|
103
|
+
columns: [
|
|
104
|
+
{ key: "primary", header: "Entry" },
|
|
105
|
+
{
|
|
106
|
+
key: "kind",
|
|
107
|
+
header: "Kind",
|
|
108
|
+
render: (row) => _jsx(Badge, { variant: "default", children: row.kind }),
|
|
109
|
+
},
|
|
110
|
+
{ key: "portal", header: "Portal" },
|
|
111
|
+
{
|
|
112
|
+
key: "href",
|
|
113
|
+
header: "Goes to",
|
|
114
|
+
render: (row) => (_jsx(Span, { size: "xs", family: "mono", color: "muted", children: row.href })),
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
key: "permission",
|
|
118
|
+
header: "Needs",
|
|
119
|
+
render: (row) => (_jsx(Span, { size: "xs", color: "muted", children: row.permission })),
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
key: "enabled",
|
|
123
|
+
header: "Shown",
|
|
124
|
+
render: (row) => (_jsx(Row, { align: "center", gap: "sm", children: _jsx(Toggle, { checked: row.enabled, size: "sm", "aria-label": `Show ${row.primary}`, onChange: (next) => toggleMutation.mutate({ entryId: row.id, enabled: next }) }) })),
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
} }), _jsx(Text, { size: "xs", color: "muted", children: "Hiding an entry removes it from the sidebar, the header search and the command palette at once. Labels, descriptions and keywords can be edited here; a route and its required permission belong to the code." })] }));
|
|
128
|
+
}
|
|
@@ -1,55 +1,60 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { normalizeError } from "../../../errors/normalize";
|
|
4
3
|
import { useApiMutation } from "@mohasinac/appkit/client";
|
|
5
4
|
import React from "react";
|
|
6
5
|
import { useQuery } from "@tanstack/react-query";
|
|
7
|
-
import { Alert,
|
|
6
|
+
import { Alert, Checkbox, Div, Span, Stack, StackedViewShell, Text, useToast } from "../../../ui";
|
|
8
7
|
import { apiClient } from "../../../http";
|
|
9
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
9
|
import { adminAdFormSchema } from "../schemas/admin-ops-forms";
|
|
11
10
|
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
11
|
+
import { FormShellContext, useFormShellState } from "../../../ui/forms/FormShell";
|
|
12
|
+
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
|
|
13
|
+
import { buildSectionsFromSchema, visibleValues } from "../../shell/build-sections";
|
|
14
|
+
import { SectionForm, useSectionFormNav } from "../../shell/SectionForm";
|
|
12
15
|
const __P = {
|
|
13
16
|
p3: "p-[var(--appkit-space-3)]",
|
|
14
17
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
18
|
+
const PROVIDER_OPTIONS = [
|
|
19
|
+
{ label: "Manual", value: "manual" },
|
|
20
|
+
{ label: "AdSense", value: "adsense" },
|
|
21
|
+
{ label: "Third Party", value: "thirdParty" },
|
|
22
|
+
];
|
|
23
|
+
const STATUS_OPTIONS = [
|
|
24
|
+
{ label: "Draft", value: "draft" },
|
|
25
|
+
{ label: "Active", value: "active" },
|
|
26
|
+
{ label: "Scheduled", value: "scheduled" },
|
|
27
|
+
{ label: "Paused", value: "paused" },
|
|
28
|
+
];
|
|
29
|
+
const EMPTY_AD = {
|
|
30
|
+
name: "",
|
|
31
|
+
provider: "manual",
|
|
32
|
+
status: "draft",
|
|
33
|
+
priority: 0,
|
|
34
|
+
placementIds: [],
|
|
35
|
+
requiresConsent: false,
|
|
36
|
+
startAt: "",
|
|
37
|
+
endAt: "",
|
|
38
|
+
creativeTitle: "",
|
|
39
|
+
creativeBody: "",
|
|
40
|
+
creativeImageUrl: "",
|
|
41
|
+
ctaLabel: "",
|
|
42
|
+
ctaHref: "",
|
|
43
|
+
adsenseSlot: "",
|
|
44
|
+
thirdPartyUrl: "",
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* The placement checkboxes.
|
|
48
|
+
*
|
|
49
|
+
* Its options come from a live query, so it cannot be a generated select;
|
|
50
|
+
* module level rather than inline for the DEEP_NESTING reason.
|
|
51
|
+
*/
|
|
52
|
+
function PlacementChecklist({ placements, selected, onToggle, }) {
|
|
53
|
+
return (_jsx(Stack, { gap: "sm", children: _jsx(Div, { layout: "grid", gap: "2", className: "grid-cols-1 md:grid-cols-2", children: placements.map((placement) => (_jsxs("label", { className: "flex items-center gap-[var(--appkit-space-2)] rounded-md border border-[var(--appkit-color-border)] px-[var(--appkit-space-3)] py-[var(--appkit-space-2)]", children: [_jsx(Checkbox, { bare: true, checked: selected.includes(placement.id), onChange: () => onToggle(placement.id) }), _jsx(Span, { size: "sm", children: placement.label })] }, placement.id))) }) }));
|
|
36
54
|
}
|
|
37
55
|
export function AdminAdEditorView({ adId, endpointBase = ADMIN_ENDPOINTS.ADS, labels = {}, onSaved, ...rest }) {
|
|
38
|
-
const [
|
|
39
|
-
const
|
|
40
|
-
const [status, setStatus] = React.useState("draft");
|
|
41
|
-
const [priority, setPriority] = React.useState(0);
|
|
42
|
-
const [requiresConsent, setRequiresConsent] = React.useState(false);
|
|
43
|
-
const [selectedPlacements, setSelectedPlacements] = React.useState([]);
|
|
44
|
-
const [startAt, setStartAt] = React.useState("");
|
|
45
|
-
const [endAt, setEndAt] = React.useState("");
|
|
46
|
-
const [title, setTitle] = React.useState("");
|
|
47
|
-
const [body, setBody] = React.useState("");
|
|
48
|
-
const [imageUrl, setImageUrl] = React.useState("");
|
|
49
|
-
const [ctaLabel, setCtaLabel] = React.useState("");
|
|
50
|
-
const [ctaHref, setCtaHref] = React.useState("");
|
|
51
|
-
const [adsenseSlot, setAdsenseSlot] = React.useState("");
|
|
52
|
-
const [thirdPartyUrl, setThirdPartyUrl] = React.useState("");
|
|
56
|
+
const [form, setForm] = React.useState(EMPTY_AD);
|
|
57
|
+
const patch = (partial) => setForm((prev) => Object.assign({}, prev, partial));
|
|
53
58
|
const { showToast } = useToast();
|
|
54
59
|
const metaQuery = useQuery({
|
|
55
60
|
queryKey: ["admin-ads-editor-meta", endpointBase],
|
|
@@ -63,45 +68,59 @@ export function AdminAdEditorView({ adId, endpointBase = ADMIN_ENDPOINTS.ADS, la
|
|
|
63
68
|
staleTime: 15000,
|
|
64
69
|
});
|
|
65
70
|
React.useEffect(() => {
|
|
66
|
-
|
|
71
|
+
const item = adQuery.data?.item;
|
|
72
|
+
if (!item)
|
|
67
73
|
return;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
patch({
|
|
75
|
+
name: item.name || "",
|
|
76
|
+
provider: item.provider || "manual",
|
|
77
|
+
status: item.status || "draft",
|
|
78
|
+
priority: item.priority ?? 0,
|
|
79
|
+
requiresConsent: Boolean(item.requiresConsent),
|
|
80
|
+
placementIds: item.placementIds ?? [],
|
|
81
|
+
startAt: item.startAt || "",
|
|
82
|
+
endAt: item.endAt || "",
|
|
83
|
+
creativeTitle: item.creative?.title || "",
|
|
84
|
+
creativeBody: item.creative?.body || "",
|
|
85
|
+
creativeImageUrl: item.creative?.imageUrl || "",
|
|
86
|
+
ctaLabel: item.creative?.ctaLabel || "",
|
|
87
|
+
ctaHref: item.creative?.ctaHref || "",
|
|
88
|
+
adsenseSlot: item.creative?.adsenseSlot || "",
|
|
89
|
+
thirdPartyUrl: item.creative?.thirdPartyUrl || "",
|
|
90
|
+
});
|
|
91
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
84
92
|
}, [adQuery.data]);
|
|
85
93
|
const saveMutation = useApiMutation({
|
|
86
94
|
errorMessage: "Save failed",
|
|
87
95
|
mutationFn: async () => {
|
|
96
|
+
/*
|
|
97
|
+
* `visibleValues` is what keeps a provider-specific field out of the
|
|
98
|
+
* payload when its input is not on screen.
|
|
99
|
+
*
|
|
100
|
+
* Both `adsenseSlot` and `thirdPartyUrl` used to go out unconditionally
|
|
101
|
+
* while their inputs unmount on a provider switch — so an ad created as
|
|
102
|
+
* AdSense and switched to "manual" shipped a stale slot nothing could
|
|
103
|
+
* show or clear. The rule was then restated as a ternary right here; it
|
|
104
|
+
* now lives once, on the schema, as the same predicate that hides them.
|
|
105
|
+
*/
|
|
106
|
+
const draft = visibleValues(adminAdFormSchema, form);
|
|
88
107
|
const payload = {
|
|
89
|
-
name,
|
|
90
|
-
provider,
|
|
91
|
-
status,
|
|
92
|
-
priority,
|
|
93
|
-
requiresConsent,
|
|
94
|
-
placementIds:
|
|
95
|
-
startAt: startAt || undefined,
|
|
96
|
-
endAt: endAt || undefined,
|
|
108
|
+
name: draft.name,
|
|
109
|
+
provider: draft.provider,
|
|
110
|
+
status: draft.status,
|
|
111
|
+
priority: draft.priority,
|
|
112
|
+
requiresConsent: draft.requiresConsent,
|
|
113
|
+
placementIds: draft.placementIds,
|
|
114
|
+
startAt: draft.startAt || undefined,
|
|
115
|
+
endAt: draft.endAt || undefined,
|
|
97
116
|
creative: {
|
|
98
|
-
title:
|
|
99
|
-
body:
|
|
100
|
-
imageUrl:
|
|
101
|
-
ctaLabel: ctaLabel || undefined,
|
|
102
|
-
ctaHref: ctaHref || undefined,
|
|
103
|
-
adsenseSlot: adsenseSlot || undefined,
|
|
104
|
-
thirdPartyUrl: thirdPartyUrl || undefined,
|
|
117
|
+
title: draft.creativeTitle || undefined,
|
|
118
|
+
body: draft.creativeBody || undefined,
|
|
119
|
+
imageUrl: draft.creativeImageUrl || undefined,
|
|
120
|
+
ctaLabel: draft.ctaLabel || undefined,
|
|
121
|
+
ctaHref: draft.ctaHref || undefined,
|
|
122
|
+
adsenseSlot: draft.adsenseSlot || undefined,
|
|
123
|
+
thirdPartyUrl: draft.thirdPartyUrl || undefined,
|
|
105
124
|
},
|
|
106
125
|
};
|
|
107
126
|
if (adId) {
|
|
@@ -120,49 +139,35 @@ export function AdminAdEditorView({ adId, endpointBase = ADMIN_ENDPOINTS.ADS, la
|
|
|
120
139
|
});
|
|
121
140
|
const placements = adQuery.data?.placements ?? metaQuery.data?.placements ?? [];
|
|
122
141
|
const providerCredentialStatus = metaQuery.data?.providerCredentialStatus;
|
|
142
|
+
/*
|
|
143
|
+
* What remains of `publishIssues` after the schema took its share.
|
|
144
|
+
*
|
|
145
|
+
* The name, the placement count, the date order, "a scheduled ad needs a
|
|
146
|
+
* start date" and the three provider rules all moved to the schema, where
|
|
147
|
+
* they reach the field that is wrong instead of a warning banner that only
|
|
148
|
+
* appeared once the status was already publish-ready. What is left cannot
|
|
149
|
+
* live there: it depends on the LIVE placement list and on whether the
|
|
150
|
+
* provider's credentials are configured in site settings — neither of which
|
|
151
|
+
* a schema can see.
|
|
152
|
+
*/
|
|
123
153
|
const publishIssues = React.useMemo(() => {
|
|
124
154
|
const issues = [];
|
|
125
155
|
const now = Date.now();
|
|
126
|
-
if (!name.trim()) {
|
|
127
|
-
issues.push("Ad name is required");
|
|
128
|
-
}
|
|
129
|
-
if (selectedPlacements.length === 0) {
|
|
130
|
-
issues.push("Select at least one placement");
|
|
131
|
-
}
|
|
132
156
|
const knownPlacementIds = new Set(placements.map((placement) => placement.id));
|
|
133
157
|
const enabledPlacementIds = new Set(placements.filter((placement) => placement.enabled).map((placement) => placement.id));
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
136
|
-
issues.push(`Unknown placements: ${
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
issues.push(`Selected placements are disabled: ${disabledPlacements.join(", ")}`);
|
|
158
|
+
const unknown = form.placementIds.filter((id) => !knownPlacementIds.has(id));
|
|
159
|
+
if (unknown.length > 0)
|
|
160
|
+
issues.push(`Unknown placements: ${unknown.join(", ")}`);
|
|
161
|
+
const disabled = form.placementIds.filter((id) => !enabledPlacementIds.has(id));
|
|
162
|
+
if (disabled.length > 0) {
|
|
163
|
+
issues.push(`Selected placements are disabled: ${disabled.join(", ")}`);
|
|
141
164
|
}
|
|
142
|
-
const startMs = startAt ? new Date(startAt).getTime() : null;
|
|
143
|
-
const endMs = endAt ? new Date(endAt).getTime() : null;
|
|
144
|
-
if (
|
|
145
|
-
issues.push("
|
|
165
|
+
const startMs = form.startAt ? new Date(form.startAt).getTime() : null;
|
|
166
|
+
const endMs = form.endAt ? new Date(form.endAt).getTime() : null;
|
|
167
|
+
if (form.status === "scheduled" && startMs !== null && !Number.isNaN(startMs) && startMs <= now) {
|
|
168
|
+
issues.push("Scheduled ads must start in the future");
|
|
146
169
|
}
|
|
147
|
-
if (
|
|
148
|
-
issues.push("End date must be a valid ISO timestamp");
|
|
149
|
-
}
|
|
150
|
-
if (startMs !== null &&
|
|
151
|
-
endMs !== null &&
|
|
152
|
-
!Number.isNaN(startMs) &&
|
|
153
|
-
!Number.isNaN(endMs) &&
|
|
154
|
-
endMs <= startMs) {
|
|
155
|
-
issues.push("End date must be after start date");
|
|
156
|
-
}
|
|
157
|
-
if (status === "scheduled") {
|
|
158
|
-
if (!startAt || startMs === null || Number.isNaN(startMs)) {
|
|
159
|
-
issues.push("Scheduled ads require a valid start date");
|
|
160
|
-
}
|
|
161
|
-
else if (startMs <= now) {
|
|
162
|
-
issues.push("Scheduled ads must start in the future");
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (status === "active") {
|
|
170
|
+
if (form.status === "active") {
|
|
166
171
|
if (startMs !== null && !Number.isNaN(startMs) && startMs > now) {
|
|
167
172
|
issues.push("Active ads cannot have a future start date");
|
|
168
173
|
}
|
|
@@ -170,75 +175,66 @@ export function AdminAdEditorView({ adId, endpointBase = ADMIN_ENDPOINTS.ADS, la
|
|
|
170
175
|
issues.push("Active ads cannot have an end date in the past");
|
|
171
176
|
}
|
|
172
177
|
}
|
|
173
|
-
if (provider === "
|
|
174
|
-
|
|
175
|
-
issues.push("Manual ads need at least one creative field (title, body, image, or CTA URL)");
|
|
176
|
-
}
|
|
178
|
+
if (form.provider === "adsense" && !providerCredentialStatus?.hasAdsenseClientId) {
|
|
179
|
+
issues.push("AdSense provider credentials are missing in ad settings");
|
|
177
180
|
}
|
|
178
|
-
if (provider === "
|
|
179
|
-
|
|
180
|
-
issues.push("AdSense ads require an AdSense slot id");
|
|
181
|
-
}
|
|
182
|
-
if (!providerCredentialStatus?.hasAdsenseClientId) {
|
|
183
|
-
issues.push("AdSense provider credentials are missing in ad settings");
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
if (provider === "thirdParty") {
|
|
187
|
-
validateThirdPartyUrl(thirdPartyUrl, issues);
|
|
188
|
-
if (!providerCredentialStatus?.hasThirdPartyScriptUrl) {
|
|
189
|
-
issues.push("Third-party provider script URL is missing in ad settings");
|
|
190
|
-
}
|
|
181
|
+
if (form.provider === "thirdParty" && !providerCredentialStatus?.hasThirdPartyScriptUrl) {
|
|
182
|
+
issues.push("Third-party provider script URL is missing in ad settings");
|
|
191
183
|
}
|
|
192
184
|
return issues;
|
|
193
185
|
}, [
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
name,
|
|
186
|
+
form.endAt,
|
|
187
|
+
form.placementIds,
|
|
188
|
+
form.provider,
|
|
189
|
+
form.startAt,
|
|
190
|
+
form.status,
|
|
200
191
|
placements,
|
|
201
|
-
provider,
|
|
202
192
|
providerCredentialStatus?.hasAdsenseClientId,
|
|
203
193
|
providerCredentialStatus?.hasThirdPartyScriptUrl,
|
|
204
|
-
selectedPlacements,
|
|
205
|
-
startAt,
|
|
206
|
-
status,
|
|
207
|
-
thirdPartyUrl,
|
|
208
|
-
title,
|
|
209
194
|
]);
|
|
210
|
-
const publishHardeningRequired = status === "active" || status === "scheduled";
|
|
195
|
+
const publishHardeningRequired = form.status === "active" || form.status === "scheduled";
|
|
211
196
|
const blockedByPublishValidation = publishHardeningRequired && publishIssues.length > 0;
|
|
212
|
-
const togglePlacement = (placementId) => {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
197
|
+
const togglePlacement = (placementId) => setForm((prev) => ({
|
|
198
|
+
...prev,
|
|
199
|
+
placementIds: prev.placementIds.includes(placementId)
|
|
200
|
+
? prev.placementIds.filter((id) => id !== placementId)
|
|
201
|
+
: [...prev.placementIds, placementId],
|
|
202
|
+
}));
|
|
203
|
+
const sections = React.useMemo(() => buildSectionsFromSchema(adminAdFormSchema, {
|
|
204
|
+
options: {
|
|
205
|
+
provider: PROVIDER_OPTIONS,
|
|
206
|
+
status: STATUS_OPTIONS,
|
|
207
|
+
},
|
|
208
|
+
renderers: {
|
|
209
|
+
placementIds: ({ values }) => (_jsx(PlacementChecklist, { placements: placements, selected: values.placementIds, onToggle: togglePlacement })),
|
|
210
|
+
},
|
|
211
|
+
}),
|
|
212
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
213
|
+
[placements]);
|
|
214
|
+
const nav = useSectionFormNav(sections, form, { scope: "admin:ad-editor" });
|
|
215
|
+
const { shellCtx, setFieldError, clearErrors } = useFormShellState(adminAdFormSchema, {
|
|
216
|
+
sections: nav.sectionMeta,
|
|
217
|
+
onGoToSection: nav.goToSection,
|
|
218
|
+
fieldToSectionIndex: nav.fieldToSectionIndex,
|
|
222
219
|
});
|
|
220
|
+
const handleSubmit = () => {
|
|
221
|
+
clearErrors();
|
|
222
|
+
const parsed = adminAdFormSchema.safeParse(visibleValues(adminAdFormSchema, form));
|
|
223
|
+
if (!parsed.success) {
|
|
224
|
+
applyZodIssues(parsed.error.issues, setFieldError);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (blockedByPublishValidation) {
|
|
228
|
+
showToast("Cannot save in publish-ready status until the readiness issues are fixed.", "warning");
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
saveMutation.mutate();
|
|
232
|
+
};
|
|
223
233
|
return (_jsx(StackedViewShell, { portal: "admin", ...rest, title: labels.title ?? (adId ? "Edit Ad" : "Create Ad"), sections: [
|
|
224
234
|
metaQuery.error ? (_jsx(Alert, { variant: "error", title: "Could not load ad metadata", children: metaQuery.error instanceof Error ? metaQuery.error.message : "Unknown error" })) : null,
|
|
225
235
|
adQuery.error ? (_jsx(Alert, { variant: "error", title: "Could not load ad", children: adQuery.error instanceof Error ? adQuery.error.message : "Unknown error" })) : null,
|
|
226
|
-
_jsxs(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
showToast("Cannot save in publish-ready status until validation issues are fixed.", "warning");
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
saveMutation.mutate();
|
|
233
|
-
}, spacing: "md", children: [_jsx(FormErrorSummary, {}), _jsx(Input, { label: "Ad name", value: name, onChange: (event) => setName(event.target.value), placeholder: "Summer Campaign" }), _jsxs(Div, { layout: "grid", gap: "3", className: "grid-cols-1 md:grid-cols-3", children: [_jsx(Select, { label: "Provider", value: provider, options: [
|
|
234
|
-
{ label: "Manual", value: "manual" },
|
|
235
|
-
{ label: "AdSense", value: "adsense" },
|
|
236
|
-
{ label: "Third Party", value: "thirdParty" },
|
|
237
|
-
], onChange: (event) => setProvider(event.target.value) }), _jsx(Select, { label: "Status", value: status, options: [
|
|
238
|
-
{ label: "Draft", value: "draft" },
|
|
239
|
-
{ label: "Active", value: "active" },
|
|
240
|
-
{ label: "Scheduled", value: "scheduled" },
|
|
241
|
-
{ label: "Paused", value: "paused" },
|
|
242
|
-
], onChange: (event) => setStatus(event.target.value) }), _jsx(Input, { label: "Priority", type: "number", value: String(priority), onChange: (event) => setPriority(Number(event.target.value || 0)) })] }), _jsx(Toggle, { checked: requiresConsent, onChange: setRequiresConsent, label: "Require user ad-consent for this ad" }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Placements" }), _jsx(Div, { layout: "grid", gap: "2", className: "grid-cols-1 md:grid-cols-2", children: placements.map((placement) => (_jsxs("label", { className: "flex items-center gap-[var(--appkit-space-2)] rounded-md border border-[var(--appkit-color-border)] px-[var(--appkit-space-3)] py-[var(--appkit-space-2)]", children: [_jsx(Checkbox, { bare: true, checked: selectedPlacements.includes(placement.id), onChange: () => togglePlacement(placement.id) }), _jsx(Span, { size: "sm", children: placement.label })] }, placement.id))) })] }), scheduleSection, creativeSection, _jsxs(Div, { border: "default", className: __P.p3, rounded: "lg", children: [_jsx(Text, { className: "mb-1", size: "sm", weight: "medium", children: "Preview" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: title || name || "Untitled ad" }), body ? _jsx(Text, { className: "mt-1", size: "sm", children: body }) : null, ctaLabel ? _jsxs(Text, { className: "mt-1", size: "xs", children: ["CTA: ", ctaLabel, " (", ctaHref || "#", ")"] }) : null] }), publishHardeningRequired && publishIssues.length > 0 ? (_jsx(Alert, { variant: "warning", title: "Publish readiness issues", children: publishIssues.join("; ") })) : null, _jsx(FormActions, { align: "right", children: _jsx(Button, { type: "submit", disabled: saveMutation.isPending || blockedByPublishValidation || !name.trim() || selectedPlacements.length === 0, children: saveMutation.isPending ? "Saving..." : status === "active" ? "Publish ad" : adId ? "Save changes" : "Create ad" }) })] }),
|
|
236
|
+
_jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: form, onChange: patch, onSubmit: handleSubmit, schema: adminAdFormSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: saveMutation.isPending, submitLabel: form.status === "active" ? "Publish ad" : adId ? "Save changes" : "Create ad" })] }, "ad-form"),
|
|
237
|
+
_jsxs(Div, { border: "default", className: __P.p3, rounded: "lg", children: [_jsx(Text, { className: "mb-1", size: "sm", weight: "medium", children: "Preview" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: form.creativeTitle || form.name || "Untitled ad" }), form.creativeBody ? _jsx(Text, { className: "mt-1", size: "sm", children: form.creativeBody }) : null, form.ctaLabel ? (_jsxs(Text, { className: "mt-1", size: "xs", children: ["CTA: ", form.ctaLabel, " (", form.ctaHref || "#", ")"] })) : null] }, "ad-preview"),
|
|
238
|
+
publishHardeningRequired && publishIssues.length > 0 ? (_jsx(Alert, { variant: "warning", title: "Publish readiness issues", children: publishIssues.join("; ") }, "ad-publish-issues")) : null,
|
|
243
239
|
] }));
|
|
244
240
|
}
|