@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
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { defineService } from "@himalaya/state";
|
|
2
|
+
import { getGroup, listRules, type WixGroup, type WixGroupRule } from "@himalaya/wix-groups";
|
|
3
|
+
import { WIX_DETAIL_IMAGE } from "@himalaya/wix-media";
|
|
4
|
+
import { copyParam, stashCopy } from "../_shared/himi-copy.js";
|
|
5
|
+
import { groupImage } from "../_shared/himi-media.js";
|
|
6
|
+
import { descriptionBody } from "../_shared/rich-body.js";
|
|
7
|
+
import { openRichLink } from "../_shared/rich-content-link.js";
|
|
8
|
+
import { formatDateMedium } from "../_shared/engine-intl.js";
|
|
9
|
+
|
|
10
|
+
// The About & rules page of ONE group. The group is `input.id`, never a fallback: a missing
|
|
11
|
+
// id is an error, not "the first group". The group read and the rules read are independent —
|
|
12
|
+
// the page renders the group's own facts when only the rules read failed, and says so in the
|
|
13
|
+
// rules region, so a rules outage is never shown as "this group has no rules".
|
|
14
|
+
//
|
|
15
|
+
// Read-only and online-only: no write exists on this page, so there is nothing to queue.
|
|
16
|
+
|
|
17
|
+
interface RuleRow {
|
|
18
|
+
id: string;
|
|
19
|
+
number: string;
|
|
20
|
+
title: string;
|
|
21
|
+
description: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type Failure = "forbidden" | "missing" | "failed";
|
|
25
|
+
|
|
26
|
+
interface State {
|
|
27
|
+
status: "loading" | "ready" | "error";
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
descriptionJson: string;
|
|
32
|
+
hasDescriptionJson: boolean;
|
|
33
|
+
privacy: string;
|
|
34
|
+
createdDate: string;
|
|
35
|
+
memberCount: string;
|
|
36
|
+
image: string;
|
|
37
|
+
initial: string;
|
|
38
|
+
rules: RuleRow[];
|
|
39
|
+
/** The group read failed: the whole page says why. */
|
|
40
|
+
errorText: string;
|
|
41
|
+
/** The rules read alone failed: only the rules region says why. */
|
|
42
|
+
rulesErrorText: string;
|
|
43
|
+
hideLoading: boolean;
|
|
44
|
+
hideError: boolean;
|
|
45
|
+
hideContent: boolean;
|
|
46
|
+
hideImage: boolean;
|
|
47
|
+
hidePrivacy: boolean;
|
|
48
|
+
hideCreated: boolean;
|
|
49
|
+
hideMemberCount: boolean;
|
|
50
|
+
hideRules: boolean;
|
|
51
|
+
hideRulesEmpty: boolean;
|
|
52
|
+
hideRulesError: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const initial: State = {
|
|
56
|
+
status: "loading",
|
|
57
|
+
id: "",
|
|
58
|
+
name: "",
|
|
59
|
+
description: "",
|
|
60
|
+
descriptionJson: "",
|
|
61
|
+
hasDescriptionJson: false,
|
|
62
|
+
privacy: "",
|
|
63
|
+
createdDate: "",
|
|
64
|
+
memberCount: "",
|
|
65
|
+
image: "",
|
|
66
|
+
initial: "G",
|
|
67
|
+
rules: [],
|
|
68
|
+
errorText: "",
|
|
69
|
+
rulesErrorText: "",
|
|
70
|
+
hideLoading: false,
|
|
71
|
+
hideError: true,
|
|
72
|
+
hideContent: true,
|
|
73
|
+
hideImage: true,
|
|
74
|
+
hidePrivacy: true,
|
|
75
|
+
hideCreated: true,
|
|
76
|
+
hideMemberCount: true,
|
|
77
|
+
hideRules: true,
|
|
78
|
+
hideRulesEmpty: true,
|
|
79
|
+
hideRulesError: true,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** Bumped by every group load, so a response that arrives after a newer load began is dropped. */
|
|
83
|
+
let loadSeq = 0;
|
|
84
|
+
|
|
85
|
+
function inputId(): string {
|
|
86
|
+
// `himalaya.input.id` is the contract: hosts thread the opener's `id` nav param
|
|
87
|
+
// (himi.open { screenId: "himi_group_about", id }) into worker input.
|
|
88
|
+
const input = (globalThis as { himalaya?: { input?: { id?: string } } }).himalaya?.input;
|
|
89
|
+
return typeof input?.id === "string" ? input.id : "";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function failureOf(e: unknown): Failure {
|
|
93
|
+
const message = String((e as Error)?.message ?? e);
|
|
94
|
+
if (/HTTP (401|403)\b/.test(message)) return "forbidden";
|
|
95
|
+
if (/HTTP 404\b/.test(message)) return "missing";
|
|
96
|
+
return "failed";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function groupFailureText(failure: Failure): string {
|
|
100
|
+
if (failure === "forbidden") return copyParam("groupAbout.forbiddenTitle", "You can’t view this group’s details.");
|
|
101
|
+
if (failure === "missing") return copyParam("groupAbout.missingTitle", "This group no longer exists.");
|
|
102
|
+
return copyParam("groupAbout.errorTitle", "This group’s details could not be loaded.");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function rulesFailureText(failure: Failure): string {
|
|
106
|
+
if (failure === "forbidden") return copyParam("groupAbout.rulesForbidden", "You can’t view this group’s rules.");
|
|
107
|
+
return copyParam("groupAbout.rulesError", "The rules could not be loaded.");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The rules the server sent, numbered from 1. `undefined` when the answer is not a rules list
|
|
111
|
+
* at all — a missing or non-array `rules` is a malformed answer, not an empty group. */
|
|
112
|
+
function ruleRowsOf(response: unknown): RuleRow[] | undefined {
|
|
113
|
+
const rules = (response as { rules?: unknown } | null | undefined)?.rules;
|
|
114
|
+
if (!Array.isArray(rules)) return undefined;
|
|
115
|
+
const rows: RuleRow[] = [];
|
|
116
|
+
for (const entry of rules as WixGroupRule[]) {
|
|
117
|
+
const title = typeof entry?.title === "string" ? entry.title.trim() : "";
|
|
118
|
+
const description = typeof entry?.description === "string" ? entry.description.trim() : "";
|
|
119
|
+
if (!title && !description) continue;
|
|
120
|
+
rows.push({ id: `rule_${rows.length + 1}`, number: String(rows.length + 1), title, description });
|
|
121
|
+
}
|
|
122
|
+
return rows;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function withRules(state: State, rules: RuleRow[] | undefined, failure: Failure | undefined): State {
|
|
126
|
+
if (failure || !rules) {
|
|
127
|
+
return {
|
|
128
|
+
...state,
|
|
129
|
+
rules: [],
|
|
130
|
+
rulesErrorText: rulesFailureText(failure ?? "failed"),
|
|
131
|
+
hideRules: true,
|
|
132
|
+
hideRulesEmpty: true,
|
|
133
|
+
hideRulesError: false,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
...state,
|
|
138
|
+
rules,
|
|
139
|
+
rulesErrorText: "",
|
|
140
|
+
hideRules: rules.length === 0,
|
|
141
|
+
hideRulesEmpty: rules.length > 0,
|
|
142
|
+
hideRulesError: true,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function capitalized(value: string): string {
|
|
147
|
+
const lower = value.trim().toLowerCase();
|
|
148
|
+
return lower ? lower[0].toUpperCase() + lower.slice(1) : "";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function createdLabel(value: unknown): string {
|
|
152
|
+
if (typeof value !== "string" || !value) return "";
|
|
153
|
+
const date = new Date(value);
|
|
154
|
+
return Number.isNaN(date.getTime()) ? "" : formatDateMedium(date);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function groupState(state: State, id: string, group: WixGroup): State {
|
|
158
|
+
const name = (group.name || (group as { title?: string }).title || copyParam("group.groupFallbackName", "Group")).trim();
|
|
159
|
+
const body = descriptionBody(group.description, descriptionBody(group.teaser, "").bodyText);
|
|
160
|
+
const privacy = capitalized(group.privacyStatus ?? "");
|
|
161
|
+
const created = createdLabel(group.createdDate);
|
|
162
|
+
const count = group.membersCount ?? group.memberCount;
|
|
163
|
+
const image = groupImage(group, WIX_DETAIL_IMAGE);
|
|
164
|
+
return {
|
|
165
|
+
...state,
|
|
166
|
+
status: "ready",
|
|
167
|
+
id,
|
|
168
|
+
name,
|
|
169
|
+
description: body.bodyText,
|
|
170
|
+
descriptionJson: body.bodyJson,
|
|
171
|
+
hasDescriptionJson: body.bodyJson !== "",
|
|
172
|
+
privacy,
|
|
173
|
+
createdDate: created,
|
|
174
|
+
memberCount: typeof count === "number" ? `${count} ${count === 1 ? "member" : "members"}` : "",
|
|
175
|
+
image,
|
|
176
|
+
initial: (name[0] || "G").toUpperCase(),
|
|
177
|
+
errorText: "",
|
|
178
|
+
hideLoading: true,
|
|
179
|
+
hideError: true,
|
|
180
|
+
hideContent: false,
|
|
181
|
+
hideImage: !image,
|
|
182
|
+
hidePrivacy: !privacy,
|
|
183
|
+
hideCreated: !created,
|
|
184
|
+
hideMemberCount: typeof count !== "number",
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function groupErrorState(id: string, failure: Failure): State {
|
|
189
|
+
return {
|
|
190
|
+
...initial,
|
|
191
|
+
id,
|
|
192
|
+
status: "error",
|
|
193
|
+
errorText: groupFailureText(failure),
|
|
194
|
+
hideLoading: true,
|
|
195
|
+
hideError: false,
|
|
196
|
+
hideContent: true,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const svc = defineService<State, {
|
|
201
|
+
load: (state: State, arg?: unknown) => Promise<State | undefined>;
|
|
202
|
+
reloadRules: (state: State, arg?: unknown) => Promise<State | undefined>;
|
|
203
|
+
openLink: (state: State, arg?: unknown) => Promise<undefined>;
|
|
204
|
+
}>("himiBrandedGroupAbout", {
|
|
205
|
+
state: initial,
|
|
206
|
+
actions: {
|
|
207
|
+
load: async (state, arg) => {
|
|
208
|
+
stashCopy(arg);
|
|
209
|
+
const seq = ++loadSeq;
|
|
210
|
+
const id = inputId();
|
|
211
|
+
if (!id) return groupErrorState("", "missing");
|
|
212
|
+
const [groupResult, rulesResult] = await Promise.allSettled([getGroup(id), listRules(id)]);
|
|
213
|
+
// A newer load began while these reads were in flight: its answer owns the state.
|
|
214
|
+
if (seq !== loadSeq) return undefined;
|
|
215
|
+
if (groupResult.status === "rejected") return groupErrorState(id, failureOf(groupResult.reason));
|
|
216
|
+
// Social Groups wraps the entity as `{ group }`; the typed signature claims a bare group.
|
|
217
|
+
const group = ((groupResult.value as unknown as { group?: WixGroup }).group ?? groupResult.value) as WixGroup;
|
|
218
|
+
if (!group || typeof group !== "object" || (group.id && group.id !== id)) return groupErrorState(id, "failed");
|
|
219
|
+
const ready = groupState(state, id, group);
|
|
220
|
+
return rulesResult.status === "rejected"
|
|
221
|
+
? withRules(ready, undefined, failureOf(rulesResult.reason))
|
|
222
|
+
: withRules(ready, ruleRowsOf(rulesResult.value), undefined);
|
|
223
|
+
},
|
|
224
|
+
// Retry for the rules region alone: the group's own facts stay on screen.
|
|
225
|
+
reloadRules: async (state, arg) => {
|
|
226
|
+
stashCopy(arg);
|
|
227
|
+
const id = state.id;
|
|
228
|
+
if (!id) return undefined;
|
|
229
|
+
const seq = loadSeq;
|
|
230
|
+
try {
|
|
231
|
+
const rules = ruleRowsOf(await listRules(id));
|
|
232
|
+
if (seq !== loadSeq || inputId() !== id) return undefined;
|
|
233
|
+
return withRules(state, rules, undefined);
|
|
234
|
+
} catch (e) {
|
|
235
|
+
if (seq !== loadSeq || inputId() !== id) return undefined;
|
|
236
|
+
return withRules(state, undefined, failureOf(e));
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
openLink: async (_state, arg) => { await openRichLink(arg); return undefined; },
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
export { svc };
|
|
@@ -1,69 +1,135 @@
|
|
|
1
|
-
//
|
|
2
|
-
// a failed query is an error with Retry (or Sign in when Wix refuses the
|
|
3
|
-
// visitor) and an empty one is a real empty state — never sample rows presented as
|
|
4
|
-
// the customer's own. Samples appear only in the host's explicit sample preview
|
|
5
|
-
// (_shared/branded-load-state.ts).
|
|
1
|
+
// Public Loyalty offering plus the current member's separately verified balance.
|
|
6
2
|
import { defineService } from "../_shared/collection-service.js";
|
|
7
3
|
import { LOADING, loaded, loadFailed, type LoadCopy, type LoadFlags } from "../_shared/branded-load-state.js";
|
|
8
|
-
import {
|
|
4
|
+
import { copyParam, copyTemplate } from "../_shared/himi-copy.js";
|
|
5
|
+
import { formatCurrency, formatNumber } from "../_shared/engine-intl.js";
|
|
6
|
+
import { memberAccess, assertMemberAccess } from "../_shared/member-access.js";
|
|
7
|
+
import { getLoyaltyProgram, listLoyaltyRewards, listLoyaltyEarningRules, getCurrentMemberLoyaltyAccount, type WixLoyaltyReward, type WixLoyaltyEarningRule } from "@himalaya/wix-loyalty";
|
|
9
8
|
|
|
10
9
|
interface Row { id: string; name: string; subtitle: string }
|
|
11
10
|
interface S extends LoadFlags {
|
|
12
|
-
programName: string; unit: string;
|
|
13
|
-
rewards: Row[]; emptyText: string;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
notMockEmpty: boolean; notMockUnavailable: boolean;
|
|
11
|
+
programName: string; unit: string; currency: string;
|
|
12
|
+
rewards: Row[]; earningRules: Row[]; emptyText: string; earningEmptyText: string;
|
|
13
|
+
hideCatalog: boolean; hideEarningHeading: boolean; inactiveText: string;
|
|
14
|
+
rewardError: string; earningError: string; hideRewardError: boolean; hideEarningError: boolean; hideRewardRows: boolean;
|
|
15
|
+
memberPoints: string; memberMessage: string;
|
|
16
|
+
hideMemberCard: boolean; hideMemberSignIn: boolean; hideMemberRetry: boolean;
|
|
19
17
|
}
|
|
18
|
+
const PRIVATE_EMPTY = { memberPoints: "", memberMessage: "", hideMemberCard: true, hideMemberSignIn: true, hideMemberRetry: true };
|
|
19
|
+
const INITIAL: S = { ...LOADING, ...PRIVATE_EMPTY, programName: "", unit: "", currency: "", rewards: [], earningRules: [], emptyText: "", earningEmptyText: "", hideCatalog: false, hideEarningHeading: true, inactiveText: "", rewardError: "", earningError: "", hideRewardError: true, hideEarningError: true, hideRewardRows: false };
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
function
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
if (pts <= 0) return "Free";
|
|
26
|
-
return `${pts.toLocaleString()} ${unit}`;
|
|
21
|
+
function amount(value: unknown): value is number { return typeof value === "number" && Number.isFinite(value) && value >= 0; }
|
|
22
|
+
function baseConfig<T extends { tierId?: string }>(configs: T[] | undefined): T | undefined {
|
|
23
|
+
const base = configs?.filter(config => !config.tierId);
|
|
24
|
+
return base?.length === 1 ? base[0] : undefined;
|
|
27
25
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return
|
|
26
|
+
function points(value: number, customUnit: string): string {
|
|
27
|
+
const unit = customUnit || copyParam(value === 1 ? "loyalty.pointOne" : "loyalty.pointMany", value === 1 ? "point" : "points");
|
|
28
|
+
return copyTemplate("loyalty.pointsLabel", "{points} {unit}", { points: formatNumber(value), unit });
|
|
29
|
+
}
|
|
30
|
+
function rewardLabel(reward: WixLoyaltyReward, customUnit: string): string {
|
|
31
|
+
const configs = reward.discountAmount?.configsByTier ?? reward.couponReward?.fixedAmount?.configsByTier
|
|
32
|
+
?? reward.couponReward?.percentage?.configsByTier ?? reward.couponReward?.freeShipping?.configsByTier;
|
|
33
|
+
const cost = configs?.length ? baseConfig<{ tierId?: string; costInPoints?: number }>(configs)?.costInPoints : reward.requiredPoints;
|
|
34
|
+
if (!amount(cost)) return copyParam("loyalty.costUnavailable", "Points details unavailable");
|
|
35
|
+
return cost === 0 ? copyParam("loyalty.free", "Free") : points(cost, customUnit);
|
|
36
|
+
}
|
|
37
|
+
function earningLabel(rule: WixLoyaltyEarningRule, customUnit: string, currency: string): string {
|
|
38
|
+
if (rule.fixedAmount) {
|
|
39
|
+
const value = baseConfig(rule.fixedAmount.configs)?.points;
|
|
40
|
+
return amount(value) ? points(value, customUnit) : copyParam("loyalty.earningUnavailable", "Points details unavailable");
|
|
41
|
+
}
|
|
42
|
+
const rate = baseConfig(rule.conversionRate?.configs);
|
|
43
|
+
if (!rate || !amount(rate.points) || !amount(rate.moneyAmount) || rate.moneyAmount === 0 || !currency) {
|
|
44
|
+
return copyParam("loyalty.earningUnavailable", "Points details unavailable");
|
|
45
|
+
}
|
|
46
|
+
return copyTemplate("loyalty.pointsPerSpend", "{points} per {amount}", {
|
|
47
|
+
points: points(rate.points, customUnit), amount: formatCurrency(rate.moneyAmount, currency, "en", Number.isInteger(rate.moneyAmount) ? 0 : undefined),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function businessStamp(): unknown {
|
|
51
|
+
return (globalThis as { himalaya?: { input?: Record<string, unknown> } }).himalaya?.input?.__himiPreferenceBusiness;
|
|
52
|
+
}
|
|
53
|
+
function currencyFrom(s: S, arg: unknown): string {
|
|
54
|
+
if (!arg || typeof arg !== "object" || !("currency" in arg)) return s.currency;
|
|
55
|
+
const raw = (arg as { currency?: unknown }).currency;
|
|
56
|
+
return typeof raw === "string" && /^[A-Z]{3}$/.test(raw) ? raw : "";
|
|
31
57
|
}
|
|
32
|
-
|
|
33
|
-
const MOCK: Row[] = [
|
|
34
|
-
{ id: "sample-5off", name: "5% off your next order", subtitle: "500 points" },
|
|
35
|
-
{ id: "sample-ship", name: "Free shipping", subtitle: "300 points" },
|
|
36
|
-
{ id: "sample-15", name: "15% off any purchase", subtitle: "750 points" },
|
|
37
|
-
{ id: "sample-gift", name: "Free welcome gift", subtitle: "1,000 points" },
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
/** sample rows reach the screen ONLY in the host's explicit sample preview;
|
|
41
|
-
* a failed or empty query is an honest error / empty state (branded-load-state.ts). */
|
|
42
58
|
const COPY: LoadCopy<Row> = {
|
|
43
|
-
prefix: "loyalty",
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
signIn: "Sign in to see your points and rewards.",
|
|
47
|
-
sample: () => MOCK.map((r) => ({ ...r })),
|
|
59
|
+
prefix: "loyalty", empty: "No rewards to redeem yet.",
|
|
60
|
+
error: "The rewards program couldn't be loaded.", signIn: "The rewards program couldn't be loaded.",
|
|
61
|
+
sample: () => [{ id: "sample-reward", name: copyParam("loyalty.sampleReward", "5% off your next order"), subtitle: points(500, "") }],
|
|
48
62
|
};
|
|
49
63
|
|
|
50
|
-
const svc = defineService<S, { load: (s: S) => Promise<S> }>("himiBrandedLoyalty", {
|
|
51
|
-
state:
|
|
64
|
+
const svc = defineService<S, { prepare: (s: S) => S; load: (s: S, arg?: unknown) => Promise<S> }>("himiBrandedLoyalty", {
|
|
65
|
+
state: INITIAL,
|
|
66
|
+
notHydrated: ["memberPoints", "memberMessage", "hideMemberCard", "hideMemberSignIn", "hideMemberRetry"],
|
|
67
|
+
notPersisted: ["memberPoints", "memberMessage", "hideMemberCard", "hideMemberSignIn", "hideMemberRetry"],
|
|
52
68
|
actions: {
|
|
53
|
-
|
|
69
|
+
prepare: s => ({ ...s, ...PRIVATE_EMPTY }),
|
|
70
|
+
load: async (s, arg) => {
|
|
71
|
+
const currency = currencyFrom(s, arg);
|
|
72
|
+
const business = businessStamp();
|
|
54
73
|
try {
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
const [response, member] = await Promise.all([getLoyaltyProgram(), memberAccess()]);
|
|
75
|
+
const program = response.loyaltyProgram ?? response.program;
|
|
76
|
+
if (!program || !["ACTIVE", "DRAFT", "PAUSED"].includes(program.status ?? "")) throw new Error("Loyalty program response is unavailable");
|
|
77
|
+
const customUnit = (program.pointDefinition?.customName ?? "").trim();
|
|
78
|
+
const common = { ...s, ...PRIVATE_EMPTY, currency, programName: program.name || copyParam("loyalty.programFallback", "Rewards"), unit: customUnit || copyParam("loyalty.pointMany", "points"), inactiveText: "", rewardError: "", earningError: "", hideRewardError: true, hideEarningError: true, hideRewardRows: true, hideEarningHeading: true };
|
|
79
|
+
if (program.status !== "ACTIVE") {
|
|
80
|
+
return { ...common, ...loaded("rewards", [], COPY), earningRules: [], earningEmptyText: "", hideCatalog: true,
|
|
81
|
+
inactiveText: copyParam("loyalty.inactive", "The rewards program isn't available right now.") };
|
|
82
|
+
}
|
|
83
|
+
// Balance failures cannot discard a public offering that loaded correctly.
|
|
84
|
+
const privateStateSupported = !!member.scope && ((globalThis as { himalaya?: { readLifecycleVersion?: number } }).himalaya?.readLifecycleVersion ?? 0) >= 1;
|
|
85
|
+
const accountRead = member.email && privateStateSupported
|
|
86
|
+
? getCurrentMemberLoyaltyAccount().then(value => ({ value, failed: false })).catch(() => ({ value: undefined, failed: true }))
|
|
87
|
+
: Promise.resolve({ value: undefined, failed: false });
|
|
88
|
+
const [[rewardResult, earningResult], account] = await Promise.all([
|
|
89
|
+
Promise.allSettled([listLoyaltyRewards(), listLoyaltyEarningRules()]), accountRead,
|
|
90
|
+
]);
|
|
91
|
+
const rewardResponse = rewardResult.status === "fulfilled" ? rewardResult.value : undefined;
|
|
92
|
+
const earningResponse = earningResult.status === "fulfilled" ? earningResult.value : undefined;
|
|
93
|
+
if (businessStamp() !== business) throw new Error("Business changed while Loyalty loaded");
|
|
94
|
+
if (rewardResult.status === "rejected" && earningResult.status === "rejected") throw new Error("Loyalty catalog is unavailable");
|
|
95
|
+
const rewards = (rewardResponse?.rewards ?? []).filter(reward => reward.active === true).map((reward, i) => ({
|
|
96
|
+
id: reward.id || `reward-${i}`, name: reward.name || copyParam("loyalty.rewardFallback", "Reward"), subtitle: rewardLabel(reward, customUnit),
|
|
97
|
+
}));
|
|
98
|
+
const earningRules = (earningResponse?.earningRules ?? []).filter(rule => rule.status === "ACTIVE").map((rule, i) => ({
|
|
99
|
+
id: rule.id || `earning-${i}`, name: rule.title || copyParam("loyalty.earnFallback", "Earn points"), subtitle: earningLabel(rule, customUnit, currency),
|
|
100
|
+
}));
|
|
101
|
+
let privateState = { ...PRIVATE_EMPTY };
|
|
102
|
+
if (member.email) {
|
|
103
|
+
try {
|
|
104
|
+
await assertMemberAccess(member);
|
|
105
|
+
const balance = account.value?.account?.points?.balance;
|
|
106
|
+
privateState = { ...PRIVATE_EMPTY, hideMemberCard: false,
|
|
107
|
+
memberPoints: typeof balance === "number" && Number.isFinite(balance) ? points(balance, customUnit) : "",
|
|
108
|
+
memberMessage: !privateStateSupported ? copyParam("loyalty.updateApp", "Update the app to see your points securely.") : account.failed ? copyParam("loyalty.pointsError", "Your points couldn't be loaded.")
|
|
109
|
+
: typeof balance !== "number" || !Number.isFinite(balance) ? copyParam("loyalty.pointsUnavailable", "Your points aren't available yet.") : "",
|
|
110
|
+
hideMemberRetry: !account.failed };
|
|
111
|
+
} catch {
|
|
112
|
+
const current = await memberAccess();
|
|
113
|
+
privateState = { ...PRIVATE_EMPTY, hideMemberCard: !current.email, hideMemberSignIn: !!current.email,
|
|
114
|
+
memberMessage: current.email ? copyParam("loyalty.pointsError", "Your points couldn't be loaded.") : "", hideMemberRetry: !current.email };
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
privateState.hideMemberSignIn = false;
|
|
118
|
+
}
|
|
119
|
+
return { ...common, ...loaded("rewards", rewards, COPY), ...privateState, earningRules, hideCatalog: false, hideEarningHeading: false,
|
|
120
|
+
rewardError: rewardResult.status === "rejected" ? copyParam("loyalty.rewardsError", "Rewards couldn't be loaded.") : "",
|
|
121
|
+
earningError: earningResult.status === "rejected" ? copyParam("loyalty.earningError", "Ways to earn points couldn't be loaded.") : "",
|
|
122
|
+
hideRewardError: rewardResult.status === "fulfilled", hideEarningError: earningResult.status === "fulfilled", hideRewardRows: rewardResult.status === "rejected",
|
|
123
|
+
emptyText: rewardResult.status === "rejected" ? "" : rewards.length ? "" : copyParam("loyalty.emptyText", COPY.empty),
|
|
124
|
+
earningEmptyText: earningResult.status === "rejected" || earningRules.length ? "" : copyParam("loyalty.earningEmpty", "No ways to earn points yet.") };
|
|
125
|
+
} catch (error) {
|
|
126
|
+
const failure = loadFailed("rewards", error, COPY);
|
|
127
|
+
return { ...s, ...PRIVATE_EMPTY, currency, earningRules: [], earningEmptyText: "", inactiveText: "", hideCatalog: !failure.isMock, hideEarningHeading: true, rewardError: "", earningError: "", hideRewardError: true, hideEarningError: true, hideRewardRows: !failure.isMock,
|
|
128
|
+
...failure, signInRequired: false, noSignIn: true };
|
|
64
129
|
}
|
|
65
130
|
},
|
|
66
131
|
},
|
|
67
132
|
});
|
|
133
|
+
svc.prepare();
|
|
68
134
|
svc.load();
|
|
69
135
|
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),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { descriptionBody } from "../_shared/rich-body.js";
|
|
1
2
|
// himi-branded-lite-podcasts — live RSS episodes with a persistent queue and
|
|
2
3
|
// the shared native/web media playback transport.
|
|
3
4
|
import { defineService } from "../_shared/collection-service.js";
|
|
@@ -99,7 +100,7 @@ function norm(episode: Episode): Row | null {
|
|
|
99
100
|
title: (episode.title || "Episode").trim() || "Episode",
|
|
100
101
|
subtitle: [dateLabel(episode.date), duration ? clock(duration) : ""].filter(Boolean).join(" · "),
|
|
101
102
|
image: (episode.image || "").trim(),
|
|
102
|
-
description: (episode.descriptionExcerpt
|
|
103
|
+
description: descriptionBody(episode.descriptionExcerpt).bodyText,
|
|
103
104
|
audioUrl,
|
|
104
105
|
durationMs: duration,
|
|
105
106
|
progressLabel: "",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { descriptionBody } from "../_shared/rich-body.js";
|
|
1
2
|
// himi-branded-lite-portfolio — real, public Wix Portfolio projects.
|
|
2
3
|
// Empty and error states stay explicit; media IDs are normalized for every host.
|
|
3
4
|
import { defineService } from "../_shared/collection-service.js";
|
|
@@ -28,7 +29,7 @@ function norm(project: WixPortfolioProject): Row {
|
|
|
28
29
|
return {
|
|
29
30
|
id: project.id || project.slug || title,
|
|
30
31
|
title,
|
|
31
|
-
subtitle: (project.description
|
|
32
|
+
subtitle: descriptionBody(project.description).bodyText,
|
|
32
33
|
image,
|
|
33
34
|
initial: (title[0] || "#").toUpperCase(),
|
|
34
35
|
hasImage: Boolean(image),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { descriptionBody } from "../_shared/rich-body.js";
|
|
1
2
|
// himi-branded-lite-portfolio-project — real Wix Portfolio project detail.
|
|
2
3
|
// The public project row is resolved again from Wix, then its ordered project
|
|
3
4
|
// items supply the actual image/video/text story. No sample project is invented.
|
|
@@ -59,7 +60,7 @@ function row(item: WixPortfolioProjectItem, index: number): MediaRow {
|
|
|
59
60
|
return {
|
|
60
61
|
id: item.id || `portfolio-item-${index}`,
|
|
61
62
|
title: (item.title ?? "").trim(),
|
|
62
|
-
description: (item.description
|
|
63
|
+
description: descriptionBody(item.description).bodyText,
|
|
63
64
|
image,
|
|
64
65
|
videoUrl: hasVideo ? videoUrl : "",
|
|
65
66
|
hasImage,
|
|
@@ -132,7 +133,7 @@ const svc = defineService<State, { load: (state: State, arg?: unknown) => Promis
|
|
|
132
133
|
id,
|
|
133
134
|
title: (project.title || copyParam("portfolioProject.projectFallbackName", "Project")).trim(),
|
|
134
135
|
initial: ((project.title || "P").trim()[0] || "P").toUpperCase(),
|
|
135
|
-
description: (project.description
|
|
136
|
+
description: descriptionBody(project.description).bodyText,
|
|
136
137
|
coverImage: wixMediaUrl(portfolioCoverRef(project.coverImage), WIX_DETAIL_IMAGE),
|
|
137
138
|
items,
|
|
138
139
|
emptyText: items.length ? "" : copyParam("portfolioProject.noMediaText", "This project has no published media yet."),
|
|
@@ -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: "",
|