@wix/himalaya-cli 0.1583.0 → 0.1584.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/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/blog-v3/src/extensions.d.ts +2 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/comments-v1/src/extensions.d.ts +2 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/events/src/index.d.ts +3 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/groups/src/extensions.d.ts +15 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/loyalty/src/extensions.d.ts +13 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/members-v1/src/index.d.ts +19 -4
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-comments.mjs +3 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +4 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-groups.mjs +5 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-loyalty.mjs +4 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-members.mjs +10 -3
- package/dist/cli.mjs +247 -186
- package/dist/preview-assets.generated.bin +0 -0
- package/fixtures/wix-canned.json +25 -0
- package/guides/one-app-to-himi-app.md +52 -1
- package/guides/sdk-reference.json +37 -9
- package/guides/wix-api-usage.md +9 -0
- package/package.json +2 -2
- package/templates/beauty-salon/config.ts +7 -0
- package/templates/beauty-salon/dev/index.ts +758 -352
- package/templates/beauty-salon/himalaya.content.json +3 -0
- package/templates/beauty-salon/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/beauty-salon/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/beauty-salon/tier5-src/_shared/owner-editor-value.ts +164 -0
- package/templates/beauty-salon/tier5-src/_shared/store-product-info.ts +50 -0
- package/templates/beauty-salon/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/beauty-salon/tier5-src/himi-branded-lite-category/index.ts +3 -2
- package/templates/beauty-salon/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +14 -3
- package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/beauty-salon/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/beauty-salon-owner/dev/index.ts +129 -66
- package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/branded-lite/config.ts +9 -0
- package/templates/branded-lite/dev/index.ts +4988 -3304
- package/templates/branded-lite/himalaya.content.json +3 -0
- package/templates/branded-lite/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/branded-lite/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/branded-lite/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/branded-lite/tier5-src/_shared/owner-editor-value.ts +164 -0
- package/templates/branded-lite/tier5-src/_shared/store-product-info.ts +50 -0
- package/templates/branded-lite/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-badges/index.ts +2 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-category/index.ts +3 -2
- package/templates/branded-lite/tier5-src/himi-branded-lite-feed/index.ts +4 -3
- package/templates/branded-lite/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-loyalty/index.ts +115 -49
- package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
- package/templates/branded-lite/tier5-src/himi-branded-lite-program/facts.ts +52 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-program/index.ts +21 -11
- package/templates/branded-lite/tier5-src/himi-branded-lite-video/index.ts +2 -1
- package/templates/branded-lite/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/branded-lite/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/branded-lite/tier5-src/himi-store-product/index.ts +13 -3
- package/templates/branded-lite/tier5-src/wix-spaces-forum-post/index.ts +3 -2
- package/templates/branded-lite/tier5-src/wix-spaces-member-detail/index.ts +53 -37
- package/templates/branded-lite/tier5-src/wix-spaces-members/index.ts +100 -34
- package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/branded-lite/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/community-club/config.ts +16 -0
- package/templates/community-club/dev/index.ts +1930 -401
- package/templates/community-club/himalaya.content.json +6 -0
- package/templates/community-club/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/community-club/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/community-club/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/community-club/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/community-club/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/community-club/tier5-src/himi-branded-lite-feed/index.ts +4 -3
- package/templates/community-club/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
- package/templates/community-club/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/community-club/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/community-club/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/community-club/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/community-club-owner/dev/index.ts +53 -1
- package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/community-club-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/community-club-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/community-club-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/community-club-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/community-club-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/community-club-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/community-club-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/events-meetups/config.ts +16 -0
- package/templates/events-meetups/dev/index.ts +1930 -401
- package/templates/events-meetups/himalaya.content.json +6 -0
- package/templates/events-meetups/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/events-meetups/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/events-meetups/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/events-meetups/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/events-meetups/tier5-src/himi-branded-lite-feed/index.ts +4 -3
- package/templates/events-meetups/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
- package/templates/events-meetups/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/events-meetups/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/events-meetups/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/events-meetups/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/events-meetups-owner/dev/index.ts +6 -0
- package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/gym-fitness/config.ts +7 -0
- package/templates/gym-fitness/dev/index.ts +1115 -278
- package/templates/gym-fitness/himalaya.content.json +3 -0
- package/templates/gym-fitness/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/gym-fitness/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/gym-fitness/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/gym-fitness/tier5-src/_shared/owner-editor-value.ts +164 -0
- package/templates/gym-fitness/tier5-src/_shared/store-product-info.ts +50 -0
- package/templates/gym-fitness/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/gym-fitness/tier5-src/himi-branded-lite-category/index.ts +3 -2
- package/templates/gym-fitness/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/gym-fitness/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/gym-fitness/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +14 -3
- package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/gym-fitness/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/gym-fitness-owner/dev/index.ts +132 -66
- package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/nonprofit-cause/config.ts +16 -0
- package/templates/nonprofit-cause/dev/index.ts +1925 -396
- package/templates/nonprofit-cause/himalaya.content.json +6 -0
- package/templates/nonprofit-cause/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/nonprofit-cause/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/nonprofit-cause/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/nonprofit-cause/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-feed/index.ts +4 -3
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/nonprofit-cause/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/nonprofit-cause/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/nonprofit-cause/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/nonprofit-cause-owner/dev/index.ts +85 -65
- package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/owner-lite/dev/index.ts +132 -66
- package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/owner-lite/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/owner-lite/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/owner-lite/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/owner-lite/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/owner-lite/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/owner-lite/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/owner-lite/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/owner-lite/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/owner-lite/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/podcast-creator/config.ts +7 -0
- package/templates/podcast-creator/dev/index.ts +1051 -158
- package/templates/podcast-creator/himalaya.content.json +3 -0
- package/templates/podcast-creator/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/podcast-creator/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/podcast-creator/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/podcast-creator/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/podcast-creator/tier5-src/himi-branded-lite-feed/index.ts +4 -3
- package/templates/podcast-creator/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/podcast-creator/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
- package/templates/podcast-creator/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/podcast-creator/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/podcast-creator/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/podcast-creator-owner/dev/index.ts +53 -1
- package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/portfolio-creative/config.ts +7 -0
- package/templates/portfolio-creative/dev/index.ts +1305 -468
- package/templates/portfolio-creative/himalaya.content.json +3 -0
- package/templates/portfolio-creative/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/portfolio-creative/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/portfolio-creative/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/portfolio-creative/tier5-src/_shared/owner-editor-value.ts +164 -0
- package/templates/portfolio-creative/tier5-src/_shared/store-product-info.ts +50 -0
- package/templates/portfolio-creative/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-category/index.ts +3 -2
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
- package/templates/portfolio-creative/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/portfolio-creative/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +14 -3
- package/templates/portfolio-creative/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/portfolio-creative-owner/dev/index.ts +129 -66
- package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/restaurant/config.ts +7 -0
- package/templates/restaurant/dev/index.ts +1051 -158
- package/templates/restaurant/himalaya.content.json +3 -0
- package/templates/restaurant/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/restaurant/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/restaurant/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/restaurant/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/restaurant/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/restaurant/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/restaurant/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/restaurant/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/restaurant/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/restaurant-owner/dev/index.ts +132 -66
- package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
- package/templates/retail-store/config.ts +7 -0
- package/templates/retail-store/dev/index.ts +597 -191
- package/templates/retail-store/himalaya.content.json +3 -0
- package/templates/retail-store/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/retail-store/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/retail-store/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/retail-store/tier5-src/_shared/owner-editor-value.ts +164 -0
- package/templates/retail-store/tier5-src/_shared/store-product-info.ts +50 -0
- package/templates/retail-store/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/retail-store/tier5-src/himi-branded-lite-category/index.ts +3 -2
- package/templates/retail-store/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/retail-store/tier5-src/himi-store-product/index.ts +13 -3
- package/templates/retail-store/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/retail-store-owner/dev/index.ts +50 -1
- package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
- package/templates/yoga-studio/config.ts +7 -0
- package/templates/yoga-studio/dev/index.ts +1107 -110
- package/templates/yoga-studio/himalaya.content.json +3 -0
- package/templates/yoga-studio/tier5-src/_shared/branded-lite-hosted.ts +1 -1
- package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/yoga-studio/tier5-src/_shared/event-registration.ts +86 -0
- package/templates/yoga-studio/tier5-src/_shared/himi-restaurants.ts +2 -1
- package/templates/yoga-studio/tier5-src/himi-blog-post/index.ts +21 -8
- package/templates/yoga-studio/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
- package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +2 -1
- package/templates/yoga-studio/tier5-src/himi-branded-lite-program/facts.ts +52 -0
- package/templates/yoga-studio/tier5-src/himi-branded-lite-program/index.ts +21 -11
- package/templates/yoga-studio/tier5-src/himi-events-detail/index.ts +12 -86
- package/templates/yoga-studio/tier5-src/himi-events-list/index.ts +47 -6
- package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
- package/templates/yoga-studio/tier5-src/wix-spaces-whats-new/index.ts +2 -1
- package/templates/yoga-studio-owner/dev/index.ts +85 -65
- package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +19 -3
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
|
@@ -97,6 +97,7 @@ export const HOSTED_SCREEN_VERTICALS: Readonly<Record<string, readonly BrandedLi
|
|
|
97
97
|
himi_gift_card: ["giftCards"],
|
|
98
98
|
himi_gift_cards: ["giftCards"],
|
|
99
99
|
himi_groups: ["groups"],
|
|
100
|
+
himi_group_about: ["groups"],
|
|
100
101
|
himi_invoice: ["invoices"],
|
|
101
102
|
himi_invoices: ["invoices"],
|
|
102
103
|
himi_loyalty: ["loyalty"],
|
|
@@ -107,7 +108,6 @@ export const HOSTED_SCREEN_VERTICALS: Readonly<Record<string, readonly BrandedLi
|
|
|
107
108
|
himi_my_events: ["events"],
|
|
108
109
|
himi_my_subscriptions: ["plans"],
|
|
109
110
|
himi_new_message: ["inbox", "members"],
|
|
110
|
-
himi_notif_settings: ["notifsettings"],
|
|
111
111
|
himi_notifications: ["notifications"],
|
|
112
112
|
himi_orders: ["store"],
|
|
113
113
|
himi_photo_album: ["galleries"],
|
|
@@ -19,6 +19,8 @@ export interface CollectionConfig {
|
|
|
19
19
|
/** Numeric 0–5 row field rendered as a Rating (accessible "N out of 5") instead of
|
|
20
20
|
* folding a "★★★★☆" glyph string into the metadata line, so screen readers get a value. */
|
|
21
21
|
rating?: string;
|
|
22
|
+
/** Domain-provided calendar text and its unavailable flag, kept separate from media. */
|
|
23
|
+
dateBadge?: { text: string; hidden: string };
|
|
22
24
|
sectioned?: boolean;
|
|
23
25
|
view: 'list' | 'grid';
|
|
24
26
|
backendQuery?: boolean;
|
|
@@ -38,7 +40,13 @@ export interface CollectionConfig {
|
|
|
38
40
|
/** The card's primary verb, shown as a visible button beside the overflow.
|
|
39
41
|
* `screenId` opens that screen with the row id; without it the verb reuses the
|
|
40
42
|
* row's own tap action (the detail screen that owns the verb). */
|
|
41
|
-
primaryVerb?: { label: string; screenId?: string
|
|
43
|
+
primaryVerb?: { label: string; screenId?: string;
|
|
44
|
+
/** A row field holding THIS row's verb label (Register, RSVP, Buy Tickets…); `label` stays the fallback copy. */
|
|
45
|
+
labelPath?: string;
|
|
46
|
+
/** A row boolean that hides the verb on rows that offer no action (an event with no registration). */
|
|
47
|
+
hiddenPath?: string };
|
|
48
|
+
/** The worker action that shares a row (`tier5.<service>.<name>` with `{ id }`): adds Share to the card's overflow menu. */
|
|
49
|
+
shareAction?: string;
|
|
42
50
|
/** A TIME-BOUND entity (an event, a booking, a reservation): the boolean row field the
|
|
43
51
|
* DOMAIN sets once the record has finished — computed by the same rule as the row's own
|
|
44
52
|
* "Past" label, in the same place, so the two cannot disagree. A finished
|
|
@@ -73,7 +81,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
|
|
|
73
81
|
himiBookingBookings: { rows: 'rows', title: 'title', subtitle: 'subtitle', metadata: ['status'], view: 'list', backendQuery: true,
|
|
74
82
|
defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search by booking ID', countNoun: ['booking', 'bookings'], filterValues: ['Upcoming', 'Past', 'Cancelled'],
|
|
75
83
|
sortLabels: { default: 'Soonest', az: 'Oldest', za: 'Latest' } },
|
|
76
|
-
himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' }, endedWhen: 'isPast' },
|
|
84
|
+
himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', dateBadge: { text: 'dateBadge', hidden: 'noDateBadge' }, gridColumns: 1, view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register', labelPath: 'actionLabel', hiddenPath: 'noAction' }, shareAction: 'share', endedWhen: 'isPast' },
|
|
77
85
|
himiBlogFeed: { rows: 'posts', title: 'title', subtitle: 'excerpt', image: 'image', filter: 'category', filterValuesFrom: 'categories', view: 'grid', narrowedBy: 'selectedCategoryId' },
|
|
78
86
|
himiOwnerLiteProducts: { rows: 'products', title: 'name', subtitle: 'price', image: 'image', view: 'grid', backendQuery: true, countNoun: ['product', 'products'] },
|
|
79
87
|
himiOwnerModules: { instanceInput: 'module', rows: 'rows', title: 'title', subtitle: 'subtitle', image: 'image', filter: 'statusLabel', view: 'list', paged: true, reviewAction: 'prepareRecord', reviewSelectedAction: 'prepareSelected' },
|
|
@@ -208,7 +216,15 @@ export function projectCollection(source: CollectionRow[], config: CollectionCon
|
|
|
208
216
|
...(config.endedWhen ? (() => {
|
|
209
217
|
const ended = row[config.endedWhen] === true || (config.endedWhen === 'isPast' && row.statusLabel === 'Past');
|
|
210
218
|
return { collectionEnded: ended, collectionNotEnded: !ended };
|
|
211
|
-
})() : {})
|
|
219
|
+
})() : {}),
|
|
220
|
+
// A row restored from state saved by an older build has no badge flag. An unset hidden flag
|
|
221
|
+
// fails open and would paint an empty chip, so it reads as "no badge" until a refresh.
|
|
222
|
+
...(config.dateBadge && typeof row[config.dateBadge.hidden] !== 'boolean'
|
|
223
|
+
? { [config.dateBadge.text]: '', [config.dateBadge.hidden]: true } : {}),
|
|
224
|
+
// Same for a per-row verb: an unset hidden flag fails open and would show a verb with an unresolved
|
|
225
|
+
// label, so a row without the flag offers no action until a refresh establishes one.
|
|
226
|
+
...(config.primaryVerb?.hiddenPath && typeof row[config.primaryVerb.hiddenPath] !== 'boolean'
|
|
227
|
+
? { ...(config.primaryVerb.labelPath ? { [config.primaryVerb.labelPath]: '' } : {}), [config.primaryVerb.hiddenPath]: true } : {}) })),
|
|
212
228
|
filters, loaded: rows.length,
|
|
213
229
|
};
|
|
214
230
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Pure Events registration helpers shared by the event detail and the event list: which action an
|
|
2
|
+
// event offers, its label, and the URLs behind it. One copy, so a card and the detail screen never
|
|
3
|
+
// disagree about whether an event can be registered for.
|
|
4
|
+
import type { WixEvent } from "@himalaya/wix-events";
|
|
5
|
+
import { formatWeekdayDateTime } from "./engine-intl.js";
|
|
6
|
+
import { copyParam } from "./himi-copy.js";
|
|
7
|
+
|
|
8
|
+
function detailCopy(key: string, fallback: string): string { return copyParam(key, fallback); }
|
|
9
|
+
|
|
10
|
+
/** Registration CTA kind derived from the event's registration/eventType. */
|
|
11
|
+
export type RegistrationKind = "rsvp" | "tickets" | "external" | "none";
|
|
12
|
+
|
|
13
|
+
/** Map registration status and decoded eventType onto the CTA variant. The decoded
|
|
14
|
+
* `eventType` already folds registration.type / registration.initialType /
|
|
15
|
+
* the legacy flat `type` (decodeEvent, #478). Without a recognized type or
|
|
16
|
+
* status there is no registration action to offer. */
|
|
17
|
+
export function registrationKindOf(e: WixEvent): RegistrationKind {
|
|
18
|
+
const raw = e as unknown as { registration?: { type?: string; initialType?: string; status?: string } };
|
|
19
|
+
const value = (raw.registration?.type ?? e.eventType ?? raw.registration?.initialType ?? "").toUpperCase();
|
|
20
|
+
if (value === "NONE" || value.includes("NO_REGISTRATION")) return "none";
|
|
21
|
+
// Open statuses select the footer action, so they must also select the
|
|
22
|
+
// inventory read and action guards when type metadata is absent or stale.
|
|
23
|
+
switch (raw.registration?.status?.toUpperCase()) {
|
|
24
|
+
case "OPEN_TICKETS": return "tickets";
|
|
25
|
+
case "OPEN_EXTERNAL": return "external";
|
|
26
|
+
case "OPEN_RSVP":
|
|
27
|
+
case "OPEN_RSVP_WAITLIST_ONLY":
|
|
28
|
+
case "SCHEDULED_RSVP": return "rsvp";
|
|
29
|
+
}
|
|
30
|
+
if (value.includes("EXTERNAL")) return "external";
|
|
31
|
+
if (value.includes("TICKET")) return "tickets";
|
|
32
|
+
if (value.includes("RSVP")) return "rsvp";
|
|
33
|
+
return "none";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function eventStatus(e: WixEvent): string {
|
|
37
|
+
const raw = e as unknown as { status?: string };
|
|
38
|
+
return (raw.status ?? e.eventStatus ?? "").toUpperCase();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function registrationUi(e: WixEvent, now: number): { state: string; label: string; action: string; caption: string; notice: string } {
|
|
42
|
+
const raw = e as unknown as { registration?: { status?: string; registrationPaused?: boolean; registrationDisabled?: boolean; allowedGuestTypes?: string; rsvp?: { startDate?: string }; tickets?: { soldOut?: boolean }; external?: { url?: string } }; form?: { messages?: { registrationClosed?: { message?: string; exploreEventsActionLabel?: string }; ticketsUnavailable?: { message?: string; exploreEventsActionLabel?: string } } } };
|
|
43
|
+
const r = raw.registration ?? {};
|
|
44
|
+
const status = eventStatus(e);
|
|
45
|
+
const type = registrationKindOf(e);
|
|
46
|
+
if (status === "CANCELED") return { state: "notice", label: "", action: "", caption: "", notice: detailCopy("event.canceledNotice", "This event was canceled.") };
|
|
47
|
+
if (status === "ENDED") return { state: "notice", label: "", action: "", caption: "", notice: detailCopy("event.endedNotice", "This event has ended.") };
|
|
48
|
+
if (type === "none" || r.registrationDisabled) return { state: "none", label: "", action: "", caption: "", notice: "" };
|
|
49
|
+
const ownerClosed = raw.form?.messages?.registrationClosed?.message;
|
|
50
|
+
const ownerSold = raw.form?.messages?.ticketsUnavailable?.message;
|
|
51
|
+
if (["CLOSED_AUTOMATICALLY", "CLOSED_MANUALLY"].includes(r.status || "") || r.registrationPaused) return { state: "notice", label: "", action: "", caption: "", notice: ownerClosed || detailCopy("event.closedNotice", "Registration is closed.") };
|
|
52
|
+
if (type === "tickets" && r.tickets?.soldOut) return { state: "notice", label: "", action: "", caption: "", notice: ownerSold || detailCopy("event.soldOutNotice", "Tickets are sold out.") };
|
|
53
|
+
if (r.status === "SCHEDULED_RSVP") return { state: "scheduled", label: detailCopy("event.rsvpCta", "RSVP"), action: "", caption: detailCopy("event.opensCaption", "RSVP opens {date}.").replace("{date}", r.rsvp?.startDate ? formatWeekdayDateTime(new Date(r.rsvp.startDate)) : ""), notice: "" };
|
|
54
|
+
if (r.status === "OPEN_RSVP_WAITLIST_ONLY") return { state: "waitlist", label: detailCopy("event.waitlistCta", "Join Waitlist"), action: "himi.open", caption: detailCopy("event.fullCaption", "This event is full."), notice: "" };
|
|
55
|
+
if (r.status === "OPEN_TICKETS" || type === "tickets") return { state: "tickets", label: detailCopy("event.ticketsCta", "Buy Tickets"), action: "openTickets", caption: "", notice: "" };
|
|
56
|
+
if (r.status === "OPEN_EXTERNAL" || type === "external") return registrationUrlOf(e)
|
|
57
|
+
? { state: "external", label: detailCopy("event.externalCta", "Register"), action: "openRegistration", caption: "", notice: "" }
|
|
58
|
+
: { state: "none", label: "", action: "", caption: "", notice: "" };
|
|
59
|
+
if (type === "rsvp" && r.status === "OPEN_RSVP") return { state: "rsvp", label: detailCopy("event.rsvpCta", "RSVP"), action: "himi.open", caption: "", notice: "" };
|
|
60
|
+
if (r.status === "OPEN_RSVP" || (type === "rsvp" && !r.status)) return { state: "rsvp", label: detailCopy("event.rsvpCta", "RSVP"), action: "himi.open", caption: "", notice: "" };
|
|
61
|
+
void now;
|
|
62
|
+
return { state: "none", label: "", action: "", caption: "", notice: "" };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** External registration URL / event page URL from the raw payload ("" when absent). */
|
|
66
|
+
export function registrationUrlOf(e: WixEvent): string {
|
|
67
|
+
const raw = e as unknown as {
|
|
68
|
+
registration?: { external?: { registration?: string; url?: string } };
|
|
69
|
+
eventPageUrl?: { base?: string; path?: string } | string;
|
|
70
|
+
};
|
|
71
|
+
const external = raw.registration?.external?.registration ?? raw.registration?.external?.url;
|
|
72
|
+
if (typeof external === "string" && /^https?:\/\//i.test(external)) return external;
|
|
73
|
+
return eventPageUrlOf(e);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The event's public page URL when the payload carries one ("" otherwise). */
|
|
77
|
+
export function eventPageUrlOf(e: WixEvent): string {
|
|
78
|
+
const raw = e as unknown as { eventPageUrl?: { base?: string; path?: string } | string };
|
|
79
|
+
const page = raw.eventPageUrl;
|
|
80
|
+
if (typeof page === "string" && /^https?:\/\//i.test(page)) return page;
|
|
81
|
+
if (page && typeof page === "object" && typeof page.base === "string" && page.base) {
|
|
82
|
+
const path = typeof page.path === "string" ? page.path : "";
|
|
83
|
+
return `${page.base.replace(/\/$/, "")}${path && !path.startsWith("/") ? "/" : ""}${path}`;
|
|
84
|
+
}
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { descriptionBody } from "./rich-body.js";
|
|
1
2
|
// Shared restaurants-menu shaping for himi workers (branded-lite `restaurants`
|
|
2
3
|
// vertical): dish cards + menu categories derived from the Wix Restaurants
|
|
3
4
|
// Menus hierarchy (menu → sections → items). Used by BOTH the menus worker
|
|
@@ -166,7 +167,7 @@ export function itemPriceVariants(item: WixItem): Array<{ id: string; variantId:
|
|
|
166
167
|
|
|
167
168
|
/** HTML-stripped, whitespace-collapsed description snippet (≤ 90 chars). */
|
|
168
169
|
export function descriptionSnippet(value: unknown, maxLength = 90): string {
|
|
169
|
-
const text = (
|
|
170
|
+
const text = descriptionBody(value).bodyText.replace(/\s+/g, " ").trim();
|
|
170
171
|
return text.length > maxLength ? `${text.slice(0, maxLength - 1).trimEnd()}…` : text;
|
|
171
172
|
}
|
|
172
173
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Tier-5 bundle: Himi Blog post detail - reader content plus member actions.
|
|
2
2
|
import { defineService } from "@himalaya/state";
|
|
3
3
|
import { share as shareSheet } from "@himalaya/share";
|
|
4
|
-
import { getPost, likePost, unlikePost, type WixPost } from "@himalaya/wix-blog";
|
|
4
|
+
import { getPost, likePost, unlikePost, type WixPost, type WixPostWithContent } from "@himalaya/wix-blog";
|
|
5
5
|
import { comments, BLOG_COMMENTS_APP_ID, blogCommentRow, listBlogComments, type WixBlogCommentRow } from "@himalaya/wix-comments";
|
|
6
6
|
import { get, type DbEntity } from "@himalaya/local-db";
|
|
7
7
|
import { isSavedId, toggleSavedId } from "../_shared/himi-blog-saved";
|
|
@@ -38,6 +38,7 @@ interface S {
|
|
|
38
38
|
signInRequired: boolean; noSignIn: boolean; signInScreenId: string;
|
|
39
39
|
status: "loading" | "ready" | "error";
|
|
40
40
|
id: string;
|
|
41
|
+
commentResourceId: string;
|
|
41
42
|
title: string;
|
|
42
43
|
subtitle: string;
|
|
43
44
|
/** Author byline ("By <name>") — resolved from the post payload's writer-ish
|
|
@@ -80,7 +81,7 @@ interface S {
|
|
|
80
81
|
blogScreenId: string;
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
type PostWithExtras =
|
|
84
|
+
type PostWithExtras = WixPostWithContent & {
|
|
84
85
|
media?: { wixMedia?: { image?: { url?: string } }; embedMedia?: { thumbnail?: { url?: string } } };
|
|
85
86
|
coverImage?: { image?: { url?: string }; url?: string };
|
|
86
87
|
metrics?: { likeCount?: number; likes?: number; totalComments?: number; commentCount?: number; comments?: number };
|
|
@@ -219,6 +220,7 @@ const baseState: S = derive({
|
|
|
219
220
|
...NO_READ_SIGN_IN, signInScreenId: "himi_account",
|
|
220
221
|
status: "loading",
|
|
221
222
|
id: "",
|
|
223
|
+
commentResourceId: "",
|
|
222
224
|
title: "",
|
|
223
225
|
subtitle: "",
|
|
224
226
|
byline: "",
|
|
@@ -265,6 +267,7 @@ async function cachedPostState(s: S, id: string): Promise<S> {
|
|
|
265
267
|
...s,
|
|
266
268
|
status: "ready",
|
|
267
269
|
id,
|
|
270
|
+
commentResourceId: "",
|
|
268
271
|
title,
|
|
269
272
|
subtitle: String(cached.meta ?? ""),
|
|
270
273
|
excerpt: String(cached.excerpt ?? "").trim(),
|
|
@@ -294,19 +297,28 @@ async function loadPostState(s: S): Promise<S | undefined> {
|
|
|
294
297
|
}
|
|
295
298
|
try {
|
|
296
299
|
const access = await readAccess();
|
|
297
|
-
const
|
|
298
|
-
|
|
300
|
+
const response = await getPost(id, { fieldsets: ["RICH_CONTENT", "CONTENT_TEXT", "INTERNAL_ID"] }, { preserveAccessErrors: true });
|
|
301
|
+
await assertReadAccess(access);
|
|
302
|
+
if (version !== loadVersion) return undefined;
|
|
303
|
+
const post = response.post as PostWithExtras;
|
|
304
|
+
// Native Blog v3 Comments scopes by internalId, not the navigation UUID.
|
|
305
|
+
// A missing projection cannot establish that the post has no comments.
|
|
306
|
+
const commentResourceId = post.internalId?.trim() ?? "";
|
|
307
|
+
let commentsUnavailable = !commentResourceId;
|
|
308
|
+
let comments: CommentRow[] = [];
|
|
309
|
+
if (commentResourceId) {
|
|
310
|
+
try { comments = (await listBlogComments(commentResourceId)).map(toCommentRow); }
|
|
311
|
+
catch { commentsUnavailable = true; }
|
|
312
|
+
}
|
|
299
313
|
await assertReadAccess(access);
|
|
300
314
|
if (version !== loadVersion) return undefined;
|
|
301
|
-
const post = (((postResult.value as unknown as { post?: WixPost }).post) ?? postResult.value) as PostWithExtras;
|
|
302
|
-
const commentsUnavailable = commentsResult.status === "rejected";
|
|
303
|
-
const comments = commentsResult.status === "fulfilled" ? commentsResult.value.map(toCommentRow) : [];
|
|
304
315
|
const cover = imageUrl(post);
|
|
305
316
|
const title = post.title || "Untitled post";
|
|
306
317
|
return derive({
|
|
307
318
|
...clean,
|
|
308
319
|
status: "ready",
|
|
309
320
|
id,
|
|
321
|
+
commentResourceId,
|
|
310
322
|
title,
|
|
311
323
|
subtitle: subtitle(post),
|
|
312
324
|
byline: bylineOf(post),
|
|
@@ -398,9 +410,10 @@ const svc = defineService<S, {
|
|
|
398
410
|
submitComment: async (s) => {
|
|
399
411
|
const text = s.commentDraft.trim();
|
|
400
412
|
if (!text) return derive({ ...s, actionMessage: copyParam("blogPost.commentValidation", "Write a comment first.") });
|
|
413
|
+
if (!s.commentResourceId) return derive({ ...s, actionMessage: copyParam("blogPost.commentsUnavailable", "Comments are unavailable right now.") });
|
|
401
414
|
try {
|
|
402
415
|
const { comment } = await comments.createComment({
|
|
403
|
-
comment: { appId: BLOG_COMMENTS_APP_ID, contextId: s.
|
|
416
|
+
comment: { appId: BLOG_COMMENTS_APP_ID, contextId: s.commentResourceId, resourceId: s.commentResourceId, content: { richContent: commentRichContent(text) } },
|
|
404
417
|
});
|
|
405
418
|
const created = blogCommentRow(comment);
|
|
406
419
|
// Named to avoid shadowing the `comments` namespace import in this same block scope.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { withOnlineWrites } from "../_shared/online-actions.js";
|
|
2
|
+
// himi-branded-lite-notifsettings — reads the site's notification topics + the member's preferences.
|
|
3
|
+
// a failed query is an error with Retry (or Sign in when Wix refuses the
|
|
4
|
+
// visitor) and an empty one is a real empty state — never sample rows presented as
|
|
5
|
+
// the customer's own. Samples appear only in the host's explicit sample preview
|
|
6
|
+
// (_shared/branded-load-state.ts).
|
|
7
|
+
// any exist. INTERACTIVE: tapping a row toggles that topic On/Off — optimistic +
|
|
8
|
+
// best-effort upsertMyNotificationPreferences (a no-op on sample rows).
|
|
9
|
+
import { defineService as registerService } from "@himalaya/state";
|
|
10
|
+
import { withEmptyFlags } from "../_shared/empty-flags.js";
|
|
11
|
+
import { stashCopy } from "../_shared/himi-copy.js";
|
|
12
|
+
import { memberAccess, assertMemberAccess, type MemberAccess } from "../_shared/member-access.js";
|
|
13
|
+
import { LOADING, loaded, loadFailed, type LoadCopy, type LoadFlags } from "../_shared/branded-load-state.js";
|
|
14
|
+
import {
|
|
15
|
+
getNotificationTopics,
|
|
16
|
+
getMyNotificationPreferences,
|
|
17
|
+
upsertMyNotificationPreferences,
|
|
18
|
+
muteStatusFor,
|
|
19
|
+
readTopics,
|
|
20
|
+
readPreferences,
|
|
21
|
+
isDeliveredNow,
|
|
22
|
+
isLocked,
|
|
23
|
+
preferenceFor,
|
|
24
|
+
type NotificationTopicDefinition,
|
|
25
|
+
type PreferenceInfo,
|
|
26
|
+
} from "@himalaya/wix-notification-preferences";
|
|
27
|
+
|
|
28
|
+
/** The channel this surface delivers on — read for the row state, written on toggle.
|
|
29
|
+
* One constant, so the read and the write cannot drift apart. */
|
|
30
|
+
const CHANNEL = "MOBILE";
|
|
31
|
+
|
|
32
|
+
/** `note` is the line under a row's switch — only a locked topic has one. */
|
|
33
|
+
interface Row { id: string; name: string; subtitle: string; note: string; on: boolean; locked: boolean;
|
|
34
|
+
/** Inverse of `locked` (hiddenWhen hides on TRUE) and the plain On/Off a locked row shows. */
|
|
35
|
+
notLocked: boolean; stateLabel: string }
|
|
36
|
+
interface S extends LoadFlags {
|
|
37
|
+
preferencesMember: MemberAccess | null;
|
|
38
|
+
topics: Row[]; emptyText: string; actionMessage: string;
|
|
39
|
+
isMock: boolean; notMock: boolean; /** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
|
|
40
|
+
* shows only when the site really has none, and the unavailable one only when the
|
|
41
|
+
* query failed. A failed query must never render the sentence that claims the
|
|
42
|
+
* customer has no content. */
|
|
43
|
+
notMockEmpty: boolean; notMockUnavailable: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Whether this topic is actually being DELIVERED — not merely what the recipient
|
|
47
|
+
* asked for. `isDeliveredNow` prefers the server's `granted` verdict, so a topic the
|
|
48
|
+
* member unmuted under a channel-wide mute renders OFF instead of promising a
|
|
49
|
+
* notification that will never arrive. No preference row at all ⇒ the topic's default,
|
|
50
|
+
* which is delivery. */
|
|
51
|
+
function isOn(topicId: string, prefs: PreferenceInfo[]): boolean {
|
|
52
|
+
const p = preferenceFor(topicId, prefs, CHANNEL);
|
|
53
|
+
if (!p) return true;
|
|
54
|
+
return isDeliveredNow(p.stateData);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** A row the server says cannot be changed here (a broader rule overrides it). */
|
|
58
|
+
function locked(topicId: string, prefs: PreferenceInfo[]): boolean {
|
|
59
|
+
return isLocked(preferenceFor(topicId, prefs, CHANNEL)?.stateData);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Row subtitle doubles as an affordance: current state + how to change it. */
|
|
63
|
+
function subtitleFor(on: boolean): string {
|
|
64
|
+
return on ? "On · tap to mute" : "Off · tap to enable";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function row(id: string, name: string, on: boolean): Row {
|
|
68
|
+
return { id, name, subtitle: subtitleFor(on), note: "", on, locked: false, notLocked: true, stateLabel: on ? "On" : "Off" };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function norm(t: NotificationTopicDefinition, prefs: PreferenceInfo[]): Row {
|
|
72
|
+
const name = (t.displayName || "Notification").trim() || "Notification";
|
|
73
|
+
const id = t.topicId || name;
|
|
74
|
+
const r = row(id, name, isOn(id, prefs));
|
|
75
|
+
// An overridden row says so instead of offering a toggle that cannot take effect.
|
|
76
|
+
return locked(id, prefs)
|
|
77
|
+
? { ...r, locked: true, notLocked: false, subtitle: "Managed by your channel settings", note: "Managed by your channel settings" }
|
|
78
|
+
: r;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const MOCK: Row[] = [
|
|
82
|
+
row("sample-orders", "Order updates", true),
|
|
83
|
+
row("sample-bookings", "Bookings & reminders", true),
|
|
84
|
+
row("sample-promos", "Promotions & offers", false),
|
|
85
|
+
row("sample-community", "Community activity", true),
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
/** sample rows reach the screen ONLY in the host's explicit sample preview;
|
|
89
|
+
* a failed or empty query is an honest error / empty state (branded-load-state.ts). */
|
|
90
|
+
const COPY: LoadCopy<Row> = {
|
|
91
|
+
prefix: "notifsettings",
|
|
92
|
+
empty: "No notification topics to manage yet.",
|
|
93
|
+
error: "Notification settings couldn't be loaded.",
|
|
94
|
+
signIn: "Sign in to manage your notifications.",
|
|
95
|
+
sample: () => MOCK.map((r) => ({ ...r })),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const defineService = withOnlineWrites(registerService, ["toggle"], { messageFields: ["actionMessage"] });
|
|
99
|
+
const INITIAL = { ...LOADING, topics: [] as Row[], emptyText: "", actionMessage: "", preferencesMember: null };
|
|
100
|
+
const signInRequired = () => new Error("Unauthenticated: sign in to manage notifications");
|
|
101
|
+
let loadVersion = 0;
|
|
102
|
+
const svc = defineService<S, {
|
|
103
|
+
prepare: (s: S) => S;
|
|
104
|
+
load: (s: S, arg?: unknown) => Promise<S | undefined>;
|
|
105
|
+
toggle: (s: S, p?: { id?: string; value?: unknown }) => Promise<S | undefined>;
|
|
106
|
+
}>("himiBrandedNotifSettings", withEmptyFlags({
|
|
107
|
+
state: INITIAL,
|
|
108
|
+
actions: {
|
|
109
|
+
prepare: (s) => ({ ...s, ...INITIAL }),
|
|
110
|
+
load: async (s, arg): Promise<S | undefined> => {
|
|
111
|
+
stashCopy(arg);
|
|
112
|
+
const version = ++loadVersion;
|
|
113
|
+
await svc.prepare();
|
|
114
|
+
try {
|
|
115
|
+
const member = await memberAccess();
|
|
116
|
+
if (!member.email) throw signInRequired();
|
|
117
|
+
const [topicsRes, prefsRes] = await Promise.all([
|
|
118
|
+
getNotificationTopics(),
|
|
119
|
+
getMyNotificationPreferences(),
|
|
120
|
+
]);
|
|
121
|
+
await assertMemberAccess(member);
|
|
122
|
+
const prefs = readPreferences(prefsRes);
|
|
123
|
+
const topics = readTopics(topicsRes).map((t) => norm(t, prefs));
|
|
124
|
+
return version === loadVersion ? { ...s, ...loaded("topics", topics, COPY), preferencesMember: member, actionMessage: "" } : undefined;
|
|
125
|
+
} catch {
|
|
126
|
+
const member = await memberAccess();
|
|
127
|
+
// An authenticated denial is not repaired by an endless sign-in loop.
|
|
128
|
+
const failure = member.email ? new Error(COPY.error) : signInRequired();
|
|
129
|
+
return version === loadVersion ? { ...s, ...loadFailed("topics", failure, COPY), preferencesMember: null, actionMessage: "" } : undefined;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
// Flip one topic's On/Off. Optimistic (updates the row immediately); the real
|
|
133
|
+
// upsert is best-effort and a no-op on sample rows.
|
|
134
|
+
toggle: async (s, p): Promise<S | undefined> => {
|
|
135
|
+
const id = p?.id;
|
|
136
|
+
const target = s.topics.find((r) => r.id === id);
|
|
137
|
+
if (s.busy || !id || !target || target.locked) return undefined;
|
|
138
|
+
if (!s.isMock) {
|
|
139
|
+
try {
|
|
140
|
+
if (!s.preferencesMember) return undefined;
|
|
141
|
+
await assertMemberAccess(s.preferencesMember);
|
|
142
|
+
if (svc.getState().preferencesMember !== s.preferencesMember) return undefined;
|
|
143
|
+
} catch {
|
|
144
|
+
await svc.load();
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
let nextOn = true;
|
|
149
|
+
const topics = s.topics.map((r) => {
|
|
150
|
+
if (r.id !== id) return r;
|
|
151
|
+
// A Toggle sends the NEXT state as `value` (action-payload contract); honour it so
|
|
152
|
+
// a repeated event can never flip the row the wrong way. A bare tap still flips.
|
|
153
|
+
nextOn = typeof p?.value === "boolean" ? p.value : !r.on;
|
|
154
|
+
return { ...r, on: nextOn, subtitle: subtitleFor(nextOn), stateLabel: nextOn ? "On" : "Off" };
|
|
155
|
+
});
|
|
156
|
+
if (!s.isMock && !id.startsWith("sample-")) {
|
|
157
|
+
try {
|
|
158
|
+
// `stateData.state` is a SCALAR enum — the old nested `{ muteStatus }` was a
|
|
159
|
+
// shape this service does not have, so every write was malformed.
|
|
160
|
+
await upsertMyNotificationPreferences([
|
|
161
|
+
{ topicId: id, channel: CHANNEL, stateData: { state: muteStatusFor(nextOn) } },
|
|
162
|
+
]);
|
|
163
|
+
} catch { /* keep the optimistic state */ }
|
|
164
|
+
try { await assertMemberAccess(s.preferencesMember!); }
|
|
165
|
+
catch { await svc.load(); return undefined; }
|
|
166
|
+
}
|
|
167
|
+
return svc.getState().preferencesMember === s.preferencesMember ? { ...svc.getState(), topics } : undefined;
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
}, { topics: "noTopics" }));
|
|
171
|
+
svc.prepare();
|
|
172
|
+
svc.load();
|
|
173
|
+
export { svc };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { descriptionBody } from "../_shared/rich-body.js";
|
|
1
2
|
import { paymentSetupMessage } from "../_shared/payment-setup.js";
|
|
2
3
|
import { offlineWriteMessage, offlineWritesEnabled, withOnlineWrites } from "../_shared/online-actions.js";
|
|
3
4
|
import { connectivity } from "@himalaya/state";
|
|
@@ -140,7 +141,7 @@ function planRow(plan: WixPlan): PlanRow {
|
|
|
140
141
|
return {
|
|
141
142
|
id: plan.id || "",
|
|
142
143
|
name,
|
|
143
|
-
description: (plan.description
|
|
144
|
+
description: descriptionBody(plan.description).bodyText,
|
|
144
145
|
trialBadge,
|
|
145
146
|
noTrialBadge: !trialBadge,
|
|
146
147
|
priceLine: money(plan),
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { WixProgramFull } from "@himalaya/wix-online-programs";
|
|
2
|
+
import { copyParam } from "../_shared/himi-copy.js";
|
|
3
|
+
import { formatDateMedium } from "../_shared/engine-intl.js";
|
|
4
|
+
|
|
5
|
+
export interface ProgramFact { id: string; icon: string; title: string; body: string }
|
|
6
|
+
|
|
7
|
+
/** Only explicit Program fields establish a fact. Missing access is never public. */
|
|
8
|
+
export function programFacts(program: WixProgramFull, locale: string): ProgramFact[] {
|
|
9
|
+
const facts: ProgramFact[] = [];
|
|
10
|
+
const timeline = program.timeline;
|
|
11
|
+
if (timeline?.selfPaced === true) facts.push({
|
|
12
|
+
id: "pace", icon: "clock",
|
|
13
|
+
title: copyParam("program.facts.selfPacedTitle", "Self-paced"),
|
|
14
|
+
body: copyParam("program.facts.selfPacedBody", "Complete steps at your own pace."),
|
|
15
|
+
});
|
|
16
|
+
else if (timeline?.selfPaced === false) facts.push({
|
|
17
|
+
id: "pace", icon: "clock",
|
|
18
|
+
title: copyParam("program.facts.scheduledTitle", "Scheduled"),
|
|
19
|
+
body: copyParam("program.facts.scheduledBody", "Follow the program schedule."),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const rawStart = timeline?.startDate;
|
|
23
|
+
// A date-only field is a calendar day, not midnight in the viewer's time zone. Build it at LOCAL noon and
|
|
24
|
+
// format it in the local zone: that reads the same day with ECMA-402 and on engines without it (watch/Wear),
|
|
25
|
+
// whose fallback ignores a timeZone argument and reads local getters.
|
|
26
|
+
const dateOnly = typeof rawStart === "string" ? /^(\d{4})-(\d{2})-(\d{2})$/.exec(rawStart.trim()) : null;
|
|
27
|
+
const date = dateOnly ? new Date(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3]), 12)
|
|
28
|
+
: typeof rawStart === "string" && rawStart.trim() ? new Date(rawStart) : undefined;
|
|
29
|
+
if (date && Number.isFinite(date.getTime())) {
|
|
30
|
+
let body: string;
|
|
31
|
+
try { body = formatDateMedium(date, locale); }
|
|
32
|
+
catch { body = formatDateMedium(date, "en"); }
|
|
33
|
+
facts.push({ id: "start", icon: "calendar", title: copyParam("program.facts.fixedStartTitle", "Fixed start date"), body });
|
|
34
|
+
} else if (timeline?.selfPaced === true && (rawStart === undefined || rawStart === "")) facts.push({
|
|
35
|
+
id: "start", icon: "calendar",
|
|
36
|
+
title: copyParam("program.facts.flexibleStartTitle", "Flexible start date"),
|
|
37
|
+
body: copyParam("program.facts.flexibleStartBody", "Start whenever you're ready."),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const access = program.restrictions?.accessType?.toUpperCase();
|
|
41
|
+
if (access === "PUBLIC") facts.push({
|
|
42
|
+
id: "access", icon: "globe",
|
|
43
|
+
title: copyParam("program.facts.publicTitle", "Public"),
|
|
44
|
+
body: copyParam("program.facts.publicBody", "Anyone can join."),
|
|
45
|
+
});
|
|
46
|
+
else if (access === "PRIVATE") facts.push({
|
|
47
|
+
id: "access", icon: "lock",
|
|
48
|
+
title: copyParam("program.facts.privateTitle", "Private"),
|
|
49
|
+
body: copyParam("program.facts.privateBody", "Approval is required to join."),
|
|
50
|
+
});
|
|
51
|
+
return facts;
|
|
52
|
+
}
|
|
@@ -40,7 +40,8 @@ import {
|
|
|
40
40
|
} from "@himalaya/wix-online-programs";
|
|
41
41
|
import { createRedirectSession } from "@himalaya/wix-redirects";
|
|
42
42
|
import { getMyMember } from "@himalaya/wix-members";
|
|
43
|
-
import { session as wixPlatformSession } from "@himalaya/wix-platform";
|
|
43
|
+
import { session as wixPlatformSession, i18n } from "@himalaya/wix-platform";
|
|
44
|
+
import { programFacts, type ProgramFact } from "./facts.js";
|
|
44
45
|
import { cleanTitle, initials } from "../_shared/himi-text.js";
|
|
45
46
|
import { copyParam, copyTemplate, stashCopy } from "../_shared/himi-copy.js";
|
|
46
47
|
import { isPaymentSetupFailure, payCashierOrder } from "@himalaya/checkout";
|
|
@@ -62,8 +63,10 @@ interface State {
|
|
|
62
63
|
description: string;
|
|
63
64
|
descriptionJson: string;
|
|
64
65
|
hasDescriptionJson: boolean;
|
|
65
|
-
/** "N sections · M steps"
|
|
66
|
+
/** "N sections · M steps" — structure meta line; pace has its own fact row. */
|
|
66
67
|
meta: string;
|
|
68
|
+
facts: ProgramFact[];
|
|
69
|
+
hideFacts: boolean;
|
|
67
70
|
/** Formatted price when the payload carries one ("" for free/absent). */
|
|
68
71
|
priceLine: string;
|
|
69
72
|
image: string;
|
|
@@ -105,8 +108,11 @@ function inputId(): string {
|
|
|
105
108
|
|
|
106
109
|
function derive(state: State): State {
|
|
107
110
|
const empty = state.status === "ready" && !state.name;
|
|
111
|
+
const facts = Array.isArray(state.facts) ? state.facts : [];
|
|
108
112
|
return {
|
|
109
113
|
...state,
|
|
114
|
+
facts,
|
|
115
|
+
hideFacts: facts.length === 0,
|
|
110
116
|
hasDescriptionJson: state.descriptionJson !== "",
|
|
111
117
|
isLoading: state.status === "loading",
|
|
112
118
|
notLoading: state.status !== "loading",
|
|
@@ -132,9 +138,7 @@ function metaOf(p: WixProgram): string {
|
|
|
132
138
|
const steps = Number(summary?.stepCount ?? 0);
|
|
133
139
|
if (sections) bits.push(`${sections} ${sections === 1 ? "section" : "sections"}`);
|
|
134
140
|
if (steps) bits.push(`${steps} ${steps === 1 ? "step" : "steps"}`);
|
|
135
|
-
|
|
136
|
-
const selfPaced = (p as { timeline?: { selfPaced?: boolean } }).timeline?.selfPaced;
|
|
137
|
-
return selfPaced ? copyParam("program.selfPacedLabel", "Self-paced") : "";
|
|
141
|
+
return bits.join(" · ");
|
|
138
142
|
}
|
|
139
143
|
|
|
140
144
|
function priceOf(p: WixProgram): string {
|
|
@@ -149,10 +153,10 @@ function priceOf(p: WixProgram): string {
|
|
|
149
153
|
}
|
|
150
154
|
}
|
|
151
155
|
|
|
152
|
-
function ready(state: State, p: WixProgram | undefined): State {
|
|
156
|
+
function ready(state: State, p: WixProgram | undefined, locale: string): State {
|
|
153
157
|
const stateOf = ((p?.state ?? p?.status ?? "") as string).toUpperCase();
|
|
154
158
|
if (!p || stateOf === "DRAFT") {
|
|
155
|
-
return derive({ ...state, status: "ready", name: "", description: "", descriptionJson: "", meta: "", priceLine: "", image: "", initial: "", emptyText: copyParam("program.emptyText", "This program is no longer available.") });
|
|
159
|
+
return derive({ ...state, status: "ready", name: "", description: "", descriptionJson: "", meta: "", facts: [], priceLine: "", image: "", initial: "", emptyText: copyParam("program.emptyText", "This program is no longer available.") });
|
|
156
160
|
}
|
|
157
161
|
const name = titleOf(p);
|
|
158
162
|
const image = programImage(p, WIX_DETAIL_IMAGE);
|
|
@@ -164,6 +168,7 @@ function ready(state: State, p: WixProgram | undefined): State {
|
|
|
164
168
|
description: body.bodyText,
|
|
165
169
|
descriptionJson: body.bodyJson,
|
|
166
170
|
meta: metaOf(p),
|
|
171
|
+
facts: programFacts(p, locale),
|
|
167
172
|
priceLine: priceOf(p),
|
|
168
173
|
image,
|
|
169
174
|
initial: initials(name),
|
|
@@ -274,7 +279,7 @@ const pricing = commerce.pricing;
|
|
|
274
279
|
const PRICING_NOT_HYDRATED = [
|
|
275
280
|
"priceLine", "priceNote", "hidePriceRetry", "enrollDisabled", "enrollNote", "enrollHidden", "buyHidden", "buyLabel", "buyDisabled", "buyNote",
|
|
276
281
|
] as const;
|
|
277
|
-
const CONTENT_KEYS = ["status", "name", "description", "descriptionJson", "meta", "image", "initial", "emptyText"] as const;
|
|
282
|
+
const CONTENT_KEYS = ["status", "name", "description", "descriptionJson", "meta", "facts", "image", "initial", "emptyText"] as const;
|
|
278
283
|
type Content = Pick<State, (typeof CONTENT_KEYS)[number]>;
|
|
279
284
|
const NO_PRICE: IapPriceFields = { priceText: "", priceNote: "", priceLoading: false, hidePriceRetry: true, actionDisabled: true, actionNote: "" };
|
|
280
285
|
const NOT_APPLE: IapPurchaseFields = { appleRoute: false, accessReady: false, buyLabel: "", buyDisabled: true, buyBusy: false, buyNote: "" };
|
|
@@ -307,9 +312,12 @@ async function load(state: State, arg?: unknown): Promise<undefined> {
|
|
|
307
312
|
if (presentation === "sheet" || presentation === "full_screen") signInPresentation = presentation;
|
|
308
313
|
const id = inputId();
|
|
309
314
|
const seq = ++loadSeq;
|
|
310
|
-
const found = await
|
|
311
|
-
|
|
312
|
-
|
|
315
|
+
const [found, locale] = await Promise.all([
|
|
316
|
+
fetchProgram(id).then((program) => ({ program }), () => undefined),
|
|
317
|
+
i18n.locale().catch(() => "en"),
|
|
318
|
+
]);
|
|
319
|
+
if (seq !== loadSeq || inputId() !== id) return undefined;
|
|
320
|
+
content = found ? contentOf(ready(state, found.program, locale)) : { ...content, status: "error" };
|
|
313
321
|
pricedItem = found?.program ? onlineProgramItem({ id: inputId() }, priceOf(found.program)) : pricedItem;
|
|
314
322
|
void svc.pricingPublish();
|
|
315
323
|
const prices = found?.program ? priceProgram() : Promise.resolve();
|
|
@@ -467,6 +475,8 @@ const INITIAL_STATE: State = derive({
|
|
|
467
475
|
descriptionJson: "",
|
|
468
476
|
hasDescriptionJson: false,
|
|
469
477
|
meta: "",
|
|
478
|
+
facts: [],
|
|
479
|
+
hideFacts: true,
|
|
470
480
|
priceLine: "",
|
|
471
481
|
image: "",
|
|
472
482
|
initial: "",
|