@wix/himalaya-cli 0.1392.0 → 0.1393.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/bookings-v2/src/extensions.d.ts +7 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-bookings.mjs +4 -4
- package/dist/cli.mjs +31 -24
- package/dist/preview-assets.generated.bin +0 -0
- package/guides/sdk-reference.json +4 -4
- package/package.json +2 -2
- package/templates/beauty-salon/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/beauty-salon/tier5-src/himi-branded-lite-cart/index.ts +52 -28
- package/templates/beauty-salon-owner/dev/index.ts +545 -100
- package/templates/beauty-salon-owner/himalaya.content.json +6 -0
- package/templates/beauty-salon-owner/tier5-src/_shared/owner-account.ts +6 -2
- package/templates/beauty-salon-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
- package/templates/beauty-salon-owner/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
- package/templates/branded-lite/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/branded-lite/tier5-src/himi-branded-lite-cart/index.ts +52 -28
- package/templates/branded-lite/tier5-src/himi-branded-lite-group/index.ts +24 -24
- package/templates/community-club/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/community-club/tier5-src/himi-branded-lite-group/index.ts +24 -24
- package/templates/community-club-owner/dev/index.ts +317 -100
- package/templates/community-club-owner/himalaya.content.json +6 -0
- package/templates/community-club-owner/tier5-src/_shared/owner-account.ts +6 -2
- package/templates/community-club-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
- package/templates/community-club-owner/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/community-club-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
- package/templates/community-club-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
- package/templates/community-club-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
- package/templates/community-club-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
- package/templates/community-club-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
- package/templates/community-club-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
- package/templates/community-club-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
- package/templates/community-club-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
- package/templates/community-club-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
- package/templates/community-club-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
- package/templates/community-club-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
- package/templates/community-club-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
- package/templates/community-club-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
- package/templates/community-club-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
- package/templates/community-club-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
- package/templates/community-club-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
- package/templates/community-club-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
- package/templates/community-club-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
- package/templates/events-meetups/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/events-meetups/tier5-src/himi-branded-lite-group/index.ts +24 -24
- package/templates/events-meetups-owner/dev/index.ts +275 -100
- package/templates/events-meetups-owner/himalaya.content.json +6 -0
- package/templates/events-meetups-owner/tier5-src/_shared/owner-account.ts +6 -2
- package/templates/events-meetups-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
- package/templates/events-meetups-owner/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
- package/templates/gym-fitness/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/gym-fitness/tier5-src/himi-branded-lite-cart/index.ts +52 -28
- package/templates/gym-fitness-owner/dev/index.ts +545 -100
- package/templates/gym-fitness-owner/himalaya.content.json +6 -0
- package/templates/gym-fitness-owner/tier5-src/_shared/owner-account.ts +6 -2
- package/templates/gym-fitness-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
- package/templates/gym-fitness-owner/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
- package/templates/himi-chat/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/nonprofit-cause/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-group/index.ts +24 -24
- package/templates/nonprofit-cause-owner/dev/index.ts +503 -100
- package/templates/nonprofit-cause-owner/himalaya.content.json +6 -0
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-account.ts +6 -2
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
- package/templates/owner-lite/dev/index.ts +545 -100
- package/templates/owner-lite/himalaya.content.json +6 -0
- package/templates/owner-lite/tier5-src/_shared/owner-account.ts +6 -2
- package/templates/owner-lite/tier5-src/_shared/owner-booking-respond.ts +82 -0
- package/templates/owner-lite/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/owner-lite/tier5-src/himi-owner-lite-account/index.ts +7 -4
- package/templates/owner-lite/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-ask/live.ts +3 -3
- package/templates/owner-lite/tier5-src/himi-owner-lite-event/index.ts +18 -20
- package/templates/owner-lite/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
- package/templates/owner-lite/tier5-src/himi-owner-lite-events/index.ts +2 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-home/index.ts +2 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-order/index.ts +3 -3
- package/templates/owner-lite/tier5-src/himi-owner-lite-orders/index.ts +2 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-overview/index.ts +2 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-post/index.ts +60 -43
- package/templates/owner-lite/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
- package/templates/owner-lite/tier5-src/himi-owner-lite-posts/index.ts +2 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-product/index.ts +31 -32
- package/templates/owner-lite/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
- package/templates/owner-lite/tier5-src/himi-owner-lite-products/index.ts +2 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-service/index.ts +18 -20
- package/templates/owner-lite/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
- package/templates/owner-lite/tier5-src/himi-owner-lite-services/index.ts +2 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-sites/index.ts +2 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-today/index.ts +315 -0
- package/templates/owner-lite/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
- package/templates/podcast-creator/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/podcast-creator-owner/dev/index.ts +317 -100
- package/templates/podcast-creator-owner/himalaya.content.json +6 -0
- package/templates/podcast-creator-owner/tier5-src/_shared/owner-account.ts +6 -2
- package/templates/podcast-creator-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
- package/templates/podcast-creator-owner/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
- package/templates/portfolio-creative/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-cart/index.ts +52 -28
- package/templates/portfolio-creative-owner/dev/index.ts +545 -100
- package/templates/portfolio-creative-owner/himalaya.content.json +6 -0
- package/templates/portfolio-creative-owner/tier5-src/_shared/owner-account.ts +6 -2
- package/templates/portfolio-creative-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
- package/templates/portfolio-creative-owner/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
- package/templates/restaurant/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/restaurant-owner/dev/index.ts +545 -100
- package/templates/restaurant-owner/himalaya.content.json +6 -0
- package/templates/restaurant-owner/tier5-src/_shared/owner-account.ts +6 -2
- package/templates/restaurant-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
- package/templates/restaurant-owner/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
- package/templates/retail-store/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/retail-store/tier5-src/himi-branded-lite-cart/index.ts +52 -28
- package/templates/retail-store-owner/dev/index.ts +317 -100
- package/templates/retail-store-owner/himalaya.content.json +6 -0
- package/templates/retail-store-owner/tier5-src/_shared/owner-account.ts +6 -2
- package/templates/retail-store-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
- package/templates/retail-store-owner/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
- package/templates/yoga-studio/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/yoga-studio-owner/dev/index.ts +503 -100
- package/templates/yoga-studio-owner/himalaya.content.json +6 -0
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-account.ts +6 -2
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-load-error.ts +115 -12
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
// Tier-5 bundle: __HIMI_APP_NAME__ — TODAY. The worker behind the Home Screen widget.
|
|
2
|
+
//
|
|
3
|
+
// A WidgetKit extension (and a Glance receiver) runs no JS and holds no Wix session, and the OTA
|
|
4
|
+
// widget producer has no credential either, so neither can read an owner's business. The PHONE
|
|
5
|
+
// app runs this worker headlessly with its owner Wix bridge, resolves the widget descriptors'
|
|
6
|
+
// `$service:ownerToday.*` leaves to literals (CompanionDescriptorResolver, the same pattern the
|
|
7
|
+
// watch companion uses), and writes the frozen tree to the App Group for the widget to render.
|
|
8
|
+
//
|
|
9
|
+
// One owner glance: "what's next, and what is waiting for me" — the next booking, the booking
|
|
10
|
+
// requests waiting on the owner, and the orders that need attention. Read-only: a widget never
|
|
11
|
+
// writes to the business.
|
|
12
|
+
//
|
|
13
|
+
// Each source degrades on its own. A site without Bookings answers 428/404 on the bookings
|
|
14
|
+
// query, and that must not cost the order count (or the reverse).
|
|
15
|
+
|
|
16
|
+
import { defineService } from "@himalaya/state";
|
|
17
|
+
import { queryBookings } from "@himalaya/wix-bookings";
|
|
18
|
+
import { searchOrders } from "@himalaya/wix-ecom";
|
|
19
|
+
import { ownerOrderAttentionFilter } from "../_shared/owner-order-attention.js";
|
|
20
|
+
import { readSelectedSite } from "../_shared/owner-selected-site.js";
|
|
21
|
+
|
|
22
|
+
type Rec = Record<string, unknown>;
|
|
23
|
+
|
|
24
|
+
/** One booking as the widget shows it. Client FIRST name only: a Home Screen is visible to
|
|
25
|
+
* anyone near the phone. */
|
|
26
|
+
export interface TodayBooking {
|
|
27
|
+
bookingId: string;
|
|
28
|
+
name: string;
|
|
29
|
+
service: string;
|
|
30
|
+
timeLabel: string; // "10:30"
|
|
31
|
+
dayLabel: string; // "Today" / "Tomorrow" / "Thu 16 Oct"
|
|
32
|
+
/** "Today 10:30" — the absolute moment, never a countdown: a frozen "in 21 min" is wrong a
|
|
33
|
+
* minute later, and nothing re-renders the widget between app foregrounds. */
|
|
34
|
+
whenLabel: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface TodayState {
|
|
38
|
+
status: "loading" | "ready" | "error" | "signedOut";
|
|
39
|
+
siteName: string;
|
|
40
|
+
/** Header line: "Today · <site>", or "Today" when the site has no name yet. */
|
|
41
|
+
header: string;
|
|
42
|
+
next: TodayBooking;
|
|
43
|
+
hasNext: boolean;
|
|
44
|
+
/** Hide the next-booking block: nothing is booked, or Bookings cannot be read. */
|
|
45
|
+
hideNext: boolean;
|
|
46
|
+
/** "No upcoming bookings" — shown in place of the block when Bookings has nothing ahead. */
|
|
47
|
+
nextNote: string;
|
|
48
|
+
hideNextNote: boolean;
|
|
49
|
+
requestCount: number;
|
|
50
|
+
/** "2 requests" / "1 request" / "No requests". At the read cap: "50+ requests". */
|
|
51
|
+
requestLabel: string;
|
|
52
|
+
orderCount: number;
|
|
53
|
+
/** "3 orders to handle" / "1 order to handle" / "No orders waiting". */
|
|
54
|
+
orderLabel: string;
|
|
55
|
+
/** Compact counts for the small widget: "2 req · 3 orders". */
|
|
56
|
+
countsShort: string;
|
|
57
|
+
bookingsReady: boolean;
|
|
58
|
+
ordersReady: boolean;
|
|
59
|
+
hideRequests: boolean;
|
|
60
|
+
hideOrders: boolean;
|
|
61
|
+
/** One-line state copy for a degrade (signed out, read failed, nothing installed). */
|
|
62
|
+
message: string;
|
|
63
|
+
hideMessage: boolean;
|
|
64
|
+
/** Epoch SECONDS after which this frame's labels are wrong: the next local midnight ("Today" /
|
|
65
|
+
* "Tomorrow" shift) or the next booking's start (it is no longer "next"), whichever is first.
|
|
66
|
+
* The phone caps the snapshot's lifetime at it, so a frozen frame never outlives its words. */
|
|
67
|
+
validUntil: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Which verticals THIS build provisions — baked into the widget descriptors' `input`. A vertical
|
|
71
|
+
* the build does not show is never read: its count must not reach the compact line, and its
|
|
72
|
+
* transient failure must not withhold a frame about the sections the build does show. */
|
|
73
|
+
export interface Verticals {
|
|
74
|
+
bookings: boolean;
|
|
75
|
+
store: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const COPY = {
|
|
79
|
+
signedOut: "Sign in to Owner to see today",
|
|
80
|
+
error: "Open Owner to refresh",
|
|
81
|
+
nothing: "Open Owner to see your business",
|
|
82
|
+
noneUpcoming: "No upcoming bookings",
|
|
83
|
+
} as const;
|
|
84
|
+
|
|
85
|
+
const PENDING = ["PENDING", "PENDING_APPROVAL", "WAITING_LIST"];
|
|
86
|
+
/** A glance, not a list: a count at the cap reads as "50+". */
|
|
87
|
+
export const LIMIT = 50;
|
|
88
|
+
|
|
89
|
+
const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
90
|
+
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
91
|
+
|
|
92
|
+
function str(r: unknown, ...paths: string[]): string {
|
|
93
|
+
for (const path of paths) {
|
|
94
|
+
let v: unknown = r;
|
|
95
|
+
for (const k of path.split(".")) v = v && typeof v === "object" ? (v as Rec)[k] : undefined;
|
|
96
|
+
if (typeof v === "string" && v) return v;
|
|
97
|
+
if (typeof v === "number") return String(v);
|
|
98
|
+
}
|
|
99
|
+
return "";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function hhmm(d: Date): string {
|
|
103
|
+
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function sameLocalDay(a: Date, b: Date): boolean {
|
|
107
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The day a booking falls on, as an owner would say it. */
|
|
111
|
+
export function dayLabel(start: Date, now: Date): string {
|
|
112
|
+
if (sameLocalDay(start, now)) return "Today";
|
|
113
|
+
const tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
|
|
114
|
+
if (sameLocalDay(start, tomorrow)) return "Tomorrow";
|
|
115
|
+
return `${WEEKDAYS[start.getDay()]} ${start.getDate()} ${MONTHS[start.getMonth()]}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function startOf(b: Rec): string {
|
|
119
|
+
return str(b, "startDate", "bookedEntity.slot.startDate");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function booking(b: Rec, now: Date): TodayBooking {
|
|
123
|
+
const start = new Date(startOf(b));
|
|
124
|
+
const day = dayLabel(start, now);
|
|
125
|
+
return {
|
|
126
|
+
bookingId: str(b, "id"),
|
|
127
|
+
name: str(b, "contactDetails.firstName") || "Client",
|
|
128
|
+
service: str(b, "bookedEntity.title", "bookedEntity.slot.serviceName") || "Appointment",
|
|
129
|
+
timeLabel: hhmm(start),
|
|
130
|
+
dayLabel: day,
|
|
131
|
+
whenLabel: `${day} ${hhmm(start)}`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const EMPTY_BOOKING: TodayBooking = { bookingId: "", name: "", service: "", timeLabel: "", dayLabel: "", whenLabel: "" };
|
|
136
|
+
|
|
137
|
+
function counted(n: number, capped: boolean, one: string, many: string): string {
|
|
138
|
+
return `${capped ? `${LIMIT}+` : n} ${n === 1 && !capped ? one : many}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface Sources {
|
|
142
|
+
/** Upcoming CONFIRMED bookings, or null when Bookings could not be read. */
|
|
143
|
+
bookings: Rec[] | null;
|
|
144
|
+
/** Upcoming booking REQUESTS (pending statuses), read on their own: one page of every status
|
|
145
|
+
* would fill with a busy week's confirmed bookings and hide next week's request. */
|
|
146
|
+
requests: Rec[] | null;
|
|
147
|
+
/** Orders needing attention, or null when Store could not be read. */
|
|
148
|
+
orders: Rec[] | null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function upcoming(rows: Rec[] | null, statuses: string[], now: Date): Rec[] {
|
|
152
|
+
return (rows ?? [])
|
|
153
|
+
.filter((b) => str(b, "id") && statuses.includes(str(b, "status").toUpperCase()))
|
|
154
|
+
.filter((b) => Date.parse(startOf(b)) >= now.getTime())
|
|
155
|
+
.sort((a, b) => Date.parse(startOf(a)) - Date.parse(startOf(b)) || str(a, "id").localeCompare(str(b, "id")));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Pure: shape what was read into the widget state. `now` is injectable for tests. */
|
|
159
|
+
export function shape(sources: Sources, now: Date, siteName: string): TodayState {
|
|
160
|
+
const bookingsReady = sources.bookings !== null && sources.requests !== null;
|
|
161
|
+
const ordersReady = sources.orders !== null;
|
|
162
|
+
// Re-filtered even though the queries filter: a server (or a fixture) that returns a
|
|
163
|
+
// cancelled or past row must not become an appointment the owner is told to expect.
|
|
164
|
+
const confirmed = bookingsReady ? upcoming(sources.bookings, ["CONFIRMED"], now) : [];
|
|
165
|
+
// Today's next CONFIRMED booking first: a request is not an appointment yet. With none left
|
|
166
|
+
// today, the first confirmed one after today, so "what's next" still has an answer.
|
|
167
|
+
const next = confirmed.find((b) => sameLocalDay(new Date(startOf(b)), now)) ?? confirmed[0];
|
|
168
|
+
const requestCount = bookingsReady ? upcoming(sources.requests, PENDING, now).length : 0;
|
|
169
|
+
const orderCount = (sources.orders ?? []).length;
|
|
170
|
+
const requestsCapped = (sources.requests ?? []).length >= LIMIT;
|
|
171
|
+
const ordersCapped = orderCount >= LIMIT;
|
|
172
|
+
|
|
173
|
+
const shortParts = [
|
|
174
|
+
...(bookingsReady ? [`${requestsCapped ? `${LIMIT}+` : requestCount} req`] : []),
|
|
175
|
+
...(ordersReady ? [counted(orderCount, ordersCapped, "order", "orders")] : []),
|
|
176
|
+
];
|
|
177
|
+
const nothing = !bookingsReady && !ordersReady;
|
|
178
|
+
const nextNote = bookingsReady && !next ? COPY.noneUpcoming : "";
|
|
179
|
+
const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).getTime();
|
|
180
|
+
const nextStart = next ? Date.parse(startOf(next)) : Infinity;
|
|
181
|
+
return {
|
|
182
|
+
validUntil: Math.floor(Math.min(midnight, nextStart) / 1000),
|
|
183
|
+
status: "ready",
|
|
184
|
+
siteName,
|
|
185
|
+
header: siteName ? `Today · ${siteName}` : "Today",
|
|
186
|
+
next: next ? booking(next, now) : EMPTY_BOOKING,
|
|
187
|
+
hasNext: !!next,
|
|
188
|
+
hideNext: !next,
|
|
189
|
+
nextNote,
|
|
190
|
+
hideNextNote: !nextNote,
|
|
191
|
+
requestCount,
|
|
192
|
+
requestLabel: requestCount ? counted(requestCount, requestsCapped, "request", "requests") : "No requests",
|
|
193
|
+
orderCount,
|
|
194
|
+
orderLabel: orderCount ? `${counted(orderCount, ordersCapped, "order", "orders")} to handle` : "No orders waiting",
|
|
195
|
+
countsShort: shortParts.join(" · "),
|
|
196
|
+
bookingsReady,
|
|
197
|
+
ordersReady,
|
|
198
|
+
hideRequests: !bookingsReady,
|
|
199
|
+
hideOrders: !ordersReady,
|
|
200
|
+
message: nothing ? COPY.nothing : "",
|
|
201
|
+
hideMessage: !nothing,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** A degrade frame: ONE line, no rows. After a sign-out or a business switch the last rows
|
|
206
|
+
* belong to someone else's clients, so none of them survive into it. */
|
|
207
|
+
export function degraded(status: "error" | "signedOut" | "loading", siteName: string): TodayState {
|
|
208
|
+
// A FIXED instant, not the clock: a degrade frame has no rows, so nothing in it depends on
|
|
209
|
+
// the time — and the initial state must be deterministic (template contract: no worker's
|
|
210
|
+
// initial state may read the clock).
|
|
211
|
+
const base = shape({ bookings: null, requests: null, orders: null }, new Date(0), siteName);
|
|
212
|
+
const message = status === "signedOut" ? COPY.signedOut : status === "error" ? COPY.error : "";
|
|
213
|
+
// validUntil pinned too: shape() derives it from LOCAL midnight, so even at a fixed instant it
|
|
214
|
+
// would vary with the build machine's timezone and change the baked services hash.
|
|
215
|
+
return { ...base, status, message, hideMessage: !message, validUntil: 0 };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** No usable owner session behind the bridge (the bridge refusing, or a 401). A transient
|
|
219
|
+
* 5xx is an ordinary error, not a sign-out — and a 403 is a missing PERMISSION for one
|
|
220
|
+
* vertical (a collaborator whose role lacks Bookings), not a sign-out either. */
|
|
221
|
+
export function isSignedOut(e: unknown): boolean {
|
|
222
|
+
const text = String((e as Error)?.message ?? e);
|
|
223
|
+
return /^HTTP 401\b/.test(text) || /wixApi not enabled|session unavailable|not signed in|webwix_bridge/i.test(text);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** This section cannot exist for this owner: the vertical is not installed on the site (its
|
|
227
|
+
* API answers 428, or 404 for the route), or the owner's role may not read it (403). Hides
|
|
228
|
+
* that one section; every other failure is an error for the whole frame.
|
|
229
|
+
*
|
|
230
|
+
* Read from the LEADING status only — the bridges format a failure as `HTTP <status>: <body>`
|
|
231
|
+
* or `HTTP <status> [<code>]: <message>` — never from anywhere in the message: a transient
|
|
232
|
+
* `HTTP 503: {"message":"upstream returned 404"}` must stay an error, not hide the section. */
|
|
233
|
+
export function isSectionUnavailable(e: unknown): boolean {
|
|
234
|
+
const text = String((e as Error)?.message ?? e);
|
|
235
|
+
return /^HTTP (?:403|404|428)\b/.test(text) || /^HTTP \d{3} \[(?:FAILED_PRECONDITION|PERMISSION_DENIED)\]/.test(text);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function siteName(): string {
|
|
239
|
+
return readSelectedSite()?.name ?? "";
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
type Read = { ok: true; value: unknown } | { ok: false; error: unknown };
|
|
243
|
+
|
|
244
|
+
async function attempt(fn: () => Promise<unknown>): Promise<Read> {
|
|
245
|
+
try {
|
|
246
|
+
return { ok: true, value: await fn() };
|
|
247
|
+
} catch (error) {
|
|
248
|
+
return { ok: false, error };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function rows(r: Read, key: string): Rec[] | null {
|
|
253
|
+
if (!r.ok) return null;
|
|
254
|
+
const list = (r.value as Rec | undefined)?.[key];
|
|
255
|
+
return Array.isArray(list) ? (list as Rec[]) : [];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** The build's verticals from the descriptor's baked `input.verticals` ("bookings,store"). A
|
|
259
|
+
* host that passes none (an older descriptor) reads both, as before. */
|
|
260
|
+
export function buildVerticals(): Verticals {
|
|
261
|
+
const raw = (globalThis as { himalaya?: { input?: Record<string, unknown> } }).himalaya?.input?.verticals;
|
|
262
|
+
if (typeof raw !== "string") return { bookings: true, store: true };
|
|
263
|
+
const set = new Set(raw.split(",").map((v) => v.trim()));
|
|
264
|
+
return { bookings: set.has("bookings"), store: set.has("store") };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** A section this build does not provision: never read, and indistinguishable from "not
|
|
268
|
+
* installed" downstream (hidden, out of the compact line, out of the error rule). */
|
|
269
|
+
const NOT_PROVISIONED: Read = { ok: false, error: new Error("HTTP 404: not provisioned in this build") };
|
|
270
|
+
|
|
271
|
+
async function read(): Promise<TodayState> {
|
|
272
|
+
const nowIso = new Date().toISOString();
|
|
273
|
+
const verticals = buildVerticals();
|
|
274
|
+
const sort = [{ fieldName: "startDate", order: "ASC" }, { fieldName: "id", order: "ASC" }];
|
|
275
|
+
const bookingsBy = (statuses: string[]) => verticals.bookings ? attempt(() => queryBookings({ query: {
|
|
276
|
+
filter: { startDate: { $gte: nowIso }, status: { $in: statuses } },
|
|
277
|
+
sort,
|
|
278
|
+
paging: { limit: LIMIT, offset: 0 },
|
|
279
|
+
} })) : Promise.resolve(NOT_PROVISIONED);
|
|
280
|
+
const reads = await Promise.all([
|
|
281
|
+
bookingsBy(["CONFIRMED"]),
|
|
282
|
+
bookingsBy(PENDING),
|
|
283
|
+
verticals.store ? attempt(() => searchOrders({ search: {
|
|
284
|
+
filter: ownerOrderAttentionFilter(),
|
|
285
|
+
sort: [{ fieldName: "createdDate", order: "DESC" }, { fieldName: "id", order: "ASC" }],
|
|
286
|
+
cursorPaging: { limit: LIMIT },
|
|
287
|
+
} })) : Promise.resolve(NOT_PROVISIONED),
|
|
288
|
+
]);
|
|
289
|
+
const failures = reads.flatMap((r) => (r.ok ? [] : [r.error]));
|
|
290
|
+
if (failures.some(isSignedOut)) return degraded("signedOut", "");
|
|
291
|
+
// A section that cannot exist (not installed, no permission) is hidden. ANY other failure —
|
|
292
|
+
// a transient 5xx on one read included — makes this an error frame, which the phone does
|
|
293
|
+
// not publish: the last good frame stays, rather than a frame that silently drops a
|
|
294
|
+
// section the owner does have and reads as "nothing waiting".
|
|
295
|
+
if (failures.some((e) => !isSectionUnavailable(e))) return degraded("error", siteName());
|
|
296
|
+
const [bookings, requests, orders] = reads;
|
|
297
|
+
return shape({
|
|
298
|
+
bookings: rows(bookings, "bookings"),
|
|
299
|
+
requests: rows(requests, "bookings"),
|
|
300
|
+
orders: rows(orders, "orders"),
|
|
301
|
+
}, new Date(), siteName());
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const svc = defineService<TodayState, {
|
|
305
|
+
load: (s: TodayState) => Promise<TodayState>;
|
|
306
|
+
refresh: (s: TodayState) => Promise<TodayState>;
|
|
307
|
+
}>("ownerToday", {
|
|
308
|
+
state: degraded("loading", ""),
|
|
309
|
+
actions: {
|
|
310
|
+
load: async () => read(),
|
|
311
|
+
refresh: async () => read(),
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
export { svc };
|
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
// render and tap must never confirm another business's booking.
|
|
14
14
|
|
|
15
15
|
import { defineService } from "@himalaya/state";
|
|
16
|
-
import {
|
|
16
|
+
import { getBooking, queryBookings } from "@himalaya/wix-bookings";
|
|
17
|
+
import {
|
|
18
|
+
PENDING_BOOKING_STATUSES as PENDING, isForActiveBusiness, isStaleBookingWrite as isStale, writeBookingResponse, type BookingResponse,
|
|
19
|
+
} from "../_shared/owner-booking-respond.js";
|
|
17
20
|
import { isBridgeUnavailable } from "../_shared/owner-load-error.js";
|
|
18
21
|
import { readSelectedSite, selectedSiteName } from "../_shared/owner-selected-site.js";
|
|
19
22
|
|
|
@@ -80,9 +83,9 @@ export const COPY = {
|
|
|
80
83
|
changed: "This request changed — check it on your iPhone",
|
|
81
84
|
error: "Open Owner on your iPhone",
|
|
82
85
|
writeFailed: "Couldn't update it — check it on your iPhone",
|
|
86
|
+
busy: "Still updating the last one — try again",
|
|
83
87
|
} as const;
|
|
84
88
|
|
|
85
|
-
const PENDING = new Set(["PENDING", "PENDING_APPROVAL", "WAITING_LIST"]);
|
|
86
89
|
const LIMIT = 50;
|
|
87
90
|
|
|
88
91
|
function str(r: Rec | undefined, ...paths: string[]): string {
|
|
@@ -257,25 +260,39 @@ async function fetchInto(s: WristState): Promise<WristState> {
|
|
|
257
260
|
return shape([...byId.values()], new Date(), site(), s.requestIndex);
|
|
258
261
|
}
|
|
259
262
|
|
|
260
|
-
function
|
|
261
|
-
return /revision|\b409\b|ABORTED|conflict/i.test(String((e as Error)?.message ?? e));
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
type Write = (bookingId: string, revision: string) => Promise<unknown>;
|
|
265
|
-
|
|
266
|
-
async function respond(s: WristState, params: unknown, write: Write): Promise<WristState> {
|
|
263
|
+
async function respond(s: WristState, params: unknown, kind: BookingResponse): Promise<WristState> {
|
|
267
264
|
if (s.inFlight) return s;
|
|
268
265
|
const p = (params && typeof params === "object" ? params : {}) as Rec;
|
|
269
266
|
const bookingId = str(p, "bookingId");
|
|
270
267
|
const siteId = str(p, "siteId");
|
|
271
|
-
// A press rendered for another business
|
|
272
|
-
|
|
273
|
-
|
|
268
|
+
// A press rendered for another business — or naming none, or made while no business is
|
|
269
|
+
// active (two empty ids are not the same business) — or for a request no longer pending.
|
|
270
|
+
if (!isForActiveBusiness(siteId, site().id)) return { ...s, actionError: COPY.changed };
|
|
271
|
+
let target = s.requests.find((r) => r.bookingId === bookingId);
|
|
272
|
+
if (!target && bookingId) {
|
|
273
|
+
// Not in the queue this state was read with: a press relayed from a NOTIFICATION
|
|
274
|
+
// can name a request that arrived after the last read, or one past the queue's first page.
|
|
275
|
+
// The booking itself, read by id from the business the press names, decides — one that is
|
|
276
|
+
// not pending there is not written. Guarded like the write, so a second press during the
|
|
277
|
+
// read cannot start a second one.
|
|
278
|
+
inFlightNow = true;
|
|
279
|
+
inFlightBooking = bookingId;
|
|
280
|
+
try {
|
|
281
|
+
const booking = ((await getBooking(bookingId, { site: siteId })) as unknown as { booking?: Rec } | undefined)?.booking;
|
|
282
|
+
if (booking && str(booking, "revision") && PENDING.has(str(booking, "status"))
|
|
283
|
+
&& handled.get(bookingId) !== str(booking, "revision")) target = request({ ...booking, id: bookingId });
|
|
284
|
+
} catch (e) {
|
|
285
|
+
inFlightNow = false;
|
|
286
|
+
return isSignedOut(e) ? failure(s, e) : { ...s, actionError: COPY.changed };
|
|
287
|
+
}
|
|
288
|
+
inFlightNow = false;
|
|
289
|
+
}
|
|
274
290
|
if (!target) return { ...s, actionError: COPY.changed };
|
|
275
291
|
inFlightNow = true;
|
|
292
|
+
inFlightBooking = bookingId;
|
|
276
293
|
writeGeneration += 1;
|
|
277
294
|
try {
|
|
278
|
-
await
|
|
295
|
+
await writeBookingResponse(kind, target.bookingId, target.revision, siteId);
|
|
279
296
|
handled.set(target.bookingId, target.revision);
|
|
280
297
|
} catch (e) {
|
|
281
298
|
inFlightNow = false;
|
|
@@ -319,6 +336,16 @@ async function read(s: WristState): Promise<WristState> {
|
|
|
319
336
|
// The state a concurrent action sees is the one it was dispatched with, so a double tap
|
|
320
337
|
// would read `inFlight: false` twice; this module flag is the guard that actually holds.
|
|
321
338
|
let inFlightNow = false;
|
|
339
|
+
/** The request that write is for. */
|
|
340
|
+
let inFlightBooking = "";
|
|
341
|
+
|
|
342
|
+
/** A press that lands while a write is running. The same request again (a double tap) is that
|
|
343
|
+
* write; ANOTHER request — a second notification's Accept relayed from the watch — was not
|
|
344
|
+
* written, and must say so rather than come back looking handled. */
|
|
345
|
+
function whileBusy(s: WristState, params: unknown): WristState {
|
|
346
|
+
const bookingId = str((params && typeof params === "object" ? params : {}) as Rec, "bookingId");
|
|
347
|
+
return bookingId && bookingId !== inFlightBooking ? { ...s, actionError: COPY.busy } : s;
|
|
348
|
+
}
|
|
322
349
|
|
|
323
350
|
const svc = defineService<WristState, {
|
|
324
351
|
load: (s: WristState) => Promise<WristState>;
|
|
@@ -333,8 +360,8 @@ const svc = defineService<WristState, {
|
|
|
333
360
|
actions: {
|
|
334
361
|
load: async (s) => read(s),
|
|
335
362
|
refresh: async (s) => read(s),
|
|
336
|
-
confirm: async (s, params) => (inFlightNow ? s : respond(s, params,
|
|
337
|
-
decline: async (s, params) => (inFlightNow ? s : respond(s, params,
|
|
363
|
+
confirm: async (s, params) => (inFlightNow ? whileBusy(s, params) : respond(s, params, "confirm")),
|
|
364
|
+
decline: async (s, params) => (inFlightNow ? whileBusy(s, params) : respond(s, params, "decline")),
|
|
338
365
|
askDecline: async (s) => (s.current.bookingId ? { ...s, confirmingDecline: true, notConfirmingDecline: false, actionError: "" } : s),
|
|
339
366
|
cancelDecline: async (s) => ({ ...s, confirmingDecline: false, notConfirmingDecline: true }),
|
|
340
367
|
nextRequest: async (s) => {
|
|
@@ -20,10 +20,9 @@ export function isOwnerPreviewLive(): boolean {
|
|
|
20
20
|
*
|
|
21
21
|
* `isBridgeUnavailable` cannot answer this: it reads "not live" as "preview",
|
|
22
22
|
* and on NATIVE there is no input at all, so `undefined` satisfies it. That is
|
|
23
|
-
*
|
|
24
|
-
* write
|
|
25
|
-
*
|
|
26
|
-
* their failures the same way (`"HTTP \(code): \(body)"`, JSWorker.swift;
|
|
23
|
+
* left alone for the other apps that use it to pick a demo fallback or to queue
|
|
24
|
+
* a write. Owner-lite reads ask `ownerDemoFallback` instead. It is NOT
|
|
25
|
+
* harmless for choosing COPY: native workers format their failures the same way (`"HTTP \(code): \(body)"`, JSWorker.swift;
|
|
27
26
|
* `"HTTP ${e.code}: ..."`, WebViewJSWorker.kt), so a transient `HTTP 503` on a
|
|
28
27
|
* phone would otherwise be answered with "open this app on a device" — on the
|
|
29
28
|
* device. Callers that pick a SENTENCE ask this instead.
|
|
@@ -43,11 +42,89 @@ export function isBridgeUnavailable(e: unknown): boolean {
|
|
|
43
42
|
return /HTTP (405|501|502|503)\b/.test(String((e as Error)?.message ?? e));
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
/**
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Whether a failed owner-lite READ may answer with the built-in demo records.
|
|
47
|
+
*
|
|
48
|
+
* Only where a demo was asked for: a web preview host that is not live. On a phone no
|
|
49
|
+
* mode is injected, so `isBridgeUnavailable` alone is `true` for a transient Wix
|
|
50
|
+
* `HTTP 503` — and the owner was shown sample appointments, orders and sites as if
|
|
51
|
+
* they were their business. Native falls through to the screen's error state.
|
|
52
|
+
* `isBridgeUnavailable` keeps its semantics: other apps' callers depend on it.
|
|
53
|
+
*/
|
|
54
|
+
export function ownerDemoFallback(e: unknown): boolean {
|
|
55
|
+
return isOwnerPreviewHost() && !isOwnerPreviewLive() && isBridgeUnavailable(e);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The sentence for an owner WRITE that failed because the device could not reach Wix at
|
|
60
|
+
* all (a transport failure). Nothing replays a queued write on any platform — every host's
|
|
61
|
+
* `drain` passes an empty closure — so the honest answer is that nothing was saved.
|
|
62
|
+
* Copy-as-configuration under `ownerCommon.`, which every owner screen forwards.
|
|
63
|
+
*/
|
|
64
|
+
export function ownerOfflineNotSaved(): string {
|
|
65
|
+
return copyParam(
|
|
66
|
+
"ownerCommon.offlineNotSaved",
|
|
67
|
+
"You’re offline — nothing was saved. Try again when you reconnect.",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The sentence for an owner WRITE that Wix answered with an unavailable status: the
|
|
72
|
+
* device is online, so it is not "offline" — but nothing was saved all the same. */
|
|
73
|
+
/**
|
|
74
|
+
* Whether a failed WRITE may have landed on Wix anyway, so the user must not be told it
|
|
75
|
+
* was not saved: a transport failure mid-request (timeout, reset, fetch rejection), the web
|
|
76
|
+
* host's after-send envelope (`HTTP 502 {…"sent":true}`), the serve bridge's mid-request
|
|
77
|
+
* `webwix_bridge_upstream_failed`, or a gateway status (502/503/504) that can arrive after
|
|
78
|
+
* the backend already ran. Only connect-time failures (`isOfflineFailure`) and the designed
|
|
79
|
+
* 405/501 bridge refusals prove nothing happened.
|
|
80
|
+
*/
|
|
81
|
+
export function writeMayHaveLanded(e: unknown): boolean {
|
|
82
|
+
if (isOfflineFailure(e)) return false;
|
|
83
|
+
if (isTransportFailure(e)) return true;
|
|
84
|
+
const msg = String((e as Error)?.message ?? e ?? "");
|
|
85
|
+
if (/"sent"\s*:\s*true/.test(msg)) return true;
|
|
86
|
+
// The web host's own envelope WITHOUT `sent: true` failed before the request left it, and
|
|
87
|
+
// a disabled owner rung / bridge refuses by design without sending anything.
|
|
88
|
+
if (/\bwebwix_(?:direct_failed|owner_disabled|bridge_disabled)\b/.test(msg)) return false;
|
|
89
|
+
// `webwix_bridge_upstream_failed` stays ambiguous: the same code covers a lookup that failed
|
|
90
|
+
// before sending and a serve-bridge failure mid-request, and nothing in it says which.
|
|
91
|
+
return /\bwebwix_bridge_upstream_failed\b/.test(msg) || /HTTP (502|503|504)\b/.test(msg);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Wix answered that the record does not exist (`HTTP 404`). A detail must not fall back to
|
|
95
|
+
* its cached row then: that row is what a delete whose reply was lost leaves behind. */
|
|
96
|
+
export function isNotFound(e: unknown): boolean {
|
|
97
|
+
if (/HTTP 404\b/.test(String((e as Error)?.message ?? e ?? ""))) return true;
|
|
98
|
+
// An SDK that answers a 404 with its own "not found" error keeps the HTTP one as `cause`
|
|
99
|
+
// (the blog SDK's getPost, after its query fallback also finds nothing).
|
|
100
|
+
const cause = (e as { cause?: unknown } | null | undefined)?.cause;
|
|
101
|
+
return cause !== undefined && cause !== e && /HTTP 404\b/.test(String((cause as Error)?.message ?? cause));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function ownerUnavailableNotSaved(): string {
|
|
105
|
+
return copyParam(
|
|
106
|
+
"ownerCommon.unavailableNotSaved",
|
|
107
|
+
"Wix isn’t available right now — nothing was saved. Try again in a moment.",
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The sentence for an owner WRITE that may have landed (`writeMayHaveLanded`): it can't
|
|
112
|
+
* be called saved or not saved. */
|
|
113
|
+
export function ownerWriteUnconfirmed(): string {
|
|
114
|
+
return copyParam(
|
|
115
|
+
"ownerCommon.writeUnconfirmed",
|
|
116
|
+
"Wix didn’t confirm this, so it may or may not have been saved. Check before trying again.",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Display copy for a failed owner write: `ownerOfflineNotSaved` when the request never
|
|
121
|
+
* left the device, `ownerWriteUnconfirmed` when it may have landed, `ownerUnavailableNotSaved`
|
|
122
|
+
* for the designed 405/501 bridge refusals outside a live preview, else `ownerLoadError`. */
|
|
123
|
+
export function ownerWriteError(e: unknown): string {
|
|
124
|
+
if (isOfflineFailure(e)) return ownerOfflineNotSaved();
|
|
125
|
+
if (writeMayHaveLanded(e)) return ownerWriteUnconfirmed();
|
|
126
|
+
if (!isOwnerPreviewLive() && /HTTP (405|501)\b/.test(String((e as Error)?.message ?? e))) return ownerUnavailableNotSaved();
|
|
127
|
+
return ownerLoadError(e);
|
|
51
128
|
}
|
|
52
129
|
|
|
53
130
|
/** `HTTP 403: …` → "403", when the message carries a status at all. */
|
|
@@ -99,6 +176,10 @@ const TRANSPORT = [
|
|
|
99
176
|
/^Failed to connect to \S/,
|
|
100
177
|
/^Unable to resolve host\b/,
|
|
101
178
|
/\b(?:Connection refused|Network is unreachable|Software caused connection abort)\b/,
|
|
179
|
+
// OkHttp / HttpURLConnection messages a dropped connection produces mid-request, which
|
|
180
|
+
// Android's bridge forwards without the exception class.
|
|
181
|
+
/\b(?:Connection reset|ECONNRESET|ETIMEDOUT|EPIPE|Broken pipe|stream was reset)\b/,
|
|
182
|
+
/^unexpected end of stream\b/i,
|
|
102
183
|
/^(?:timeout|Read timed out|connect timed out)$/i,
|
|
103
184
|
/\bFailed to fetch\b/,
|
|
104
185
|
/\bNetworkError when attempting to fetch\b/,
|
|
@@ -113,6 +194,27 @@ export function isTransportFailure(e: unknown): boolean {
|
|
|
113
194
|
return TRANSPORT.some((re) => re.test(msg));
|
|
114
195
|
}
|
|
115
196
|
|
|
197
|
+
/** Transport failures that happen while CONNECTING — no connection, no route, no such host,
|
|
198
|
+
* connection refused — so the request provably never left the device. Every other
|
|
199
|
+
* transport failure (a timeout, a reset, a bad or truncated response, a browser's fetch
|
|
200
|
+
* rejection, which says nothing about when it failed) may come after Wix took the write. */
|
|
201
|
+
const NEVER_SENT = [
|
|
202
|
+
/\bNSURLErrorDomain Code=-10(?:09|04|03|06|20|18)\b/, // not connected / cannot connect / no host / DNS / data not allowed / roaming off
|
|
203
|
+
/\b(?:UnknownHost|Connect|NoRouteToHost)Exception\b/,
|
|
204
|
+
/^Failed to connect to \S/,
|
|
205
|
+
/^Unable to resolve host\b/,
|
|
206
|
+
/\b(?:Connection refused|Network is unreachable)\b/,
|
|
207
|
+
/\bnet::ERR_(?:INTERNET_DISCONNECTED|NAME_NOT_RESOLVED|CONNECTION_REFUSED|ADDRESS_UNREACHABLE|NETWORK_ACCESS_DENIED)\b/,
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
/** No HTTP answer, and the request provably never left the device: the user is offline, and
|
|
211
|
+
* a write certainly did not happen. Anything else may have landed (see `NEVER_SENT`). */
|
|
212
|
+
export function isOfflineFailure(e: unknown): boolean {
|
|
213
|
+
if (!isTransportFailure(e)) return false;
|
|
214
|
+
const msg = String((e as Error)?.message ?? e ?? "");
|
|
215
|
+
return NEVER_SENT.some((re) => re.test(msg));
|
|
216
|
+
}
|
|
217
|
+
|
|
116
218
|
/** The one sentence for a transport failure. Copy-as-configuration: the key is
|
|
117
219
|
* under `ownerCommon.`, which every owner screen (Aria included) forwards. */
|
|
118
220
|
export function ownerTransportFailureText(): string {
|
|
@@ -194,8 +296,8 @@ export function ownerPreviewUnavailable(): string {
|
|
|
194
296
|
* error says: this names the web preview when there IS one. `isBridgeUnavailable`
|
|
195
297
|
* reads "not live" as "preview", and native injects no mode at all, so the
|
|
196
298
|
* default would answer a device-side `HTTP 503` with "open this app on a device"
|
|
197
|
-
* — on the device. That predicate is deliberately left alone:
|
|
198
|
-
* write-
|
|
299
|
+
* — on the device. That predicate is deliberately left alone: other apps'
|
|
300
|
+
* demo and write-queue decisions depend on exactly it.
|
|
199
301
|
*
|
|
200
302
|
* The reductions this used to add are now in `humanWireError`, so every owner
|
|
201
303
|
* screen gets them rather than the two that happened to call this.
|
|
@@ -232,6 +334,7 @@ export function ownerDisplayError(e: unknown): string {
|
|
|
232
334
|
*/
|
|
233
335
|
export function ownerLoadError(e: unknown): string {
|
|
234
336
|
const msg = String((e as Error)?.message ?? e);
|
|
235
|
-
|
|
337
|
+
// Same host guard as `ownerDisplayError`: on a phone, "open this app on a device" is false.
|
|
338
|
+
if (isOwnerPreviewHost() && isBridgeUnavailable(e)) return ownerPreviewUnavailable();
|
|
236
339
|
return humanWireError(msg);
|
|
237
340
|
}
|