@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
|
@@ -1,13 +1,29 @@
|
|
|
1
|
+
import { type NavSearchable } from "../../../shared/features/layout/matchesNavQuery";
|
|
1
2
|
/**
|
|
2
|
-
* Shared inline-filter logic for the Admin/Store/User sidebars
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Shared inline-filter logic for the Admin/Store/User sidebars. Each sidebar
|
|
4
|
+
* keeps its own accordion/markup; only this filtering is shared so it is not
|
|
5
|
+
* rewritten three times.
|
|
6
|
+
*
|
|
7
|
+
* ## It searches what a screen is FOR, not just what it is called
|
|
8
|
+
*
|
|
9
|
+
* This was `item.label.toLowerCase().includes(q)`, which could only find a
|
|
10
|
+
* screen by the name already on the button. "refund" found nothing, because
|
|
11
|
+
* the screen is called Payouts. "postcode" found nothing, because the field is
|
|
12
|
+
* called PIN code. "maintenance" found nothing, because that toggle lives
|
|
13
|
+
* inside Site Settings. W6 gave every nav item a `description` and `keywords`;
|
|
14
|
+
* `matchesNavQuery` reads all three.
|
|
15
|
+
*
|
|
16
|
+
* ## Results are RANKED, and within a group only
|
|
17
|
+
*
|
|
18
|
+
* A label hit beats a keyword hit beats a description hit, so typing "orders"
|
|
19
|
+
* puts **Orders** above the guide that merely mentions orders. Groups keep
|
|
20
|
+
* their declared order — reordering them by best score would move the sidebar
|
|
21
|
+
* around under the reader's cursor while they type.
|
|
6
22
|
*/
|
|
7
23
|
export declare function useSidebarSearch<Item extends {
|
|
8
24
|
href: string;
|
|
9
25
|
label: string;
|
|
10
|
-
}
|
|
26
|
+
} & Partial<NavSearchable>, Group extends {
|
|
11
27
|
title: string;
|
|
12
28
|
items: Item[];
|
|
13
29
|
defaultOpen?: boolean;
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useMemo, useState } from "react";
|
|
3
|
+
import { matchesNavQuery } from "../../../shared/features/layout/matchesNavQuery";
|
|
3
4
|
/**
|
|
4
|
-
* Shared inline-filter logic for the Admin/Store/User sidebars
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Shared inline-filter logic for the Admin/Store/User sidebars. Each sidebar
|
|
6
|
+
* keeps its own accordion/markup; only this filtering is shared so it is not
|
|
7
|
+
* rewritten three times.
|
|
8
|
+
*
|
|
9
|
+
* ## It searches what a screen is FOR, not just what it is called
|
|
10
|
+
*
|
|
11
|
+
* This was `item.label.toLowerCase().includes(q)`, which could only find a
|
|
12
|
+
* screen by the name already on the button. "refund" found nothing, because
|
|
13
|
+
* the screen is called Payouts. "postcode" found nothing, because the field is
|
|
14
|
+
* called PIN code. "maintenance" found nothing, because that toggle lives
|
|
15
|
+
* inside Site Settings. W6 gave every nav item a `description` and `keywords`;
|
|
16
|
+
* `matchesNavQuery` reads all three.
|
|
17
|
+
*
|
|
18
|
+
* ## Results are RANKED, and within a group only
|
|
19
|
+
*
|
|
20
|
+
* A label hit beats a keyword hit beats a description hit, so typing "orders"
|
|
21
|
+
* puts **Orders** above the guide that merely mentions orders. Groups keep
|
|
22
|
+
* their declared order — reordering them by best score would move the sidebar
|
|
23
|
+
* around under the reader's cursor while they type.
|
|
8
24
|
*/
|
|
9
25
|
export function useSidebarSearch(groups) {
|
|
10
26
|
const [query, setQuery] = useState("");
|
|
@@ -12,9 +28,14 @@ export function useSidebarSearch(groups) {
|
|
|
12
28
|
const filteredGroups = useMemo(() => {
|
|
13
29
|
if (!isSearching)
|
|
14
30
|
return groups;
|
|
15
|
-
const q = query.trim().toLowerCase();
|
|
16
31
|
return groups
|
|
17
|
-
.map((group) =>
|
|
32
|
+
.map((group) => {
|
|
33
|
+
const scored = group.items
|
|
34
|
+
.map((item) => ({ item, score: matchesNavQuery(item, query) }))
|
|
35
|
+
.filter((entry) => entry.score > 0)
|
|
36
|
+
.sort((a, b) => b.score - a.score);
|
|
37
|
+
return { ...group, items: scored.map((entry) => entry.item) };
|
|
38
|
+
})
|
|
18
39
|
.filter((group) => group.items.length > 0);
|
|
19
40
|
}, [groups, query, isSearching]);
|
|
20
41
|
return { query, setQuery, isSearching, filteredGroups };
|
|
@@ -1,5 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LotteryAdminEditView — the slot configuration for one lottery. Sectionised
|
|
3
|
+
* 2026-08-30.
|
|
4
|
+
*
|
|
5
|
+
* ## Two schemas became one
|
|
6
|
+
*
|
|
7
|
+
* It declared `lotteryConfigFormSchema` on `<Form>` and parsed
|
|
8
|
+
* `lotteryConfigWriteSchema` in the submit handler, so the form schema's
|
|
9
|
+
* duplicate-slot and free-lottery rules had never executed. The form schema is
|
|
10
|
+
* deleted; `lotteryConfigWriteSchema` carries the annotations now and is both
|
|
11
|
+
* what this form renders from and what the route parses.
|
|
12
|
+
*
|
|
13
|
+
* ## Title and description are gone, not moved
|
|
14
|
+
*
|
|
15
|
+
* They were editable here and reached nothing: `LotteryConfigClient` posts
|
|
16
|
+
* `data.lotteryConfig` to `PUT .../lottery-config` and ignores the rest, and
|
|
17
|
+
* that route accepts only the config. An event's title, dates, status and
|
|
18
|
+
* media are edited in the ordinary event editor — this page is the slots. Two
|
|
19
|
+
* inputs that saved silently into nowhere are worse than their absence, which
|
|
20
|
+
* is the same call made on `rememberMe`. The title still appears, as the
|
|
21
|
+
* heading, so it is obvious which lottery is open.
|
|
22
|
+
*/
|
|
1
23
|
import React from "react";
|
|
2
24
|
import type { LotteryConfigWriteInput } from "../../../../features/lottery/schemas/config-write";
|
|
25
|
+
/** One editable row of the slot table. */
|
|
3
26
|
interface LotterySlotRow {
|
|
4
27
|
slotNumber: number;
|
|
5
28
|
name: string;
|
|
@@ -22,9 +45,6 @@ interface LotterySlotRow {
|
|
|
22
45
|
* fullness check reads.
|
|
23
46
|
*/
|
|
24
47
|
interface LotteryEventFormData {
|
|
25
|
-
title: string;
|
|
26
|
-
description?: string;
|
|
27
|
-
type: "lottery";
|
|
28
48
|
lotteryConfig: LotteryConfigWriteInput;
|
|
29
49
|
}
|
|
30
50
|
interface LotteryAdminEditViewProps {
|
|
@@ -1,98 +1,121 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
/**
|
|
4
|
+
* LotteryAdminEditView — the slot configuration for one lottery. Sectionised
|
|
5
|
+
* 2026-08-30.
|
|
6
|
+
*
|
|
7
|
+
* ## Two schemas became one
|
|
8
|
+
*
|
|
9
|
+
* It declared `lotteryConfigFormSchema` on `<Form>` and parsed
|
|
10
|
+
* `lotteryConfigWriteSchema` in the submit handler, so the form schema's
|
|
11
|
+
* duplicate-slot and free-lottery rules had never executed. The form schema is
|
|
12
|
+
* deleted; `lotteryConfigWriteSchema` carries the annotations now and is both
|
|
13
|
+
* what this form renders from and what the route parses.
|
|
14
|
+
*
|
|
15
|
+
* ## Title and description are gone, not moved
|
|
16
|
+
*
|
|
17
|
+
* They were editable here and reached nothing: `LotteryConfigClient` posts
|
|
18
|
+
* `data.lotteryConfig` to `PUT .../lottery-config` and ignores the rest, and
|
|
19
|
+
* that route accepts only the config. An event's title, dates, status and
|
|
20
|
+
* media are edited in the ordinary event editor — this page is the slots. Two
|
|
21
|
+
* inputs that saved silently into nowhere are worse than their absence, which
|
|
22
|
+
* is the same call made on `rememberMe`. The title still appears, as the
|
|
23
|
+
* heading, so it is obvious which lottery is open.
|
|
24
|
+
*/
|
|
25
|
+
import React from "react";
|
|
26
|
+
import { Button, DataTable, Input, Row, Stack, Text, } from "../../../../ui";
|
|
10
27
|
import { FormErrorSummary } from "../../../../ui/forms/FormErrorSummary";
|
|
28
|
+
import { FormShellContext, useFormShellState } from "../../../../ui/forms/FormShell";
|
|
29
|
+
import { applyZodIssues } from "../../../../ui/forms/apply-zod-issues";
|
|
30
|
+
import { StackedViewShell } from "../../../../ui";
|
|
31
|
+
import { buildSectionsFromSchema, visibleValues } from "../../../../features/shell/build-sections";
|
|
32
|
+
import { SectionForm, useSectionFormNav } from "../../../../features/shell/SectionForm";
|
|
33
|
+
import { lotteryConfigWriteSchema } from "../../../../features/lottery/schemas/config-write";
|
|
34
|
+
const MAX_SLOTS = 200;
|
|
11
35
|
export function LotteryAdminEditView({ eventId, initialData, onSubmit, }) {
|
|
12
|
-
const [isPending, startTransition] = useTransition();
|
|
13
|
-
const [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
};
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
? { ...s, [field]: field === "price" ? Math.round(parseFloat(value) * 100) / 100 || 0 : value }
|
|
34
|
-
: s));
|
|
35
|
-
};
|
|
36
|
+
const [isPending, startTransition] = React.useTransition();
|
|
37
|
+
const [form, setForm] = React.useState({
|
|
38
|
+
pricingMode: initialData?.pricingMode ?? "uniform",
|
|
39
|
+
uniformPrice: initialData?.uniformPrice ?? 0,
|
|
40
|
+
drawWindowDurationMinutes: initialData?.drawWindowDurationMinutes ?? 5,
|
|
41
|
+
maxPullsPerTransaction: initialData?.maxPullsPerTransaction ?? 1,
|
|
42
|
+
maxPullsPerUser: initialData?.maxPullsPerUser ?? 1,
|
|
43
|
+
slots: initialData?.slots ?? [{ slotNumber: 1, name: "", price: 0 }],
|
|
44
|
+
});
|
|
45
|
+
const patch = (partial) => setForm((prev) => Object.assign({}, prev, partial));
|
|
46
|
+
const sections = React.useMemo(() => buildSectionsFromSchema(lotteryConfigWriteSchema, {
|
|
47
|
+
renderers: {
|
|
48
|
+
slots: ({ values, onChange }) => (_jsx(SlotTable, { slots: values.slots, showPrice: values.pricingMode === "variable", onSlotsChange: (next) => onChange({ slots: next }) })),
|
|
49
|
+
},
|
|
50
|
+
}), []);
|
|
51
|
+
const nav = useSectionFormNav(sections, form, { scope: "admin:lottery-config" });
|
|
52
|
+
const { shellCtx, setFieldError, clearErrors } = useFormShellState(lotteryConfigWriteSchema, {
|
|
53
|
+
sections: nav.sectionMeta,
|
|
54
|
+
onGoToSection: nav.goToSection,
|
|
55
|
+
fieldToSectionIndex: nav.fieldToSectionIndex,
|
|
56
|
+
});
|
|
36
57
|
const handleSubmit = () => {
|
|
37
|
-
|
|
58
|
+
clearErrors();
|
|
59
|
+
/*
|
|
60
|
+
* Per-slot prices are dropped under uniform pricing, where the price of a
|
|
61
|
+
* slot IS `uniformPrice`. They used to be sent in both modes, so a figure
|
|
62
|
+
* loaded from `initialData` survived a switch to uniform — unreachable on
|
|
63
|
+
* screen, still written on save. `visibleValues` cannot reach inside an
|
|
64
|
+
* array, so this one masking stays explicit.
|
|
65
|
+
*/
|
|
66
|
+
const draft = visibleValues(lotteryConfigWriteSchema, form);
|
|
67
|
+
const candidate = {
|
|
68
|
+
...draft,
|
|
69
|
+
slots: form.slots.map((s) => ({
|
|
70
|
+
slotNumber: s.slotNumber,
|
|
71
|
+
name: s.name,
|
|
72
|
+
image: s.image?.trim() || undefined,
|
|
73
|
+
price: form.pricingMode === "variable" ? s.price : 0,
|
|
74
|
+
})),
|
|
75
|
+
};
|
|
76
|
+
const parsed = lotteryConfigWriteSchema.safeParse(candidate);
|
|
77
|
+
if (!parsed.success) {
|
|
78
|
+
applyZodIssues(parsed.error.issues, setFieldError);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
38
81
|
startTransition(async () => {
|
|
39
|
-
|
|
40
|
-
/*
|
|
41
|
-
* 🛑 This used to send `isBooked: false` and `weight: 0` on every slot,
|
|
42
|
-
* and drop `bookedByUserId` / `bookedByDisplayName` /
|
|
43
|
-
* `bookedByUserLotteryNumber` entirely — against a `.passthrough()`
|
|
44
|
-
* PATCH, which made the first save of a live lottery erase every
|
|
45
|
-
* purchased slot, silently, with a 200.
|
|
46
|
-
*
|
|
47
|
-
* The write shape now has NO booking fields at all and is `.strict()`,
|
|
48
|
-
* so a slot's buyer is not something this form can express, let alone
|
|
49
|
-
* overwrite. `PUT /lottery-config` merges by `slotNumber` against the
|
|
50
|
-
* stored config and is the only writer.
|
|
51
|
-
*/
|
|
52
|
-
const candidate = {
|
|
53
|
-
slots: slots.map((s) => ({
|
|
54
|
-
slotNumber: s.slotNumber,
|
|
55
|
-
name: s.name,
|
|
56
|
-
image: s.image?.trim() || undefined,
|
|
57
|
-
price: s.price,
|
|
58
|
-
})),
|
|
59
|
-
pricingMode,
|
|
60
|
-
uniformPrice: pricingMode === "uniform"
|
|
61
|
-
? Math.round(parseFloat(uniformPrice) * 100) / 100 || 0
|
|
62
|
-
: undefined,
|
|
63
|
-
drawWindowDurationMinutes: parseInt(drawWindowMinutes, 10) || 5,
|
|
64
|
-
maxPullsPerTransaction: parseInt(maxPullsPerTx, 10) || 1,
|
|
65
|
-
maxPullsPerUser: parseInt(maxPullsPerUser, 10) || 1,
|
|
66
|
-
};
|
|
67
|
-
/*
|
|
68
|
-
* The schema was passed to `<Form>` and never actually run — so the
|
|
69
|
-
* `parseFloat(x) || 0` coercions above, which it exists to catch, were
|
|
70
|
-
* still live. Parsed here so a bad slot is refused on the client
|
|
71
|
-
* rather than discovered as a 400.
|
|
72
|
-
*/
|
|
73
|
-
const parsed = lotteryConfigWriteSchema.safeParse(candidate);
|
|
74
|
-
if (!parsed.success) {
|
|
75
|
-
setError(parsed.error.issues[0]?.message ?? "Check the lottery settings.");
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
await onSubmit({ title, description, type: "lottery", lotteryConfig: parsed.data });
|
|
79
|
-
}
|
|
80
|
-
catch (err) {
|
|
81
|
-
const e = normalizeError(err);
|
|
82
|
-
setError(toUserMessage(e.code, undefined, { fallback: "Failed to save lottery." }));
|
|
83
|
-
}
|
|
82
|
+
await onSubmit({ lotteryConfig: parsed.data });
|
|
84
83
|
});
|
|
85
84
|
};
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
return (_jsx(StackedViewShell, { portal: "admin", title: eventId
|
|
86
|
+
? `Edit lottery${initialData?.title ? ` — ${initialData.title}` : ""}`
|
|
87
|
+
: "Create lottery", sections: [
|
|
88
|
+
_jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: form, onChange: patch, onSubmit: handleSubmit, schema: lotteryConfigWriteSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: isPending, submitLabel: eventId ? "Update lottery" : "Create lottery" })] }, "lottery-config-form"),
|
|
89
|
+
] }));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The repeatable slot rows.
|
|
93
|
+
*
|
|
94
|
+
* A module-level component rather than an inline renderer: its handlers would
|
|
95
|
+
* otherwise sit six braces deep inside `useMemo`, which is what
|
|
96
|
+
* `audit-code-quality`'s DEEP_NESTING rule exists to stop.
|
|
97
|
+
*/
|
|
98
|
+
function SlotTable({ slots, showPrice, onSlotsChange, }) {
|
|
99
|
+
const addSlot = () => onSlotsChange([...slots, { slotNumber: slots.length + 1, name: "", price: 0 }]);
|
|
100
|
+
const removeSlot = (idx) => onSlotsChange(slots.filter((_, i) => i !== idx).map((s, i) => ({ ...s, slotNumber: i + 1 })));
|
|
101
|
+
const updateSlot = (idx, field, value) => onSlotsChange(slots.map((s, i) => i === idx
|
|
102
|
+
? {
|
|
103
|
+
...s,
|
|
104
|
+
[field]: field === "price"
|
|
105
|
+
? Math.round(parseFloat(value) * 100) / 100 || 0
|
|
106
|
+
: value,
|
|
107
|
+
}
|
|
108
|
+
: s));
|
|
109
|
+
const rows = slots.map((s, i) => ({ ...s, _idx: i }));
|
|
110
|
+
const columns = [
|
|
88
111
|
{
|
|
89
112
|
key: "slotNumber",
|
|
90
113
|
header: "#",
|
|
91
|
-
render: (s) => _jsx(Text, { size: "xs", color: "muted", family: "mono", children: s.slotNumber }),
|
|
114
|
+
render: (s) => (_jsx(Text, { size: "xs", color: "muted", family: "mono", children: s.slotNumber })),
|
|
92
115
|
},
|
|
93
116
|
{
|
|
94
117
|
key: "name",
|
|
95
|
-
header: "Prize
|
|
118
|
+
header: "Prize name",
|
|
96
119
|
render: (s) => (_jsx(Input, { type: "text", className: "w-full", placeholder: `Prize for slot ${s.slotNumber}`, value: s.name, onChange: (e) => updateSlot(s._idx, "name", e.target.value), "aria-label": `Slot ${s.slotNumber} prize name` })),
|
|
97
120
|
},
|
|
98
121
|
{
|
|
@@ -100,7 +123,7 @@ export function LotteryAdminEditView({ eventId, initialData, onSubmit, }) {
|
|
|
100
123
|
header: "Image",
|
|
101
124
|
render: (s) => (_jsx(Input, { type: "text", className: "w-full", placeholder: "/media/\u2026 or https://\u2026", value: s.image ?? "", onChange: (e) => updateSlot(s._idx, "image", e.target.value), "aria-label": `Slot ${s.slotNumber} prize image URL` })),
|
|
102
125
|
},
|
|
103
|
-
...(
|
|
126
|
+
...(showPrice
|
|
104
127
|
? [
|
|
105
128
|
{
|
|
106
129
|
key: "price",
|
|
@@ -110,9 +133,5 @@ export function LotteryAdminEditView({ eventId, initialData, onSubmit, }) {
|
|
|
110
133
|
]
|
|
111
134
|
: []),
|
|
112
135
|
];
|
|
113
|
-
|
|
114
|
-
return (_jsx(Container, { children: _jsx(Section, { children: _jsx(Form, { schema: lotteryConfigFormSchema, onSubmit: (e) => e.preventDefault(), className: "space-y-6", children: ({ clearErrors }) => (_jsxs(Stack, { gap: "xl", children: [_jsx(FormErrorSummary, {}), _jsx(Heading, { level: 1, size: "2xl", weight: "bold", children: eventId ? "Edit Lottery" : "Create Lottery" }), _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: "Basic Info" }), _jsx(FieldInput, { name: "title", label: "Title", required: true, value: title, onChange: (v) => { setTitle(v); clearErrors(); } }), _jsx(FieldInput, { name: "description", label: "Description", value: description, onChange: (v) => { setDescription(v); clearErrors(); } })] }), _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: "Configuration" }), _jsx(FieldSelect, { name: "pricingMode", label: "Pricing Mode", value: pricingMode, options: [
|
|
115
|
-
{ label: "Uniform (all slots same price)", value: "uniform" },
|
|
116
|
-
{ label: "Variable (per-slot pricing)", value: "variable" },
|
|
117
|
-
], onChange: (v) => setPricingMode(v) }), pricingMode === "uniform" && (_jsx(FieldInput, { name: "uniformPrice", label: "Price per Slot (\u20B9)", type: "number", value: uniformPrice, onChange: (v) => setUniformPrice(v) })), _jsx(FieldInput, { name: "drawWindowMinutes", label: "Draw Window (minutes)", type: "number", value: drawWindowMinutes, onChange: (v) => setDrawWindowMinutes(v) }), _jsx(FieldInput, { name: "maxPullsPerTx", label: "Max Pulls per Transaction", type: "number", value: maxPullsPerTx, onChange: (v) => setMaxPullsPerTx(v) }), _jsx(FieldInput, { name: "maxPullsPerUser", label: "Max Pulls per User", type: "number", value: maxPullsPerUser, onChange: (v) => setMaxPullsPerUser(v) })] }), _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Heading, { level: 2, size: "lg", weight: "semibold", children: ["Slots (", slots.length, ")"] }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: addSlot, disabled: slots.length >= 200, children: "+ Add Slot" })] }), _jsx(DataTable, { data: slotsWithIdx, columns: slotColumns, keyExtractor: (s) => String(s.slotNumber), emptyMessage: "No slots yet.", actions: (s) => slots.length > 1 ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeSlot(s._idx), "aria-label": `Remove slot ${s.slotNumber}`, children: "\u00D7" })) : null })] }), error && _jsx(Text, { className: "text-error", size: "sm", role: "alert", children: error }), _jsx(Button, { type: "submit", variant: "primary", isLoading: isPending, onClick: handleSubmit, className: "w-full sm:w-auto", children: isPending ? "Saving…" : eventId ? "Update Lottery" : "Create Lottery" })] })) }) }) }));
|
|
136
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Text, { size: "sm", color: "muted", children: [slots.length, " slot", slots.length === 1 ? "" : "s"] }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: addSlot, disabled: slots.length >= MAX_SLOTS, children: "+ Add slot" })] }), _jsx(DataTable, { data: rows, columns: columns, keyExtractor: (s) => String(s.slotNumber), emptyMessage: "No slots yet.", actions: (s) => slots.length > 1 ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeSlot(s._idx), "aria-label": `Remove slot ${s.slotNumber}`, children: "\u00D7" })) : null })] }));
|
|
118
137
|
}
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User-facing lottery pull form — claim a slot with a UPI reference.
|
|
3
|
+
*
|
|
4
|
+
* Fields come from `lotteryPullSchema`. Two need a `renderers` entry because
|
|
5
|
+
* their labels depend on `totalSlots`, which a static annotation cannot know,
|
|
6
|
+
* and one needs `datetime-local` rather than a plain date.
|
|
7
|
+
*
|
|
8
|
+
* ## The upper bound stays a runtime check, on purpose
|
|
9
|
+
*
|
|
10
|
+
* `purchasedItemNumber` is `.min(1)` in the schema and has no `.max()`: the
|
|
11
|
+
* ceiling is `totalSlots`, a prop that varies per lottery. A schema shared with
|
|
12
|
+
* the route cannot carry it, so it is checked after the parse rather than
|
|
13
|
+
* pretended into the annotation. Everything else the hand-written validation
|
|
14
|
+
* used to duplicate — the reference length, the phone shape, the date — now
|
|
15
|
+
* comes from the schema, which is where those rules already lived.
|
|
16
|
+
*/
|
|
1
17
|
import React from "react";
|
|
2
18
|
interface LotteryPullFormProps {
|
|
3
19
|
sourceType: "event" | "product";
|
|
@@ -11,10 +27,5 @@ interface LotteryPullFormProps {
|
|
|
11
27
|
slotName: string;
|
|
12
28
|
}) => void;
|
|
13
29
|
}
|
|
14
|
-
/**
|
|
15
|
-
* User-facing lottery pull form.
|
|
16
|
-
* Submits TX ID + phone + payment time + item number.
|
|
17
|
-
* On success, shows the user their lottery number and assigned prize slot.
|
|
18
|
-
*/
|
|
19
30
|
export declare function LotteryPullForm({ sourceType, eventId, productId, totalSlots, maxPullsPerUser, onSuccess, }: LotteryPullFormProps): React.JSX.Element;
|
|
20
31
|
export {};
|
|
@@ -1,34 +1,67 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* User-facing lottery pull form — claim a slot with a UPI reference.
|
|
5
|
+
*
|
|
6
|
+
* Fields come from `lotteryPullSchema`. Two need a `renderers` entry because
|
|
7
|
+
* their labels depend on `totalSlots`, which a static annotation cannot know,
|
|
8
|
+
* and one needs `datetime-local` rather than a plain date.
|
|
9
|
+
*
|
|
10
|
+
* ## The upper bound stays a runtime check, on purpose
|
|
11
|
+
*
|
|
12
|
+
* `purchasedItemNumber` is `.min(1)` in the schema and has no `.max()`: the
|
|
13
|
+
* ceiling is `totalSlots`, a prop that varies per lottery. A schema shared with
|
|
14
|
+
* the route cannot carry it, so it is checked after the parse rather than
|
|
15
|
+
* pretended into the annotation. Everything else the hand-written validation
|
|
16
|
+
* used to duplicate — the reference length, the phone shape, the date — now
|
|
17
|
+
* comes from the schema, which is where those rules already lived.
|
|
18
|
+
*/
|
|
19
|
+
import { useMemo, useState } from "react";
|
|
6
20
|
import { useApiMutation } from "../../../../client/api/useApiMutation";
|
|
7
|
-
import { apiClient } from "../../../../http";
|
|
8
21
|
import { lotteryPullSchema } from "../../../../features/admin/schemas/admin-user-form";
|
|
22
|
+
import { buildSectionsFromSchema } from "../../../../features/shell/build-sections";
|
|
23
|
+
import { SectionForm, useSectionFormNav } from "../../../../features/shell/SectionForm";
|
|
24
|
+
import { apiClient } from "../../../../http";
|
|
25
|
+
import { FieldInput, Heading, Row, Span, Stack, Text } from "../../../../ui";
|
|
26
|
+
import { applyZodIssues } from "../../../../ui/forms/apply-zod-issues";
|
|
9
27
|
import { FormErrorSummary } from "../../../../ui/forms/FormErrorSummary";
|
|
10
|
-
|
|
11
|
-
* User-facing lottery pull form.
|
|
12
|
-
* Submits TX ID + phone + payment time + item number.
|
|
13
|
-
* On success, shows the user their lottery number and assigned prize slot.
|
|
14
|
-
*/
|
|
28
|
+
import { FormShellContext, useFormShellState } from "../../../../ui/forms/FormShell";
|
|
15
29
|
export function LotteryPullForm({ sourceType, eventId, productId, totalSlots, maxPullsPerUser, onSuccess, }) {
|
|
16
30
|
const [result, setResult] = useState(null);
|
|
17
|
-
const [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
31
|
+
const [form, setForm] = useState({
|
|
32
|
+
sourceType,
|
|
33
|
+
transactionId: "",
|
|
34
|
+
paymentTime: "",
|
|
35
|
+
purchasedItemNumber: "",
|
|
36
|
+
userPhone: "",
|
|
37
|
+
});
|
|
21
38
|
const apiRoute = sourceType === "event"
|
|
22
39
|
? `/api/events/${eventId ?? ""}/lottery-pull`
|
|
23
40
|
: `/api/products/${productId ?? ""}/lottery-pull`;
|
|
41
|
+
const sections = useMemo(() => buildSectionsFromSchema(lotteryPullSchema, {
|
|
42
|
+
renderers: {
|
|
43
|
+
paymentTime: ({ values, onChange, errors }) => (_jsx(FieldInput, { name: "paymentTime", label: "Payment Date & Time",
|
|
44
|
+
// `datetime-local`, not `date` — the pull is matched against a
|
|
45
|
+
// bank timestamp, so the time of day is part of the evidence.
|
|
46
|
+
type: "datetime-local", required: true, value: values.paymentTime, error: errors.paymentTime, onChange: (v) => onChange({ paymentTime: v }) })),
|
|
47
|
+
purchasedItemNumber: ({ values, onChange, errors }) => (_jsx(FieldInput, { name: "purchasedItemNumber", label: `Slot Number (1 – ${totalSlots})`, type: "number", placeholder: `Enter a number between 1 and ${totalSlots}`, required: true, value: values.purchasedItemNumber, error: errors.purchasedItemNumber, onChange: (v) => onChange({ purchasedItemNumber: v }) })),
|
|
48
|
+
},
|
|
49
|
+
}), [totalSlots]);
|
|
50
|
+
const nav = useSectionFormNav(sections, form, { scope: "lottery:pull" });
|
|
51
|
+
const { shellCtx, setFieldError, clearErrors } = useFormShellState(lotteryPullSchema, {
|
|
52
|
+
sections: nav.sectionMeta,
|
|
53
|
+
onGoToSection: nav.goToSection,
|
|
54
|
+
fieldToSectionIndex: nav.fieldToSectionIndex,
|
|
55
|
+
});
|
|
24
56
|
const mutation = useApiMutation({
|
|
57
|
+
errorMessage: "Could not submit your entry.",
|
|
25
58
|
mutationFn: () => apiClient.post(apiRoute, {
|
|
26
59
|
sourceType,
|
|
27
60
|
...(sourceType === "event" ? { eventId } : { productId }),
|
|
28
|
-
transactionId,
|
|
29
|
-
paymentTime: new Date(paymentTime).toISOString(),
|
|
30
|
-
purchasedItemNumber: parseInt(
|
|
31
|
-
userPhone,
|
|
61
|
+
transactionId: form.transactionId,
|
|
62
|
+
paymentTime: new Date(form.paymentTime).toISOString(),
|
|
63
|
+
purchasedItemNumber: parseInt(form.purchasedItemNumber, 10),
|
|
64
|
+
userPhone: form.userPhone,
|
|
32
65
|
}),
|
|
33
66
|
successMessage: "Your lottery entry has been submitted!",
|
|
34
67
|
onSuccess: (data) => {
|
|
@@ -38,30 +71,22 @@ export function LotteryPullForm({ sourceType, eventId, productId, totalSlots, ma
|
|
|
38
71
|
}
|
|
39
72
|
},
|
|
40
73
|
});
|
|
74
|
+
const onSubmit = () => {
|
|
75
|
+
clearErrors();
|
|
76
|
+
const parsed = lotteryPullSchema.safeParse(form);
|
|
77
|
+
if (!parsed.success) {
|
|
78
|
+
applyZodIssues(parsed.error.issues, setFieldError);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Data-dependent ceiling — see the header.
|
|
82
|
+
if (parsed.data.purchasedItemNumber > totalSlots) {
|
|
83
|
+
setFieldError("purchasedItemNumber", `This lottery has ${totalSlots} slots.`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
mutation.mutate();
|
|
87
|
+
};
|
|
41
88
|
if (result) {
|
|
42
89
|
return (_jsxs(Stack, { gap: "sm", padding: "md", rounded: "2xl", surface: "success-surface", className: "border border-success/20", children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Span, { layout: "inline-flex", color: "inverse", weight: "semibold", className: "bg-success text-[11px] tracking-wide", padding: "pill-sm", rounded: "full", transform: "uppercase", children: "Entry Confirmed" }), _jsxs(Heading, { level: 2, className: "text-success", size: "lg", weight: "bold", children: ["You're #", result.userLotteryNumber, "!"] })] }), _jsxs(Text, { className: "text-success", size: "sm", children: ["You've been assigned", " ", _jsxs(Span, { weight: "bold", children: ["Slot #", result.assignedPrizeSlotNumber] }), result.slotName ? ` — ${result.slotName}` : "", ". Good luck!"] })] }));
|
|
43
90
|
}
|
|
44
|
-
return (
|
|
45
|
-
clearErrors();
|
|
46
|
-
const num = parseInt(itemNumber, 10);
|
|
47
|
-
let valid = true;
|
|
48
|
-
if (!transactionId || transactionId.length < 4) {
|
|
49
|
-
setFieldError("transactionId", "Transaction ID must be at least 4 characters");
|
|
50
|
-
valid = false;
|
|
51
|
-
}
|
|
52
|
-
if (!paymentTime) {
|
|
53
|
-
setFieldError("paymentTime", "Payment date & time is required");
|
|
54
|
-
valid = false;
|
|
55
|
-
}
|
|
56
|
-
if (isNaN(num) || num < 1 || num > totalSlots) {
|
|
57
|
-
setFieldError("itemNumber", `Slot number must be between 1 and ${totalSlots}`);
|
|
58
|
-
valid = false;
|
|
59
|
-
}
|
|
60
|
-
if (!userPhone || userPhone.replace(/\D/g, "").length < 10) {
|
|
61
|
-
setFieldError("userPhone", "Enter a valid phone number (minimum 10 digits)");
|
|
62
|
-
valid = false;
|
|
63
|
-
}
|
|
64
|
-
if (valid)
|
|
65
|
-
mutation.mutate();
|
|
66
|
-
}, className: "w-full", children: mutation.isPending ? "Submitting…" : "Submit Entry" })] })) }));
|
|
91
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, size: "lg", weight: "semibold", children: "Submit Your Entry" }), _jsxs(Text, { size: "sm", color: "muted", children: ["Enter your UPI Transaction ID and payment details to claim your slot. You may enter up to", " ", maxPullsPerUser, " time", maxPullsPerUser === 1 ? "" : "s", "."] }), _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: lotteryPullSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: mutation.isPending, submitLabel: "Submit Entry" })] })] }));
|
|
67
92
|
}
|
|
@@ -26,6 +26,8 @@ export declare function checkAnyPermission(resolved: ResolvedPermissions, requir
|
|
|
26
26
|
type GetUser = () => Promise<{
|
|
27
27
|
uid: string;
|
|
28
28
|
role: string;
|
|
29
|
+
permissions?: readonly string[] | null;
|
|
30
|
+
/** @deprecated Legacy tester flags; still read during the migration window. */
|
|
29
31
|
isTester?: boolean;
|
|
30
32
|
canTestAdmin?: boolean;
|
|
31
33
|
} | null>;
|
|
@@ -60,8 +60,9 @@ export function makeAdminSectionLayout(permission, opts) {
|
|
|
60
60
|
redirect(opts.loginPath ?? String(ROUTES.AUTH.LOGIN));
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
|
-
//
|
|
64
|
-
//
|
|
63
|
+
// Admin role, or a tester cleared for admin surfaces (the `tester` role
|
|
64
|
+
// plus `tester:admin-surfaces`, or the legacy isTester/canTestAdmin pair),
|
|
65
|
+
// passes without a permission check.
|
|
65
66
|
if (isEffectiveAdminUser(user))
|
|
66
67
|
return children;
|
|
67
68
|
// non-employee non-admin roles have no business in /admin
|
|
@@ -5,10 +5,21 @@ import { safeRead } from "../../../../errors/safe-read";
|
|
|
5
5
|
import { CATEGORIES_FEATURED_LIMIT, CATEGORIES_MENU_LIMIT, CATEGORIES_SITEMAP_LIMIT } from "../../../shared/features/categories/config";
|
|
6
6
|
/** Full category document by slug — deduped per request via React.cache(). */
|
|
7
7
|
export const getCategoryForDetail = cache(async (slug) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
/*
|
|
9
|
+
* 🛑 No view-count write here — deleted 2026-08-31.
|
|
10
|
+
*
|
|
11
|
+
* This fired `incrementViewCount` on every render, which was two defects at
|
|
12
|
+
* once. It is a Firestore WRITE on the render path (Rule #6), and product
|
|
13
|
+
* and category detail were being counted TWICE by two systems that key
|
|
14
|
+
* differently: this one on the document by id, and the client
|
|
15
|
+
* `PageViewTracker` into `pageViews` by slug. They can never reconcile —
|
|
16
|
+
* they disagree on bots, on prefetch, and on JS-disabled clients — so the
|
|
17
|
+
* two numbers were guaranteed to drift and neither could be trusted.
|
|
18
|
+
*
|
|
19
|
+
* `pageViews` is the single counter now. `viewCount` on the document stays
|
|
20
|
+
* for historical rows; nothing increments it.
|
|
21
|
+
*/
|
|
22
|
+
return (await categoriesRepository.getCategoryBySlug(slug)) ?? null;
|
|
12
23
|
});
|
|
13
24
|
/** Flat list of all categories at tier 1 (roots) — for nav/sitemap. */
|
|
14
25
|
export const listRootCategories = cache(async () => {
|