File manager - Edit - /home/premiey/www/wp-includes/images/media/frontend.tar
Back
css/style.css 0000666 00000077656 15165306747 0007255 0 ustar 00 @font-face{font-family:"qlwapp-whatsapp";src:url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAYEAAsAAAAABbgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABCAAAAGAAAABgDxIFKmNtYXAAAAFoAAAAVAAAAFQXVtKHZ2FzcAAAAbwAAAAIAAAACAAAABBnbHlmAAABxAAAAfwAAAH8pb7IGGhlYWQAAAPAAAAANgAAADYUXm9HaGhlYQAAA/gAAAAkAAAAJAfAA8ZobXR4AAAEHAAAABQAAAAUCgAAA2xvY2EAAAQwAAAADAAAAAwAKAESbWF4cAAABDwAAAAgAAAAIAAJAJluYW1lAAAEXAAAAYYAAAGGmUoJ+3Bvc3QAAAXkAAAAIAAAACAAAwAAAAMDAAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA6QADwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADgAAAAKAAgAAgACAAEAIOkA//3//wAAAAAAIOkA//3//wAB/+MXBAADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAwAD/8AD/gPAACcATwCWAAABJicuAScmIyIHDgEHBhUUFhcDJR4BMzE4ATEyNz4BNzY1NCcuAScmATEiJi8BBzcnLgE1NDc+ATc2MzIXHgEXFhcWFx4BFxYVFAcOAQcGIxMuAScmIgcOAQcOAScuAScuAScmNjc+ATc+ATc2JicuAScuASMmIiMiBgcOARUUFhcWFx4BFxYXHgEXHgE3PgE3PgEnLgEnA2kkKSpbMTEzaVxdiSgoIiJIAQ03e0BpXF2KKCgKCiYbHP51OW0xD6ArCiAhIiFyTU1XKygpTCIjHh0XFyAICCEick1NV+cJRAkJDQcGHAYGDAkKOCQdJAYGBwUECgUEBQMDAQMCHQgHEAUGDAcGEwgJJC8FAhEQOCcnMBYkDhcoERI7CAgCAgMMCgMrJBwcJQoKKCiKXFxpQ4E6/vlHHx8oKIldXGkzMTFbKin9Fx4dCSmbEDJzPFdNTHMhIQgIHxcXHh4jIkwpKCtXTU1yISIBPAUhBAMKCSIHBgIFBRogGTYJCQwFBAwGBgkGBgwEBUcTEgMBBwkJMi8vTgYDFxY8IB8UCg0EBwEDAyMWFiIEBAcEAAAAAAEAAAABAAAmrdZpXw889QALBAAAAAAA2KCVZQAAAADYoJVlAAD/wAP+A8AAAAAIAAIAAAAAAAAAAQAAA8D/wAAABAAAAAAAA/4AAQAAAAAAAAAAAAAAAAAAAAUEAAAAAAAAAAAAAAACAAAABAAAAwAAAAAACgAUAB4A/gABAAAABQCXAAMAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEABwAAAAEAAAAAAAIABwBgAAEAAAAAAAMABwA2AAEAAAAAAAQABwB1AAEAAAAAAAUACwAVAAEAAAAAAAYABwBLAAEAAAAAAAoAGgCKAAMAAQQJAAEADgAHAAMAAQQJAAIADgBnAAMAAQQJAAMADgA9AAMAAQQJAAQADgB8AAMAAQQJAAUAFgAgAAMAAQQJAAYADgBSAAMAAQQJAAoANACkaWNvbW9vbgBpAGMAbwBtAG8AbwBuVmVyc2lvbiAxLjAAVgBlAHIAcwBpAG8AbgAgADEALgAwaWNvbW9vbgBpAGMAbwBtAG8AbwBuaWNvbW9vbgBpAGMAbwBtAG8AbwBuUmVndWxhcgBSAGUAZwB1AGwAYQByaWNvbW9vbgBpAGMAbwBtAG8AbwBuRm9udCBnZW5lcmF0ZWQgYnkgSWNvTW9vbi4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==) format("woff");font-weight:normal;font-style:normal}.qlwapp-whatsapp-icon{font-family:"qlwapp-whatsapp" !important;speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.qlwapp-whatsapp-icon:before{content:""}#qlwapp{pointer-events:none;box-sizing:border-box;font-size:var(--qlwapp-scheme-font-size);font-family:var(--qlwapp-scheme-font-family)}@media(min-width: 430px){#qlwapp{width:430px}}#qlwapp[data-visibility=readonly].qlwapp-timeout,#qlwapp [data-visibility=readonly].qlwapp-timeout{pointer-events:none;opacity:.5}#qlwapp[data-visibility=hidden].qlwapp-timeout,#qlwapp [data-visibility=hidden].qlwapp-timeout{display:none !important}#qlwapp *{box-sizing:border-box}#qlwapp.qlwapp-js-ready{display:block}#qlwapp.qlwapp-hide,#qlwapp .qlwapp-account.qlwapp-hide{display:none}#qlwapp.mobile .qlwapp-account.qlwapp-desktop,#qlwapp.mobile.qlwapp-desktop{display:none}#qlwapp.desktop .qlwapp-account.qlwapp-mobile,#qlwapp.desktop.qlwapp-mobile{display:none}#qlwapp .qlwapp-disabled,#qlwapp.qlwapp-disabled{display:none;visibility:hidden}#qlwapp.qlwapp-middle-left,#qlwapp.qlwapp-bottom-left,#qlwapp.qlwapp-middle-right,#qlwapp.qlwapp-bottom-right{position:fixed;z-index:9999999}#qlwapp.qlwapp-middle-left,#qlwapp.qlwapp-bottom-left{right:auto;left:0}#qlwapp.qlwapp-middle-left .qlwapp-toggle .qlwapp-days,#qlwapp.qlwapp-middle-left .qlwapp-toggle .qlwapp-time,#qlwapp.qlwapp-bottom-left .qlwapp-toggle .qlwapp-days,#qlwapp.qlwapp-bottom-left .qlwapp-toggle .qlwapp-time{left:0;right:auto}#qlwapp.qlwapp-middle-left.qlwapp-bubble .qlwapp-developer,#qlwapp.qlwapp-middle-left.qlwapp-bubble .qlwapp-toggle,#qlwapp.qlwapp-middle-left.qlwapp-button .qlwapp-developer,#qlwapp.qlwapp-middle-left.qlwapp-button .qlwapp-toggle,#qlwapp.qlwapp-bottom-left.qlwapp-bubble .qlwapp-developer,#qlwapp.qlwapp-bottom-left.qlwapp-bubble .qlwapp-toggle,#qlwapp.qlwapp-bottom-left.qlwapp-button .qlwapp-developer,#qlwapp.qlwapp-bottom-left.qlwapp-button .qlwapp-toggle{margin-left:20px}#qlwapp.qlwapp-middle-left.qlwapp-bubble .qlwapp-toggle .qlwapp-text,#qlwapp.qlwapp-bottom-left.qlwapp-bubble .qlwapp-toggle .qlwapp-text{right:auto;left:60px}#qlwapp.qlwapp-middle-left .qlwapp-box:before,#qlwapp.qlwapp-bottom-left .qlwapp-box:before{right:auto;left:20px}#qlwapp.qlwapp-middle-right,#qlwapp.qlwapp-bottom-right{left:auto;right:0}#qlwapp.qlwapp-middle-right .qlwapp-toggle .qlwapp-days,#qlwapp.qlwapp-middle-right .qlwapp-toggle .qlwapp-time,#qlwapp.qlwapp-bottom-right .qlwapp-toggle .qlwapp-days,#qlwapp.qlwapp-bottom-right .qlwapp-toggle .qlwapp-time{left:auto;right:0}#qlwapp.qlwapp-middle-right.qlwapp-bubble .qlwapp-developer,#qlwapp.qlwapp-middle-right.qlwapp-bubble .qlwapp-toggle,#qlwapp.qlwapp-middle-right.qlwapp-button .qlwapp-developer,#qlwapp.qlwapp-middle-right.qlwapp-button .qlwapp-toggle,#qlwapp.qlwapp-bottom-right.qlwapp-bubble .qlwapp-developer,#qlwapp.qlwapp-bottom-right.qlwapp-bubble .qlwapp-toggle,#qlwapp.qlwapp-bottom-right.qlwapp-button .qlwapp-developer,#qlwapp.qlwapp-bottom-right.qlwapp-button .qlwapp-toggle{margin-right:20px}#qlwapp.qlwapp-middle-left,#qlwapp.qlwapp-middle-right{top:40%;bottom:auto}#qlwapp.qlwapp-middle-left .qlwapp-developer,#qlwapp.qlwapp-middle-right .qlwapp-developer{padding:0 24px}#qlwapp.qlwapp-middle-left.qlwapp-button .qlwapp-developer,#qlwapp.qlwapp-middle-left.qlwapp-button .qlwapp-toggle{margin-left:-48px;padding-left:60px}#qlwapp.qlwapp-middle-right.qlwapp-button .qlwapp-developer,#qlwapp.qlwapp-middle-right.qlwapp-button .qlwapp-toggle{margin-right:-48px;padding-right:60px}#qlwapp.qlwapp-bottom-left,#qlwapp.qlwapp-bottom-right{top:auto;bottom:0}#qlwapp.qlwapp-rounded.qlwapp-bubble .qlwapp-toggle{border-radius:50%}#qlwapp.qlwapp-rounded.qlwapp-button .qlwapp-toggle{border-radius:50px}@media(min-width: 430px){#qlwapp.qlwapp-rounded .qlwapp-box{border-radius:8px}}#qlwapp.qlwapp-rounded .qlwapp-box .qlwapp-user,#qlwapp.qlwapp-rounded .qlwapp-box .qlwapp-message{border-radius:4px}#qlwapp p:last-child{margin:0}#qlwapp time{opacity:.8;font-style:italic;font-size:10px}#qlwapp .qlwapp-no-contacts{display:flex;height:134px;justify-content:center;align-items:center}#qlwapp .qlwapp-container{display:flex;flex-direction:column}#qlwapp .qlwapp-clearfix:before,#qlwapp .qlwapp-clearfix:after{content:"";display:block;clear:both}#qlwapp .qlwapp-previous,#qlwapp .qlwapp-close{font-family:"Source Serif Pro";display:block;opacity:.8;font-style:unset;font-size:24px;font-weight:bold;width:24px;height:24px;cursor:pointer;text-align:center;line-height:24px;z-index:1}#qlwapp .qlwapp-previous{width:16px;height:36px;cursor:pointer;text-align:center;line-height:36px;margin-right:auto}#qlwapp .qlwapp-developer{font-size:8px;top:-20px;position:relative}#qlwapp .qlwapp-developer,#qlwapp .qlwapp-toggle{display:inline-flex;align-items:center;justify-content:center;margin-left:auto;margin-right:auto}#qlwapp .qlwapp-toggle{pointer-events:all;cursor:pointer;box-shadow:0 1px 6px 0 rgba(0,0,0,.06),0 2px 32px 0 rgba(0,0,0,.16);margin-top:24px;margin-bottom:24px;position:relative;text-decoration:none}#qlwapp .qlwapp-toggle,#qlwapp .qlwapp-toggle:active,#qlwapp .qlwapp-toggle:focus,#qlwapp .qlwapp-toggle:hover{outline:none;text-decoration:none}#qlwapp .qlwapp-toggle .qlwapp-days,#qlwapp .qlwapp-toggle .qlwapp-time{position:absolute;bottom:-2em;color:initial;font-size:10px;display:inline-block;top:auto;width:120px}#qlwapp .qlwapp-days>span{display:none}#qlwapp .qlwapp-days>span.qlwapp-available-day{display:inline-block !important}#qlwapp .qlwapp-avatar .qlwapp-avatar-container{overflow:hidden;border-radius:50%;width:inherit;height:inherit;box-shadow:0 0 0 2px #eee;display:flex;align-items:center}#qlwapp .qlwapp-avatar .qlwapp-avatar-container img{width:100%;height:auto}#qlwapp.qlwapp-bubble .qlwapp-toggle{width:var(--qlwapp-scheme-icon-size, 60px);height:var(--qlwapp-scheme-icon-size, 60px)}#qlwapp.qlwapp-bubble .qlwapp-toggle .qlwapp-icon,#qlwapp.qlwapp-bubble .qlwapp-toggle .qlwapp-close{position:absolute;display:block;pointer-events:none;font-size:var(--qlwapp-scheme-icon-font-size, 24px)}#qlwapp.qlwapp-bubble .qlwapp-toggle .qlwapp-text{position:absolute;right:var(--qlwapp-scheme-icon-size, 60px);height:var(--qlwapp-scheme-icon-size, 60px);margin:0 24px;display:flex;align-items:center;justify-content:flex-end;font-weight:bold;white-space:pre}#qlwapp.qlwapp-button .qlwapp-toggle{height:40px;white-space:nowrap;padding:12px 24px}#qlwapp.qlwapp-button .qlwapp-toggle .qlwapp-icon{margin:0 12px 0 0}#qlwapp.qlwapp-button .qlwapp-toggle .qlwapp-close{display:none}#qlwapp .qlwapp-box{pointer-events:all;padding-bottom:56px;position:fixed;top:auto;bottom:0;left:0;right:0;z-index:99999;box-shadow:0px 5px 40px 5px rgba(0,0,0,.5)}@media(min-width: 430px){#qlwapp .qlwapp-box{position:relative;top:auto;bottom:auto;left:auto;right:auto;box-shadow:0 8px 25px -5px rgba(45,62,79,.15);margin-top:24px;margin-left:24px;margin-right:24px;margin-bottom:12px}#qlwapp .qlwapp-box:before{content:"";position:absolute;bottom:-6px;right:24px;width:13px;height:13px;transform:rotate(45deg)}#qlwapp .qlwapp-box .qlwapp-header{border-top-left-radius:inherit;border-top-right-radius:inherit}#qlwapp .qlwapp-box .qlwapp-footer,#qlwapp .qlwapp-box .qlwapp-response{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}}#qlwapp .qlwapp-box .qlwapp-header{position:relative;overflow:hidden}#qlwapp .qlwapp-box .qlwapp-header:before{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background:linear-gradient(315deg, transparent 0%, rgba(0, 0, 0, 0.05) 100%)}#qlwapp .qlwapp-box .qlwapp-header .qlwapp-close{position:absolute;top:12px;right:12px}@media(min-width: 430px){#qlwapp .qlwapp-box .qlwapp-header .qlwapp-close{position:absolute;top:7px;right:7px;font-size:18px}}#qlwapp .qlwapp-box .qlwapp-header .qlwapp-description{position:relative;display:flex;align-items:center}#qlwapp .qlwapp-box .qlwapp-description,#qlwapp .qlwapp-box .qlwapp-contact{padding:12px 36px;height:100%}#qlwapp .qlwapp-box .qlwapp-description p,#qlwapp .qlwapp-box .qlwapp-description h3{color:inherit;font-family:inherit;margin:0}#qlwapp .qlwapp-box .qlwapp-description h3{margin:0 0 .25em 0}#qlwapp .qlwapp-box .qlwapp-contact{display:flex;justify-content:flex-end;align-items:center}#qlwapp .qlwapp-box .qlwapp-contact .qlwapp-previous{position:relative;left:-16px}#qlwapp .qlwapp-box .qlwapp-contact .qlwapp-previous>i{line-height:36px}#qlwapp .qlwapp-box .qlwapp-contact .qlwapp-avatar{width:42px;height:42px}#qlwapp .qlwapp-box .qlwapp-contact .qlwapp-label,#qlwapp .qlwapp-box .qlwapp-contact .qlwapp-name{color:inherit}#qlwapp .qlwapp-box .qlwapp-account{color:inherit;padding:16px 36px;display:flex;align-items:center;overflow:hidden;position:relative;cursor:pointer;width:100%;text-decoration:none}#qlwapp .qlwapp-box .qlwapp-account:only-of-type{padding-top:36px;padding-bottom:36px}#qlwapp .qlwapp-box .qlwapp-info{padding:0 16px}#qlwapp .qlwapp-box .qlwapp-avatar{position:relative;width:62px;height:62px}#qlwapp .qlwapp-box .qlwapp-avatar:after{content:"";display:block;width:15px;height:15px;position:absolute;top:3px;right:-2px;background-image:url(data:image/png;base64,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);background-color:rgba(0,0,0,0);background-repeat:no-repeat;background-position:center center;background-size:cover;z-index:4}#qlwapp .qlwapp-box .qlwapp-days,#qlwapp .qlwapp-box .qlwapp-time,#qlwapp .qlwapp-box .qlwapp-label,#qlwapp .qlwapp-box .qlwapp-hidden,#qlwapp .qlwapp-box .qlwapp-offline-text{font-size:12px;line-height:1.5em;opacity:.6;display:block}#qlwapp .qlwapp-box .qlwapp-days,#qlwapp .qlwapp-box .qlwapp-time{font-size:11px;font-style:italic}#qlwapp .qlwapp-box .qlwapp-name{display:block;font-weight:bold;line-height:1.5em}#qlwapp .qlwapp-box .qlwapp-body{overflow-x:hidden;overflow-y:auto}#qlwapp .qlwapp-box .qlwapp-body .qlwapp-carousel{max-height:470px;min-height:134px}#qlwapp .qlwapp-box .qlwapp-chat{padding:36px 0;display:flex;flex-direction:column;overflow:hidden;justify-content:center;align-items:center;height:100%}#qlwapp .qlwapp-box .qlwapp-user,#qlwapp .qlwapp-box .qlwapp-message{padding:16px 24px;position:relative;word-wrap:break-word;width:calc(100% - 72px)}#qlwapp .qlwapp-box .qlwapp-user:before,#qlwapp .qlwapp-box .qlwapp-message:before{content:"";position:absolute;top:-6px;right:24px;width:13px;height:13px;transform:rotate(45deg)}#qlwapp .qlwapp-box .qlwapp-message{word-break:var(--qlwapp-scheme-box-message-word-break, break-all)}#qlwapp .qlwapp-box .qlwapp-user{width:auto;max-width:75%;align-self:flex-start;justify-self:flex-end;margin-top:0}#qlwapp .qlwapp-box .qlwapp-user:before{top:auto;bottom:-6px;left:24px}#qlwapp .qlwapp-box .qlwapp-padding{padding-bottom:56px}#qlwapp .qlwapp-box .qlwapp-footer,#qlwapp .qlwapp-box .qlwapp-response{position:absolute;bottom:0;left:0;right:0;height:auto;width:100%}#qlwapp .qlwapp-box .qlwapp-footer,#qlwapp .qlwapp-box .qlwapp-footer pre,#qlwapp .qlwapp-box .qlwapp-footer textarea,#qlwapp .qlwapp-box .qlwapp-response,#qlwapp .qlwapp-box .qlwapp-response pre,#qlwapp .qlwapp-box .qlwapp-response textarea{max-height:160px}#qlwapp .qlwapp-box .qlwapp-footer pre,#qlwapp .qlwapp-box .qlwapp-footer textarea,#qlwapp .qlwapp-box .qlwapp-response pre,#qlwapp .qlwapp-box .qlwapp-response textarea{color:inherit;background-color:inherit}#qlwapp .qlwapp-box .qlwapp-footer:before,#qlwapp .qlwapp-box .qlwapp-response:before{pointer-events:none;position:absolute;content:"";height:59px;top:0;background:linear-gradient(315deg, transparent 0%, rgba(0, 0, 0, 0.025) 100%);left:0;right:0}#qlwapp .qlwapp-box .qlwapp-footer{min-height:56px;display:flex;align-items:center;text-align:center;font-size:12px;padding:12px 36px}#qlwapp .qlwapp-box .qlwapp-footer p{padding:0;font-size:inherit}#qlwapp .qlwapp-box .qlwapp-footer p:last-child{margin-bottom:0}#qlwapp .qlwapp-box .qlwapp-response,#qlwapp .qlwapp-box .qlwapp-response pre,#qlwapp .qlwapp-box .qlwapp-response textarea,#qlwapp .qlwapp-box .qlwapp-response .qlwapp-buttons{min-height:56px}#qlwapp .qlwapp-box .qlwapp-response pre,#qlwapp .qlwapp-box .qlwapp-response textarea{width:100%;height:100%;resize:none;border:none;padding:18px 88px 18px 36px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:1em !important;font-weight:normal;line-height:20px;white-space:pre-wrap;word-wrap:break-word;border-radius:inherit;margin:0}#qlwapp .qlwapp-box .qlwapp-response pre{visibility:hidden}#qlwapp .qlwapp-box .qlwapp-response textarea{position:absolute;bottom:0;left:0}#qlwapp .qlwapp-box .qlwapp-response textarea:focus{outline:none}#qlwapp .qlwapp-box .qlwapp-response .qlwapp-buttons{position:absolute;top:0;right:21px;display:flex;align-items:center}#qlwapp .qlwapp-box .qlwapp-response .qlwapp-buttons.active>i{display:none}#qlwapp .qlwapp-box .qlwapp-response .qlwapp-buttons.active>i.qlwf-emoji{display:block}#qlwapp .qlwapp-box .qlwapp-response .qlwapp-buttons>i{opacity:.4}#qlwapp .qlwapp-box .qlwapp-response .qlwapp-buttons>i,#qlwapp .qlwapp-box .qlwapp-response .qlwapp-buttons>a{cursor:pointer;margin:0 0 0 10px;display:inherit;font-size:18px;line-height:24px;text-decoration:none}#qlwapp.qlwapp-bubble .qlwapp-toggle .qlwapp-text{color:#303030}#qlwapp .qlwapp-box,#qlwapp .qlwapp-box:before{background-color:#fff}#qlwapp .qlwapp-box .qlwapp-account{background-color:#fff}#qlwapp .qlwapp-box .qlwapp-account:not(:first-child){border-top:1px solid #f5f5f5}#qlwapp .qlwapp-box .qlwapp-avatar .qlwapp-avatar-container{background-color:#eee}#qlwapp .qlwapp-box .qlwapp-message,#qlwapp .qlwapp-box .qlwapp-message:before{background-color:#eee}#qlwapp .qlwapp-box .qlwapp-footer,#qlwapp .qlwapp-box .qlwapp-response{color:#303030;background-color:#fff;border-top:1px solid #e6e6e6}#qlwapp .qlwapp-toggle,#qlwapp .qlwapp-box .qlwapp-header,#qlwapp .qlwapp-box .qlwapp-user,#qlwapp .qlwapp-box .qlwapp-user:before{background-color:var(--qlwapp-scheme-brand, #25d366)}#qlwapp .qlwapp-previous,#qlwapp .qlwapp-close,#qlwapp .qlwapp-toggle,#qlwapp .qlwapp-box .qlwapp-header,#qlwapp .qlwapp-box .qlwapp-contact,#qlwapp .qlwapp-box .qlwapp-user{color:var(--qlwapp-scheme-text, #ffffff)}#qlwapp .qlwapp-info .qlwapp-label{color:var(--qlwapp-scheme-contact-role-color, inherit)}#qlwapp .qlwapp-info .qlwapp-name{color:var(--qlwapp-scheme-contact-name-color, inherit)}#qlwapp .qlwapp-info .qlwapp-time{color:var(--qlwapp-scheme-contact-availability-color, inherit)}#qlwapp{animation-duration:1.5s;animation-timing-function:ease-in;animation-name:var(--qlwapp-button-animation-name, "none");animation-delay:var(--qlwapp-button-animation-delay, 0);animation-iteration-count:1;transform-origin:center;display:none}@keyframes flash{from,50%,to{opacity:1}25%,75%{opacity:0}}@keyframes bounce{from,20%,53%,80%,to{animation-timing-function:cubic-bezier(0.215, 0.61, 0.355, 1);transform:translate3d(0, 0, 0)}40%,43%{animation-timing-function:cubic-bezier(0.755, 0.05, 0.855, 0.06);transform:translate3d(0, -30px, 0)}70%{animation-timing-function:cubic-bezier(0.755, 0.05, 0.855, 0.06);transform:translate3d(0, -15px, 0)}90%{transform:translate3d(0, -4px, 0)}}@keyframes pulse{from{transform:scale3d(1, 1, 1)}50%{transform:scale3d(1.05, 1.05, 1.05)}to{transform:scale3d(1, 1, 1)}}@keyframes shakeX{from,to{transform:translate3d(0, 0, 0)}10%,30%,50%,70%,90%{transform:translate3d(-10px, 0, 0)}20%,40%,60%,80%{transform:translate3d(10px, 0, 0)}}@keyframes shakeY{from,to{transform:translate3d(0, 0, 0)}10%,30%,50%,70%,90%{transform:translate3d(0, -10px, 0)}20%,40%,60%,80%{transform:translate3d(0, 10px, 0)}}#qlwapp.qlwapp-show .qlwapp-box{visibility:visible;opacity:1;transform:translateY(0) scale(1);display:block}#qlwapp.qlwapp-transition .qlwapp-box{visibility:visible;display:block}#qlwapp.qlwapp-bubble.qlwapp-show .qlwapp-toggle .qlwapp-icon{transform:scale(0, 0);opacity:0}#qlwapp.qlwapp-bubble.qlwapp-show .qlwapp-toggle .qlwapp-close{transform:scale(1, 1);opacity:1}#qlwapp.qlwapp-bubble .qlwapp-toggle .qlwapp-close{transform:scale(0, 0);opacity:0}#qlwapp.qlwapp-bubble .qlwapp-toggle .qlwapp-icon{transform:scale(1, 1);opacity:1}#qlwapp.qlwapp-bubble .qlwapp-toggle .qlwapp-icon,#qlwapp.qlwapp-bubble .qlwapp-toggle .qlwapp-close{transition:transform 300ms cubic-bezier(0.215, 0.61, 0.355, 1),opacity 300ms ease-in}#qlwapp .qlwapp-box{display:none;opacity:0;visibility:hidden;transform:translateY(50px);transition:transform 300ms cubic-bezier(0.215, 0.61, 0.355, 1),opacity 300ms ease-in}@media(min-width: 430px){#qlwapp .qlwapp-box{transform:translateY(50px) scale(0.9)}}#qlwapp .qlwapp-box .qlwapp-carousel{display:flex;width:200%;height:100%}#qlwapp .qlwapp-box .qlwapp-carousel .qlwapp-slide{width:50%;transition:max-height 500ms cubic-bezier(0.215, 0.61, 0.355, 1) 1000ms,transform 300ms cubic-bezier(0.215, 0.61, 0.355, 1) 500ms,opacity 500ms ease-in 500ms}#qlwapp .qlwapp-box .qlwapp-header .qlwapp-slide{max-height:300px}#qlwapp .qlwapp-box .qlwapp-header .qlwapp-name,#qlwapp .qlwapp-box .qlwapp-header .qlwapp-label,#qlwapp .qlwapp-box .qlwapp-header .qlwapp-avatar{opacity:0;transition:transform cubic-bezier(0.215, 0.61, 0.355, 1) .3s,opacity ease-in .3s}#qlwapp .qlwapp-box .qlwapp-header .qlwapp-name,#qlwapp .qlwapp-box .qlwapp-header .qlwapp-label{transform:translateX(56px)}#qlwapp .qlwapp-box .qlwapp-header .qlwapp-avatar{transform:scale(0.5)}#qlwapp .qlwapp-box .qlwapp-header .qlwapp-name{transition-delay:800ms}#qlwapp .qlwapp-box .qlwapp-header .qlwapp-label{transition-delay:850ms}#qlwapp .qlwapp-box .qlwapp-header .qlwapp-avatar{transition-delay:900ms}#qlwapp .qlwapp-box .qlwapp-footer,#qlwapp .qlwapp-box .qlwapp-response,#qlwapp .qlwapp-box .qlwapp-previous,#qlwapp .qlwapp-box .qlwapp-message,#qlwapp .qlwapp-box .qlwapp-user{transition:transform cubic-bezier(0.215, 0.61, 0.355, 1) 600ms,opacity ease-in 200ms}#qlwapp .qlwapp-box .qlwapp-response,#qlwapp .qlwapp-box .qlwapp-previous,#qlwapp .qlwapp-box .qlwapp-user{opacity:0}#qlwapp .qlwapp-box .qlwapp-response{pointer-events:none}#qlwapp .qlwapp-box .qlwapp-previous{transform:translateX(56px)}#qlwapp .qlwapp-box .qlwapp-user{transform:translateY(56px)}#qlwapp .qlwapp-box .qlwapp-close{transition:opacity 300ms cubic-bezier(0.215, 0.61, 0.355, 1)}#qlwapp .qlwapp-box .qlwapp-description{transition:transform 600ms cubic-bezier(0.215, 0.61, 0.355, 1),opacity 900ms cubic-bezier(0.215, 0.61, 0.355, 1)}#qlwapp .qlwapp-box .qlwapp-account{transition:transform 600ms cubic-bezier(0.215, 0.61, 0.355, 1),opacity 150ms ease-in}#qlwapp .qlwapp-box .qlwapp-previous{transition-delay:800ms}#qlwapp .qlwapp-box .qlwapp-message{transition-delay:900ms}#qlwapp .qlwapp-box .qlwapp-close,#qlwapp .qlwapp-box .qlwapp-description{transition-delay:900ms}#qlwapp .qlwapp-box .qlwapp-user{transition-delay:200ms}#qlwapp .qlwapp-box .qlwapp-account:nth-of-type(6){transition-delay:600ms}#qlwapp .qlwapp-box .qlwapp-account:nth-of-type(5){transition-delay:500ms}#qlwapp .qlwapp-box .qlwapp-account:nth-of-type(4){transition-delay:400ms}#qlwapp .qlwapp-box .qlwapp-account:nth-of-type(3){transition-delay:300ms}#qlwapp .qlwapp-box .qlwapp-account:nth-of-type(2){transition-delay:200ms}#qlwapp .qlwapp-box .qlwapp-account:nth-of-type(1){transition-delay:100ms}#qlwapp .qlwapp-box .qlwapp-slide .qlwapp-message{opacity:0;transform:translateY(-56px)}#qlwapp .qlwapp-box .qlwapp-slide .qlwapp-box .qlwapp-account:hover{background-color:#fafafa}#qlwapp .qlwapp-box.response .qlwapp-carousel .qlwapp-slide{transform:translateX(-100%)}#qlwapp .qlwapp-box.response .qlwapp-body .qlwapp-carousel{overflow:hidden;min-height:122px}#qlwapp .qlwapp-box.response .qlwapp-close{transition-delay:0ms}#qlwapp .qlwapp-box.response .qlwapp-close,#qlwapp .qlwapp-box.response .qlwapp-footer,#qlwapp .qlwapp-box.response .qlwapp-description,#qlwapp .qlwapp-box.response .qlwapp-account{opacity:0}#qlwapp .qlwapp-box.response .qlwapp-account{transform:translateY(-33.333%);border-top-color:rgba(0,0,0,0) !important}#qlwapp .qlwapp-box.response .qlwapp-user{display:block}#qlwapp .qlwapp-box.response .qlwapp-message,#qlwapp .qlwapp-box.response .qlwapp-previous,#qlwapp .qlwapp-box.response .qlwapp-response{opacity:1}#qlwapp .qlwapp-box.response .qlwapp-response{pointer-events:all}#qlwapp .qlwapp-box.response .qlwapp-message,#qlwapp .qlwapp-box.response .qlwapp-previous{transform:translate(0)}#qlwapp .qlwapp-box.response .qlwapp-header .qlwapp-slide{max-height:84px}#qlwapp .qlwapp-box.response .qlwapp-header .qlwapp-name,#qlwapp .qlwapp-box.response .qlwapp-header .qlwapp-avatar{opacity:1}#qlwapp .qlwapp-box.response .qlwapp-header .qlwapp-label{opacity:.5}#qlwapp .qlwapp-box.response .qlwapp-header .qlwapp-name,#qlwapp .qlwapp-box.response .qlwapp-header .qlwapp-label{transform:translateX(0)}#qlwapp .qlwapp-box.response .qlwapp-header .qlwapp-avatar{transform:scale(1)}#qlwapp .qlwapp-box.response.opening .qlwapp-footer,#qlwapp .qlwapp-box.response.opening .qlwapp-response,#qlwapp .qlwapp-box.response.opening .qlwapp-close,#qlwapp .qlwapp-box.response.opening .qlwapp-previous,#qlwapp .qlwapp-box.response.opening .qlwapp-description,#qlwapp .qlwapp-box.response.opening .qlwapp-header .qlwapp-name,#qlwapp .qlwapp-box.response.opening .qlwapp-header .qlwapp-label{visibility:visible}#qlwapp .qlwapp-box.texting .qlwapp-body{overflow-y:auto}#qlwapp .qlwapp-box.texting .qlwapp-user{opacity:1;transform:translate(0)}#qlwapp .qlwapp-box.closing .qlwapp-message{transition:all .4s;transform:translateY(100%);opacity:0}#qlwapp .qlwapp-box.closing .qlwapp-previous{transition-delay:0ms;transform:translateX(-24px);opacity:0;transition:transform cubic-bezier(0.215, 0.61, 0.355, 1) 300ms,opacity ease-out 200ms}#qlwapp .qlwapp-box.closing .qlwapp-description{opacity:0}body.rtl #qlwapp.qlwapp-button .qlwapp-toggle .qlwapp-icon{margin:0 0 0 12px}body.rtl #qlwapp .qlwapp-box .qlwapp-response .qlwapp-buttons{right:auto;left:21px;flex-direction:row-reverse}body.rtl #qlwapp .qlwapp-box .qlwapp-response .qlwapp-buttons>a{transform:rotate(180deg)}body.rtl #qlwapp .qlwapp-box .qlwapp-response pre,body.rtl #qlwapp .qlwapp-box .qlwapp-response textarea{padding:18px 36px 18px 88px}body.rtl #qlwapp .qlwapp-box.response .qlwapp-carousel .qlwapp-slide{transform:translateX(100%)}body.rtl #qlwapp .qlwapp-box .qlwapp-contact{flex-direction:row-reverse}.product #qlwapp{display:inline-block !important;width:auto !important}.product #qlwapp.qlwapp-button .qlwapp-toggle{margin:0} js/index.js 0000666 00000016341 15165306747 0006654 0 ustar 00 (()=>{"use strict";function e(e,t){return!!e.includes(parseInt(t))||!!e.includes(t.toString())}function t(e,t){return parseInt(e+t)}function n(e){let t=""+e.getMinutes();return 1===t.length&&(t="0"+t),e.getHours()+":"+t}function s(e){this.qlwapp=e,this.init(this)}s.prototype={open(e,t){let n="https://api.whatsapp.com/send";this.mobiledevice||(n="https://web.whatsapp.com/send");const s=t,a=s.dataset.message||"",o=s.dataset.phone||"",i=s.dataset.type||"phone",l=s.dataset.group||"";"group"==i?s.setAttribute("href",l):s.setAttribute("href",n+"?phone="+o+"&text="+encodeURIComponent(a));const p=new CustomEvent("qlwapp.click",{bubbles:!0,cancelable:!0});this.qlwapp.dispatchEvent(p)},toggle(e){e?.preventDefault();const t=new CustomEvent("qlwapp.toggle");this.qlwapp.dispatchEvent(t)},chat(e,t){e.preventDefault();const n=t.closest(".qlwapp-box"),s=n.querySelector(".qlwapp-reply"),a=n.querySelector(".qlwapp-header"),o=t.querySelector(".qlwapp-avatar img")?.getAttribute("src"),i=t.querySelector(".qlwapp-name")?.textContent,l=t.querySelector(".qlwapp-label")?.textContent,p=t.querySelector(".qlwapp-time")?.textContent,r=t.dataset.message,c=t.dataset.type,u=t.dataset.group,d=t.dataset.phone;n.classList.add("response","opening"),this.qlwapp.dispatchEvent(new CustomEvent("qlwapp.height")),setTimeout((function(){n.classList.remove("opening")}),300);const w=a.querySelector(".qlwapp-avatar img"),q=a.querySelector(".qlwapp-number"),m=a.querySelector(".qlwapp-name"),g=a.querySelector(".qlwapp-label"),f=n.querySelector(".qlwapp-message");w&&(w.setAttribute("src",o),w.setAttribute("alt",i)),q&&(q.innerHTML=u),m&&(m.innerHTML=i),g&&(g.innerHTML=p?p+" - "+l:l),f&&(f.innerHTML=r),s.dataset[c]="phone"==c?d:u,s.dataset.type=c},previous(e,t){e.preventDefault();const n=t.closest(".qlwapp-box");n.classList.add("closing"),setTimeout((function(){n.classList.remove("response","closing"),n.classList.remove("texting")}),300)},init(s){const a=new CustomEvent("qlwapp.init"),o=new CustomEvent("qlwapp.resize"),i=this.qlwapp;if(i.classList.add("qlwapp-js-ready"),i.classList.contains("auto-load")&&!function(){const e=document.cookie.match("(^|;) ?qlwapp-auto-load=([^;]*)(;|$)");return e?e[2]:null}()){const e=Number(i.dataset.autoloadelay);setTimeout((()=>{s.toggle()}),e),function(e,t,n){const s=new Date;s.setTime(s.getTime()+864e5*n),document.cookie=e+"="+t+";path=/;expires="+s.toGMTString()}("qlwapp-auto-load","auto open cookie",1)}i.addEventListener("qlwapp.init",(function(e){s.mobiledevice=function(){const e=window.matchMedia("(pointer:coarse)");return e&&e.matches}()})),i.addEventListener("qlwapp.time",(function(s){const a=s.target,o=JSON.parse(a.dataset.timedays)||[],i=parseInt(a.dataset.timezone)||0,l=new Date((new Date).getTime()+60*i*1e3).getUTCDay().toString(),p=a.querySelector(".qlwapp-days"),r=a.querySelector(".qlwapp-time");if(o&&o.length&&!o.includes(l)){a.classList.add("qlwapp-timeout"),p&&(p.style.display="block"),r&&(r.style.display="none");const t=function(t,n){for(let s=t;s<=6;s++)if(e(n,s))return s;for(let s=0;s<=t;s++)if(e(n,s))return s}(l,o),n=a.querySelector(`.day${t}`);return n&&n.classList.add("qlwapp-available-day"),!0}o&&o.length&&o.includes(l)&&(p&&(p.style.display="none"),r&&(r.style.display="block"));const c=a.dataset.timefrom||!1,u=a.dataset.timeto||!1;if(!u||!c||c===u)return!0;const d=new Date,w=-d.getTimezoneOffset()-i,q=new Date,m=new Date;let g,f;g=t(c[0],c[1]),f=t(c[3],c[4]),m.setHours(g),m.setMinutes(f+w),g=t(u[0],u[1]),f=t(u[3],u[4]),q.setHours(g),q.setMinutes(f+w);let y=m.getTime();const v=q.getTime();if(y>v&&(y-=864e5),d.getTime()>=y&&d.getTime()<=v||(a.classList.add("qlwapp-timeout"),p&&(p.style.display="none"),r&&(r.style.display="block")),!i)return!0;a.querySelector(".from").textContent=n(m),a.querySelector(".to").textContent=n(q)})),i.addEventListener("qlwapp.pro",(function(){const e=i.querySelector(".qlwapp-toggle"),t=i.querySelectorAll(".qlwapp-account"),n=new CustomEvent("qlwapp.time",{bubbles:!0});e.dispatchEvent(n),t.forEach((function(e){e.dispatchEvent(n)}))})),i.addEventListener("qlwapp.resize",(function(){this.classList.contains("qlwapp-show")&&s.toggle()})),i.addEventListener("qlwapp.init",(function(){s.mobiledevice?(i.classList.add("mobile"),i.classList.remove("desktop")):(i.classList.add("desktop"),i.classList.remove("mobile")),i.classList.add("qlwapp-js-ready")})),i.addEventListener("qlwapp.init",(function(){if(i.classList.contains("qlwapp-premium")){const e=new CustomEvent("qlwapp.pro");i.dispatchEvent(e)}})),i.addEventListener("qlwapp.height",(function(e){const t=e.currentTarget,n=t.querySelector(".qlwapp-body").querySelector(".qlwapp-carousel"),a=t?.querySelector(".qlwapp-header")?.offsetHeight||0,o=t?.querySelector(".qlwapp-footer")?.offsetHeight||0;if(!n)return;let i=window.innerHeight-a-o;s.mobiledevice||(i=.7*window.innerHeight-a-o),n.style.maxHeight=i+"px"})),i.addEventListener("qlwapp.toggle",(function(e){const t=e.currentTarget,n=t.querySelector(".qlwapp-box");t.classList.add("qlwapp-transition"),n.classList.remove("response","texting"),setTimeout((function(){t.classList.toggle("qlwapp-show");const e=new CustomEvent("qlwapp.height",{bubbles:!0});t.dispatchEvent(e)}),10),setTimeout((function(){t.classList.toggle("qlwapp-transition")}),300)})),i.addEventListener("click",(function(e){const t=e.target.closest("[data-action]");if(!t||!i.contains(t))return;const n=t.dataset?.action;switch(n){case"open":s.open(e,t);break;case"box":case"close":s.toggle(e,t);break;case"chat":s.chat(e,t);break;case"previous":s.previous(e,t)}})),i.querySelector("[data-action=response]")?.addEventListener("keypress",(function(e){e.target.matches("textarea")&&13==e.keyCode&&setTimeout((function(){!function(e){if("createEvent"in document){const t=e.ownerDocument,n=t.createEvent("MouseEvents");n.initMouseEvent("click",!0,!0,t.defaultView,1,0,0,0,0,!1,!1,!1,!1,0,null),e.dispatchEvent(n)}else e.click()}(i.querySelector(".qlwapp-reply"))}),100)})),i.querySelector("[data-action=response]")?.addEventListener("keyup",(function(e){if(e.target.matches("textarea")){e.preventDefault();const t=e.currentTarget,n=e.target,s=t.querySelector("pre"),a=t.querySelector(".qlwapp-reply"),o=i.querySelector(".qlwapp-box"),l=o.querySelector(".qlwapp-buttons");s.innerHTML=n.value,setTimeout((function(){o.classList.add("texting"),o.style.paddingBottom=s.offsetHeight+"px",l.classList.add("active");const e=n.value;a.dataset.message=e,""==e&&(o.classList.remove("texting"),l.classList.remove("active"))}),300)}})),i.dispatchEvent(a),window.addEventListener("click",(e=>{if(!e.target.closest("#qlwapp.qlwapp-show")){const e=document.querySelector("#qlwapp.qlwapp-show");e&&e.dispatchEvent(new CustomEvent("qlwapp.toggle"))}})),window.addEventListener("resize",(()=>{if("TEXTAREA"!==document.activeElement.tagName){const e=document.querySelector("#qlwapp");e&&(e.dispatchEvent(o),e.dispatchEvent(a))}}))}};const a=s;(()=>{window.qlwapp=(e,t)=>{if(void 0===t||"object"==typeof t)e.plugin_qlwapp||(e.plugin_qlwapp=new a(e,t));else if("string"==typeof t&&"_"!==t[0]&&"init"!==t){let n;const s=e.plugin_qlwapp;if(s instanceof a&&"function"==typeof s[t]){const e=Array.from(arguments).slice(1);n=s[t](...e)}return"destroy"===t&&(e.plugin_qlwapp=null),void 0!==n?n:e}};const e=()=>{document.querySelectorAll(".qlwapp").forEach((function(e){window.qlwapp(e)}))};e(),window.addEventListener("load",(()=>{e()}))})()})(); js/index.asset.php 0000666 00000000124 15165306747 0010135 0 ustar 00 <?php return array('dependencies' => array(), 'version' => '050b65b6ccb82df6ae3c'); events/EventsList.vue 0000666 00000124273 15165376672 0010730 0 ustar 00 <template> <div id="amelia-booking-wrap" class="am-wrap"> <!-- Spinner --> <div class="am-spinner am-section" v-show="!fetched"> <img class="svg-booking am-spin" :src="$root.getUrl + 'public/img/oval-spinner.svg'"> <img class="svg-booking am-hourglass" :src="$root.getUrl + 'public/img/hourglass.svg'"> </div> <div id="am-events-booking" v-show="fetched"> <!-- Event Filter --> <div class="am-events-filter"> <el-row :gutter="24"> <!-- Event Filter Tags --> <el-col v-show="showTags()" :sm="getColumnLength()[0]" :class="$root.settings.customization.forms ? `el-form-item am-select-${$options.name}`: ''" > <el-select v-model="params.tag" clearable :placeholder="eventFilterLabels.event_type.value || $root.labels.event_type" :popper-class="$root.settings.customization.forms ? `am-dropdown-${$options.name}` : ''" @change="getEvents(false)" > <template slot="prefix"> <svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M17.5 17.4999H2.5C1.84661 17.508 1.21666 17.2568 0.748098 16.8013C0.279533 16.3459 0.0105328 15.7233 0 15.0699V2.92994C0.0105328 2.27659 0.279533 1.65403 0.748098 1.19858C1.21666 0.743137 1.84661 0.491921 2.5 0.499942H7.1C7.24771 0.500919 7.39336 0.534605 7.5265 0.59858C7.65964 0.662555 7.77695 0.755229 7.87 0.869942L10.47 4.04994H17.47C17.796 4.04198 18.1204 4.09866 18.4244 4.21672C18.7285 4.33478 19.0061 4.51188 19.2413 4.7378C19.4765 4.96372 19.6647 5.23398 19.7949 5.53299C19.9251 5.83201 19.9948 6.15385 20 6.47994V15.0699C19.9895 15.7233 19.7205 16.3459 19.2519 16.8013C18.7833 17.2568 18.1534 17.508 17.5 17.4999Z"/> </svg> </template> <el-option v-for="(tag, index) in options.entities.tags.map(eventTag => eventTag.name)" :key="index" :label="tag" :value="tag" > </el-option> </el-select> </el-col> <!-- /Event Filter Tags --> <!-- Event Filter Date --> <el-col v-show="showDatePicker()" :sm="getColumnLength()[1]" :class="$root.settings.customization.forms ? `am-calendar-${$options.name}`: ''" class="v-calendar-column" > <v-date-picker v-model="params.date" id="am-calendar-picker" class="am-calendar-picker" tint-color='#1A84EE' mode="single" popover-visibility="focus" popover-direction="bottom" popover-align="center" :input-props="{class: 'el-input__inner', placeholder: this.$root.labels.event_pick_min_date, readonly: true}" :show-day-popover=false :is-expanded=false :is-inline=false :is-required=true :formats="vCalendarFormats" @input="getEvents(false)" > <el-input v-model="selectedDateInput" readonly > <template slot="prefix"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="22" viewBox="0 0 22 24"> <path fill-rule="evenodd" d="M15.714 2.667H6.286V0H2.75v2.667H.78A.783.783 0 0 0 0 3.45v19.764A.78.78 0 0 0 .783 24h20.434a.785.785 0 0 0 .783-.785V3.451a.782.782 0 0 0-.78-.784h-1.97V0h-3.536v2.667zM2.75 21.429V8h16.5v13.429H2.75zM12 14v6h6v-6h-6z"/> </svg> </template> </el-input> </v-date-picker> </el-col> <!-- /Event Filter Date --> <!-- Event Filter Location --> <el-col v-show="showLocations()" :sm="getColumnLength()[2]" :class="$root.settings.customization.forms ? `el-form-item am-select-${$options.name}`: ''" > <el-select v-model="params.locationId" clearable :placeholder="(eventFilterLabels.hasOwnProperty('event_location') ? eventFilterLabels.event_location.value : '') || $root.labels.event_location" :popper-class="$root.settings.customization.forms ? `am-dropdown-${$options.name}` : ''" @change="getEvents(false)" > <template slot="prefix"> <svg width="20" height="24" viewBox="0 0 20 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M10 0.16674C7.54075 0.166617 5.18082 1.13713 3.43314 2.86733C1.68546 4.59753 0.69128 6.9476 0.666687 9.40674C0.666687 15.8001 8.89169 22.9167 9.24169 23.2201C9.45301 23.4008 9.72194 23.5001 10 23.5001C10.2781 23.5001 10.547 23.4008 10.7584 23.2201C11.1667 22.9167 19.3334 15.8001 19.3334 9.40674C19.3088 6.9476 18.3146 4.59753 16.5669 2.86733C14.8192 1.13713 12.4593 0.166617 10 0.16674ZM10 20.7584C8.05169 18.9034 3.00002 13.7584 3.00002 9.40674C3.00002 7.55022 3.73752 5.76975 5.05027 4.45699C6.36303 3.14424 8.1435 2.40674 10 2.40674C11.8565 2.40674 13.637 3.14424 14.9498 4.45699C16.2625 5.76975 17 7.55022 17 9.40674C17 13.7234 11.9484 18.9034 10 20.7584Z"/> <path d="M10 4.83337C9.19241 4.83337 8.40294 5.07286 7.73144 5.52154C7.05994 5.97022 6.53656 6.60795 6.22751 7.35408C5.91845 8.10022 5.83759 8.92124 5.99514 9.71333C6.1527 10.5054 6.5416 11.233 7.11266 11.8041C7.68373 12.3751 8.41131 12.764 9.2034 12.9216C9.99548 13.0791 10.8165 12.9983 11.5626 12.6892C12.3088 12.3802 12.9465 11.8568 13.3952 11.1853C13.8439 10.5138 14.0833 9.72431 14.0833 8.91671C14.0833 7.83374 13.6531 6.79513 12.8874 6.02935C12.1216 5.26358 11.083 4.83337 10 4.83337ZM10 10.6667C9.6539 10.6667 9.31555 10.5641 9.02777 10.3718C8.73998 10.1795 8.51568 9.90617 8.38323 9.5864C8.25077 9.26663 8.21612 8.91477 8.28364 8.5753C8.35116 8.23583 8.51784 7.92401 8.76258 7.67927C9.00732 7.43453 9.31914 7.26786 9.65861 7.20033C9.99807 7.13281 10.3499 7.16746 10.6697 7.29992C10.9895 7.43237 11.2628 7.65667 11.4551 7.94446C11.6474 8.23225 11.75 8.57059 11.75 8.91671C11.75 9.38084 11.5656 9.82596 11.2375 10.1541C10.9093 10.4823 10.4641 10.6667 10 10.6667Z"/> </svg> </template> <el-option v-for="(location, index) in options.entities.locations" :key="index" :label="location.name" :value="location.id" > </el-option> </el-select> </el-col> <!-- /Event Filter Location --> </el-row> </div> <!-- /Event Filter --> <!-- Event List --> <div class="am-event-list" :style="{'opacity': !fetchedFiltered ? '0.3' : 1, 'pointer-events': fetchedFiltered ? 'all' : 'none'}"> <!-- Event --> <div v-for="(evt, index) in events" v-if="evt.show" :id="'am-event-' + evt.id" :key="index" class="am-event" :class="[{ 'am-active': evt.showEventDetails, 'inactive': events.filter(event => (event.showEventDetails || event.showEventBooking || event.showAddToCalendar) && event.id !== evt.id).length > 0, 'canceled': getEventAvailability(evt).class === 'canceled', 'closed': getEventAvailability(evt).class === 'closed' && !evt.showAddToCalendar, 'full': getEventAvailability(evt).class === 'full', 'upcoming': getEventAvailability(evt).class === 'upcoming' }, $root.settings.customization.forms ? `am-form-${$options.name}` : '']" :style="{'pointer-events': 'all'}" > <!-- Event Data --> <div class="am-event-data" @click="toggleEventDetails(evt)"> <!-- Event Dates --> <div v-if="isEventInSameDay(evt)" class="am-event-date"> <div class="am-event-date-month" :style="getBookableColor(evt, false)"> {{ getEventFrontedFormattedDate(evt.periods[0].periodStart).split(' ')[0] }} </div> <div class="am-event-date-day"> {{ getEventFrontedFormattedDate(evt.periods[0].periodStart).split(' ')[1] }} </div> </div> <div v-else class="am-event-dates"> <div> <div class="am-event-date-month" :style="getBookableColor(evt, false)"> {{ getEventFrontedFormattedDate(evt.periods[0].periodStart).split(' ')[0] }} </div> <div class="am-event-date-day"> {{ getEventFrontedFormattedDate(evt.periods[0].periodStart).split(' ')[1] }} </div> </div> <div> <div class="am-event-date-month" :style="getBookableColor(evt, false)"> {{ getEventFrontedFormattedDate(evt.periods[evt.periods.length - 1].periodEnd).split(' ')[0] }} </div> <div class="am-event-date-day"> {{ getEventFrontedFormattedDate(evt.periods[evt.periods.length - 1].periodEnd).split(' ')[1] }} </div> </div> </div> <!-- Event Info --> <div class="am-event-info"> <div class="am-event-title"> {{ evt.name }} <span class="am-event-booking-status" :class="getEventAvailability(evt).class" > {{ getEventAvailability(evt).label }} </span> </div> <div class="am-event-sub-info"> <div v-if="eventInfoLabels.event_capacity.visibility" class="am-event-sub-info-capacity"> <img :src="$root.getUrl + 'public/img/capacity.svg'"> {{ eventInfoLabels.event_capacity.value || $root.labels.event_capacity}} {{ evt.maxCapacity - evt.places }} / {{ evt.maxCapacity }} </div> <div v-if="getLocation(evt) && eventInfoLabels.location.visibility"> <img :src="$root.getUrl + 'public/img/pin.svg'"> {{ getLocation(evt) }} </div> <div v-if="eventInfoLabels.event_date.visibility"> <img :src="$root.getUrl + 'public/img/clock.svg'"> {{ getEventDatesAndTimes(evt.periods) }} </div> <div v-if="eventInfoLabels.time_zone.visibility"> {{ timeZoneString }} </div> </div> </div> <!-- Event Price --> <div v-if="(evt.price !== 0 && eventInfoLabels.event_price.visibility) || (evt.customTickets.length && evt.customPricing && eventInfoLabels.event_price.visibility)" class="am-event-price" :style="getBookableColor(evt, true)"> {{ (evt.customTickets.length && evt.customPricing) ? ($root.labels.from + ' ' + getFormattedPrice(getMinTicketPrice(evt), !$root.settings.payments.hideCurrencySymbolFrontend)): getFormattedPrice(evt.price, !$root.settings.payments.hideCurrencySymbolFrontend) }} </div> <div v-else-if="eventInfoLabels.event_price.visibility" class="am-event-price am-event-free" :style="getBookableColor(evt, false)"> {{eventInfoLabels.event_free.value || $root.labels.event_free}} </div> <!-- /Event Price --> </div> <!-- /Event Data --> <!-- Event Details --> <transition name="fade"> <div v-show="evt.showEventDetails"> <!-- Event Details --> <div v-if="(evt.gallery && evt.gallery.length) || (evt.description && evt.description.length)" class="am-event-details" > <div v-if="evt.gallery && evt.gallery.length" class="am-event-photos" > <div v-for="(photo, index) in evt.gallery" :key="photo.id" > <lightbox :thumbnail="photo.pictureThumbPath" :images="getImages(evt.gallery.map(image => image.pictureFullPath), index)" > <lightbox-default-loader slot="loader"></lightbox-default-loader> <!-- If you want to use built-in loader --> <!-- <div slot="loader"></div> --> <!-- If you want to use your own loader --> </lightbox> </div> <!-- <img v-for="photo in evt.photos" :src="photo.url">--> </div> <div v-if="evt.description && evt.description.length" class="am-event-about"> <div>{{ eventInfoLabels.event_about.value || $root.labels.event_about }}</div> <div v-html="evt.description" class="ql-description"></div> </div> </div> <!-- Event Book --> <div v-if="getEventAvailability(evt).class === 'open'" class="am-event-book-cta" :class="getEventAvailability(evt).class" > <div v-if="eventInfoLabels.event_book.visibility" class="am-event-book-cta__inner"> <span> {{eventInfoLabels.event_book.value || $root.labels.event_book}} </span> </div> <div :style="{flexWrap: 'wrap', width: '100%'}"> <el-form> <el-form-item v-if="evt.bringingAnyone && !evt.customPricing" :class="$root.settings.customization.forms ? `am-input-number-${$options.name}`: ''" > <template slot="label"> <span :style="{ fontWeight: 700, color: $root.settings.customization.useGlobalColors.eventListForm ? $root.settings.customization.globalColors.formTextColor : forms.eventListForm.globalSettings.formTextColor }" > {{ eventInfoLabels.event_book_persons.value || $root.labels.event_book_persons }} </span> </template> <el-input-number :value="appointment.bookings[0].persons" :min="1" :max="(evt.maxExtraPeople !== null && evt.places > evt.maxExtraPeople ? (evt.maxExtraPeople + 1) : evt.places)" type="number" @change="setPlaces" > </el-input-number> </el-form-item> <!-- Custom Tickets --> <div :style="{flexWrap: 'wrap'}" v-else-if="evt.customTickets.length && evt.customPricing" class="am-ticket-box" > <div class="am-ticket" v-for="(ticket, index) in evt.customTickets" :key="ticket.id" v-if="ticket.enabled" > <el-row class="justify-content-center"> <el-col :sm="12" :lg="12" class="am-ticket-info"> <span class="am-ticket-name" :style="{fontWeight: 500, color: forms.eventListForm.globalSettings.formTextColor}"> {{ ticket.name }} </span> <div class="am-event-price" :style="{fontWeight: 500}"> {{ getFormattedPrice(ticket.dateRangePrice ? ticket.dateRangePrice : ticket.price, !$root.settings.payments.hideCurrencySymbolFrontend) }} </div> <span v-if="!evt.maxCustomCapacity && eventSpotsVisibility"> {{ ticketSpotsNumber(evt, index, ticket) }} </span> </el-col> <el-col :sm="12" :lg="12" class="am-ticket-number-of-people"> <p> {{ eventInfoLabels.event_book_persons.value || $root.labels.event_book_persons }} </p> <el-input-number :style="{maxHeight: '32px', lineHeight: '32px', overflow: 'hidden'}" size="mini" v-model="evt.bookingToEventTickets[index].persons" :min="0" :max="getMaxTicketSelected(evt, ticket, evt.bookingToEventTickets)" type="number" @blur="() => { if (!evt.bookingToEventTickets[index].persons) { evt.bookingToEventTickets[index].persons = 0 } }" :disabled="selectedTicketsCount(evt.bookingToEventTickets) === 1 && evt.bookingToEventTickets[index].persons === 0 && !evt.bringingAnyone" > </el-input-number> </el-col> </el-row> </div> </div> <!-- /Custom Tickets --> </el-form> </div> </div> <div class="am-event-book-continue" v-if="getEventAvailability(evt).class === 'open'"> <el-button type="primary" :style="getBookableColor(evt, true)" @click="toggleEventBooking(evt)" :disabled="(!evt.customPricing && evt.places <= 0) || (evt.customPricing && evt.customTickets.length > 0 && selectedTicketsCount(evt.bookingToEventTickets) === 0)" > {{$root.labels.continue}} </el-button> </div> </div> </transition> <!-- /Event Details --> <!-- Confirm Booking --> <transition name="fade"> <div v-show="evt.showEventBooking" class="am-event-booking" > <confirm-booking v-if="evt.showEventBooking" :visible.sync="evt.showEventBooking" dialogClass="am-confirm-booking-events-list" v-bind="cacheData && (cacheData.status === 'canceled' || cacheData.status === 'failed') ? getCacheDataRequestProps() : { bookableType: 'event', containerId: getContainerId(), bookable: getBookableData(evt), marketing: marketing, appointment: appointment, event: evt, customFields: options.entities.customFields, phonePopulated: phonePopulated, useGlobalCustomization: useGlobalCustomization(), queryParams: getSearchParams() }" :form-type= "'eventListForm'" :forms-data="forms['eventListForm']" @confirmedBooking="confirmedBooking" @cancelBooking="evt.showEventBooking = false" > </confirm-booking> </div> </transition> <!-- /Confirm Booking --> <!-- Add To Calendar --> <transition name="fade"> <div v-show="evt.showAddToCalendar" class="am-event-booking" > <add-to-calendar v-if="evt.showAddToCalendar" :form-type="'eventListForm'" :forms-data="forms['eventListForm']" :addToCalendarData="evt.addToCalendarData" @closeDialogAddToCalendar="evt.showAddToCalendar = false" > </add-to-calendar> </div> </transition> <!-- /Add To Calendar --> </div> </div> <!-- /Event List --> <div class="am-event-pagination"> <el-pagination v-show="pagination.count > pagination.show && events.length > 0" :page-size="pagination.show" :total="pagination.count" layout="prev, pager, next" :current-page.sync=pagination.page > </el-pagination> </div> <div class="am-empty-state am-section" v-show="events.length === 0"> <img :src="$root.getUrl + 'public/img/emptystate.svg'"> <p>{{ $root.labels.no_results }}</p> </div> </div> <div class="am-lite-footer"> <a v-if="$root.isLite && $root.settings.general.backLink.enabled" rel="nofollow" class="am-lite-footer-link" :href="$root.settings.general.backLink.url" target="_blank" > {{ $root.settings.general.backLink.label }} </a> </div> </div> </template> <script> import moment from 'moment' import formsCustomizationMixin from '../../../js/common/mixins/formsCustomizationMixin' import imageMixin from '../../../js/common/mixins/imageMixin' import settingsMixin from '../../../js/common/mixins/settingsMixin' import dateMixin from '../../../js/common/mixins/dateMixin' import priceMixin from '../../../js/common/mixins/priceMixin' import ConfirmBooking from '../parts/ConfirmBooking.vue' import bookingMixin from '../../../js/frontend/mixins/bookingMixin' import cacheMixin from '../../../js/frontend/mixins/cacheMixin' import commonEventMixin from '../../../js/common/mixins/eventMixin' import helperMixin from '../../../js/backend/mixins/helperMixin' import AddToCalendar from '../parts/AddToCalendar.vue' import customFieldMixin from '../../../js/common/mixins/customFieldMixin' import translationMixin from '../../../js/common/mixins/translationMixin' import marketingMixin from '../../../js/frontend/mixins/marketingMixin' export default { name: 'eventListForm', mixins: [formsCustomizationMixin, cacheMixin, translationMixin, imageMixin, dateMixin, priceMixin, bookingMixin, commonEventMixin, helperMixin, customFieldMixin, settingsMixin], data () { return { marketing: { event: null, payment: null }, tags: [], pagination: { show: this.$root.settings.general.itemsPerPage, page: 1, count: 0 }, params: { tag: null, locationId: null, date: 'ameliaBooking' in window && 'pastEventsDays' in window['ameliaBooking'] ? moment().subtract(window['ameliaBooking']['pastEventsDays'], 'days').toDate() : new Date(), page: 1 }, options: { entities: { tags: [], locations: [] } }, fetched: false, fetchedFiltered: false, events: [], event: null, payment: null, appointment: { bookings: [{ customer: { email: '', externalId: null, firstName: '', id: null, lastName: '', phone: '' }, customFields: {}, customerId: 0, extras: [], persons: 1 }], payment: { amount: 0, gateway: '', data: {} } }, forms: {}, eventInfoLabels: {}, eventFilterLabels: {}, eventSpotLabel: '', eventSpotsLabel: '', eventNoSpotsLabel: '', eventSpotsVisibility: true, showSingleEventDetails: true, timeZoneString: this.$root.settings.general.showClientTimeZone ? Intl.DateTimeFormat().resolvedOptions().timeZone : this.$root.settings.wordpress.timezone } }, created () { this.forms = this.getTranslatedForms('eventListForm') this.eventInfoLabels = this.$root.settings.customization.forms ? this.forms.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.labels : this.defaultFormsData.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.labels this.showSingleEventDetails = (this.$root.settings.customization.forms && this.forms.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.hasOwnProperty('showSingleEvent')) ? this.forms.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.showSingleEvent : this.defaultFormsData.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.showSingleEvent this.eventFilterLabels = this.$root.settings.customization.forms ? this.forms.eventListForm.eventFilterForm.itemsStatic.eventFilterFormField.labels : this.defaultFormsData.eventListForm.eventFilterForm.itemsStatic.eventFilterFormField.labels this.eventSpotLabel = 'ev_spot' in this.eventInfoLabels ? this.eventInfoLabels.ev_spot.value || this.$root.labels.ev_spot : this.defaultFormsData.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.labels.ev_spot.value || this.$root.labels.ev_spot this.eventSpotsLabel = 'ev_spots' in this.eventInfoLabels ? this.eventInfoLabels.ev_spots.value || this.$root.labels.ev_spots : this.defaultFormsData.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.labels.ev_spots.value || this.$root.labels.ev_spots this.eventNoSpotsLabel = 'ev_no_spots' in this.eventInfoLabels ? this.eventInfoLabels.ev_no_spots.value || this.$root.labels.ev_no_spots : this.defaultFormsData.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.labels.ev_no_spots.value || this.$root.labels.ev_no_spots this.eventSpotsVisibility = this.$root.settings.customization.forms && 'parts' in this.forms.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField ? this.forms.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.parts.ev_spots_number.visibility : this.defaultFormsData.eventListForm.eventFilterForm.itemsStatic.eventFilterFormField.parts.ev_spots_number.visibility this.setCacheData(this.getContainerId(), true) if (this.cacheData && this.cacheData.request.queryParams) { this.pagination.page = this.cacheData.request.queryParams.page this.params.page = this.cacheData.request.queryParams.page this.params.date = new Date(this.cacheData.request.queryParams.dates[0]) if (this.cacheData.request.queryParams.tag) { this.params.tag = this.cacheData.request.queryParams.tag } if (this.cacheData.request.queryParams.locationId) { this.params.locationId = this.cacheData.request.queryParams.locationId } } }, mounted () { this.getEntities(this.processListEntities) if (!this.$root.shortcodeData.hasEventShortcode) { this.inlineBookingSVG() } // expand event if its selected on shortcode and recurring is set to off let expandEvent = this.$root.shortcodeData.booking.eventId && this.$root.shortcodeData.booking.eventRecurring === 0 this.getEvents(expandEvent) }, methods: { runCacheAction () { if (this.loadingCacheBookingData) { if (this.cacheData.status === 'canceled' || this.cacheData.status === 'failed') { let event = this.events.find(event => event.id === this.cacheData.request.bookable.id) event.showEventBooking = true setTimeout(() => { this.cacheData = null }, 500) } else if (this.cacheData.status === 'paid' || this.cacheData.status === null) { this.confirmedBooking(this.cacheData.response, true, this.cacheData.request) } this.loadingCacheBookingData = false } }, getContainerId () { return 'amelia-app-booking' + this.$root.shortcodeData.counter }, useGlobalCustomization () { return 'ameliaBooking' in window && 'useGlobalCustomization' in window.ameliaBooking && window.ameliaBooking.useGlobalCustomization === true }, getImages (gallery, index) { for (let i = 0; i < index; i++) { gallery.push(gallery.shift()) } return gallery }, setPlaces (value) { this.appointment.bookings[0].persons = value }, getBookableColor (bookable, colorBackground) { return colorBackground ? { 'color': '#ffffff', 'background-color': bookable.color, 'border-color': '#ffffff' } : { 'color': bookable.color, 'background-color': '', 'border-color': '' } }, getEventDatesAndTimes (periods) { let $this = this let resultPeriods = [] this.getImplodedPeriods(periods).forEach(function (period) { let periodStart = period.periodStart.split(' ') let periodEnd = period.periodEnd.split(' ') if (period.isConnected) { resultPeriods.push($this.getFrontedFormattedDateTime(periodStart) + ' - ' + $this.getFrontedFormattedDateTime(periodEnd)) } else { if (periodStart[0] === periodEnd[0]) { resultPeriods.push($this.getFrontedFormattedDate(periodStart[0]) + ' ' + $this.getFrontedFormattedTime(periodStart[1]) + ' - ' + $this.getFrontedFormattedTime(periodEnd[1])) } else { resultPeriods.push($this.getFrontedFormattedDate(periodStart[0]) + ' - ' + $this.getFrontedFormattedDate(periodEnd[0]) + ' ' + $this.getFrontedFormattedTime(periodStart[1]) + ' - ' + $this.getFrontedFormattedTime(periodEnd[1])) } } }) return resultPeriods.join(', ') }, showTags () { return this.options.entities.tags.length > 1 && this.showDatePicker() }, showDatePicker () { return this.getPreselectedEventId() === null || (this.getPreselectedEventId() !== null && this.getPreselectedEventRecurring()) }, showLocations () { return this.options.entities.locations.length > 1 && this.showDatePicker() }, getColumnLength () { if (this.showTags() && this.showLocations()) { return [12, 12, 24] } if (!this.showTags() && this.showLocations()) { return [0, 12, 12] } if (this.showTags() && !this.showLocations()) { return [12, 12, 0] } if (!this.showTags() && !this.showLocations()) { return [0, 24, 0] } return [12, 12, 24] }, getEntities () { this.getCurrentUser() this.options.entities.locations = [] this.options.entities.tags = [] this.options.entities.customFields = [] this.setBookingCustomFields() }, getEventAvailability (evt) { if (evt.status === 'approved' || evt.status === 'pending') { if (evt.full) { return { label: this.eventInfoLabels.full.value || this.$root.labels.full, class: 'full' } } if (evt.upcoming) { return { label: this.eventInfoLabels.upcoming.value || this.$root.labels.upcoming, class: 'upcoming' } } return !evt.bookable ? { label: this.eventInfoLabels.closed.value || this.$root.labels.closed, class: 'closed' } : { label: this.eventInfoLabels.open.value || this.$root.labels.open, class: 'open' } } else { return { label: this.eventInfoLabels.canceled.value || this.$root.labels.canceled, class: 'canceled' } } }, isEventInSameDay (evt) { let result = true if (evt.periods.length === 1) { result = evt.periods[0].periodStart.split(' ')[0] === evt.periods[0].periodEnd.split(' ')[0] } else { let periodStart = evt.periods[0].periodStart.split(' ')[0] let periodEnd = evt.periods[0].periodEnd.split(' ')[0] evt.periods.forEach(function (period) { if (period.periodStart.split(' ')[0] !== periodStart || period.periodEnd.split(' ')[0] !== periodEnd) { result = false } }) } return result }, confirmedBooking (responseData, skipNotify, requestData) { let event = this.events.find(event => event.id === responseData.event.id) if (event.customTickets.length && event.customPricing) { event.bookingToEventTickets.forEach(ticket => { event.customTickets.filter(tick => tick.id === ticket.eventTicketId)[0].sold += ticket.persons ticket.persons = 0 }) event.places = (typeof requestData === 'undefined') ? event.maxCapacity - this.getAllSoldTickets(event) : event.places } else { event.places = (typeof requestData === 'undefined') ? event.places - responseData.booking.persons : event.places } if (event.places <= 0) event.full = true event.addToCalendarData = this.getEventAddToCalendarData( responseData, skipNotify ) if ((typeof requestData !== 'undefined')) { event.showEventDetails = false event.addToCalendarData.forceScroll = true } if (marketingMixin.hasAmeliaTracking(this)) { this.marketing.event = responseData.event if (typeof requestData !== 'undefined' && requestData !== null) { this.marketing.payment = requestData.appointment.payment } else { this.marketing.payment = this.appointment.payment } if (this.appointment.payment.gateway === 'onSite') { marketingMixin.trackAmeliaData(this, this.$root.marketing, 'event', 'Schedule') } else { marketingMixin.trackAmeliaData(this, this.$root.marketing, 'event', 'Purchase') } } // Customization hook if ('beforeConfirmedBooking' in window) { window.beforeConfirmedBooking(event.addToCalendarData) } else { event.showEventBooking = false event.showAddToCalendar = true event.bookingCompleted = true } }, getSearchParams () { let params = JSON.parse(JSON.stringify(this.params)) let eventId = this.getPreselectedEventId() let recurring = this.getPreselectedEventRecurring() let tagName = this.getPreselectedTag() ? this.getPreselectedTag() : params.tag let employee = this.getPreselectedEmployee() if (!tagName) { tagName = null } return { dates: params.date ? [ this.getDateString(params.date) ] : [ this.getDateString(this.getNowDate()) ], tag: tagName, locationId: params.locationId, page: this.pagination.page, id: eventId, recurring: recurring, providers: employee ? [employee] : null } }, getBookableData (evt) { let ticketsData = null if (evt.customTickets.length && evt.customPricing) { let totalPrice = 0 let totalTickets = 0 evt.customTickets.forEach(ticket => { let ticketPrice = evt.bookingToEventTickets.filter(tick => tick.eventTicketId === ticket.id)[0].price let ticketQuant = evt.bookingToEventTickets.filter(tick => tick.eventTicketId === ticket.id)[0].persons evt.bookingToEventTickets.filter(tick => tick.eventTicketId === ticket.id)[0].name = ticket.name totalTickets += ticketQuant totalPrice += (evt.aggregatedPrice || ticketQuant === 0 ? ticketQuant : 1) * ticketPrice }) evt.price = totalPrice ticketsData = { totalTickets, totalPrice, bookingToEventTickets: evt.bookingToEventTickets } } return { id: evt.id, name: evt.name, price: evt.price, depositData: evt.depositPayment !== 'disabled' ? { deposit: evt.deposit, depositPayment: evt.depositPayment, depositPerPerson: evt.depositPerPerson, depositFullPayment: evt.fullPayment } : null, maxCapacity: evt.maxCapacity, color: evt.color, aggregatedPrice: evt.aggregatedPrice, bookingStart: evt.periods[0].periodStart, bookingStartTime: evt.periods[0].periodStart.split(' ')[1], ticketsData } }, getEvents (expandEvent) { this.$http.get(`${this.$root.getAjaxUrl}/events`, { params: this.getAppropriateUrlParams(this.getSearchParams()) }) .then(response => { let $this = this this.events = [] this.pagination.count = response.data.data.count response.data.data.events.forEach(function (event) { event.gallery = event.gallery.sort((a, b) => (a.position > b.position) ? 1 : -1) event.showEventDetails = (expandEvent || response.data.data.events.length === 1) && $this.showSingleEventDetails event.showEventBooking = false event.showAddToCalendar = false event.bookingCompleted = false if ($this.$root.useTranslations) { event.name = $this.getNameTranslated(event) event.description = $this.getDescriptionTranslated(event) event.customTickets.forEach((ticket) => { ticket.name = $this.getTicketTranslated(ticket) }) } if (event.customTickets.length && event.customPricing) { event.bookingToEventTickets = $this.setTicketsData(event) } $this.events.push(event) if ($this.$root.settings.general.showClientTimeZone) { event.periods.forEach(function (period) { let utcOffsetStart = moment(period.periodStart, 'YYYY-MM-DD HH:mm:ss').toDate().getTimezoneOffset() let utcOffsetEnd = moment(period.periodEnd, 'YYYY-MM-DD HH:mm:ss').toDate().getTimezoneOffset() if (utcOffsetStart > 0) { period.periodStart = moment.utc(period.periodStart, 'YYYY-MM-DD HH:mm:ss').subtract(utcOffsetStart, 'minutes').format('YYYY-MM-DD HH:mm:ss') } else { period.periodStart = moment.utc(period.periodStart, 'YYYY-MM-DD HH:mm:ss').add(-1 * utcOffsetStart, 'minutes').format('YYYY-MM-DD HH:mm:ss') } if (utcOffsetEnd > 0) { period.periodEnd = moment.utc(period.periodEnd, 'YYYY-MM-DD HH:mm:ss').subtract(utcOffsetEnd, 'minutes').format('YYYY-MM-DD HH:mm:ss') } else { period.periodEnd = moment.utc(period.periodEnd, 'YYYY-MM-DD HH:mm:ss').add(-1 * utcOffsetEnd, 'minutes').format('YYYY-MM-DD HH:mm:ss') } }) } }) this.runCacheAction() if ('ameliaEventsLoaded' in window) { window.ameliaEventsLoaded($this.events) } this.fetched = true this.fetchedFiltered = true }) .catch(e => { console.log(e.message) }) }, toggleEventDetails (evt) { evt.showEventDetails = !evt.showEventDetails this.events.forEach(function (event) { if (event.id !== evt.id) { event.showEventDetails = false event.showEventBooking = false event.showAddToCalendar = false event.showConfirmBooking = false } }) evt.showEventBooking = false this.appointment.bookings[0].persons = 1 this.updateSettings(evt.settings) setTimeout(() => { this.scrollView('am-event-' + evt.id, 'start') }, 300) if ('ameliaEventSelected' in window) { window.ameliaEventSelected(evt) } if (marketingMixin.hasAmeliaTracking(this.$root.marketing, evt)) { this.marketing.event = evt marketingMixin.trackAmeliaData(this, this.$root.marketing, 'event', 'SelectEvent') } }, toggleEventBooking (evt) { evt.showEventDetails = !evt.showEventDetails evt.showEventBooking = !evt.showEventBooking this.appointment.payment = { amount: 0, gateway: '', data: {} } setTimeout(() => { let element = document.getElementById(`am-event-${evt.id}`) if (typeof element !== 'undefined' && element !== null) { element.scrollIntoView({behavior: 'smooth', block: 'start', inline: 'start'}) } }, 300) this.updateSettings(evt.settings) }, getLocation (evt) { if (evt.locationId && this.options.entities.locations.length) { let location = this.options.entities.locations.find(location => location.id === evt.locationId) return typeof location !== 'undefined' ? location.name : '' } else if (evt.customLocation) { return evt.customLocation } }, inlineBookingSVG () { let inlineSVG = require('inline-svg') inlineSVG.init({ svgSelector: 'img.svg-booking', initClass: 'js-inlinesvg' }) }, ticketSpotsNumber (evt, index, ticket) { let spots = ticket.spots - ticket.sold - evt.bookingToEventTickets[index].persons if (spots <= 0) { spots = '' } return `${spots} ${this.spotsLabels(spots)}` }, spotsLabels (number) { if (number > 1) { return this.eventSpotsLabel } if (number === 1) { return this.eventSpotLabel } return this.eventNoSpotsLabel } }, computed: { selectedDateInput () { return this.$moment(this.params.date).format(this.momentDateFormat) } }, watch: { 'pagination.page' () { if (this.cacheData && this.cacheData.request.queryParams) { this.cacheData.request.queryParams = null } else { this.getEvents(false) } } }, components: { ConfirmBooking, AddToCalendar } } </script> booking/Booking.vue 0000666 00000165535 15165376672 0010352 0 ustar 00 <template> <div id="amelia-booking-wrap" class="am-wrap"> <!-- Spinner --> <div class="am-spinner am-section" v-show="!empty && (!fetched || loadingCacheBookingData)"> <img class="svg-booking am-spin" :src="$root.getUrl + 'public/img/oval-spinner.svg'"> <img class="svg-booking am-hourglass" :src="$root.getUrl + 'public/img/hourglass.svg'"> </div> <div v-if="empty" class="am-no-services"> <img :src="$root.getUrl+'public/img/am-empty-booking.svg'" style="margin-top: 10px;"> <h1>{{$root.labels.oops}}</h1> <h3 v-if="$root.shortcodeData.booking.show !== 'packages'">{{$root.labels.no_services_employees}}</h3> <p v-if="$root.shortcodeData.booking.show !== 'packages'">{{$root.labels.add_services_employees}}</p> <p v-if="$root.shortcodeData.booking.show === 'packages'">{{$root.labels.no_package_services}}</p> <a href="https://wpamelia.com/services-and-categories/"> {{$root.labels.add_services_url}} </a> <span v-if="$root.shortcodeData.booking.show !== 'packages'" style="font-size:14px">{{$root.labels.and}}</span> <a v-if="$root.shortcodeData.booking.show !== 'packages'" href="https://wpamelia.com/employees/"> {{$root.labels.add_employees_url}} </a> </div> <div :id="id" class="am-step-booking-catalog" :class="$root.settings.customization.forms ? `am-form-${formType}` : ''" v-show="!empty && fetched"> <!-- Select Service Form --> <select-service-form v-if="!bookingCompleted && !activePackage && !passedPackage && !showPackagesOnly && !packageCalendarActive" :show-services="showServices" :show-locations="showLocations" :show-employees="showEmployees" :passed-service="passedService" :appointment="appointment" :group="group" :selected-extras="selectedExtras" :options="options" :fetched="fetched" :extras-error="extrasError" :form-type="formType" :forms-data="forms[formType]" :loading-time-slots="loadingTimeSlots" @change="changeFormItem" @getSlots="getTimeSlots" @enableGrouping="enableGrouping" ></select-service-form> <!-- /Select Service Form --> <!-- Recurring Setup Form --> <recurring-setup v-if="activeRecurringSetup && !bookingCompleted" :containerId="id" :initialRecurringData="initialRecurringData" :recurringData="recurringData" :disabledWeekdays="disabledWeekdays" :availableDates="availableDates" :calendarTimeSlots="calendarTimeSlots" :occupiedTimeSlots="occupiedTimeSlots" :service="getServiceById(appointment.serviceId)" :isFrontend="true" :form-type="formType" :forms-data="forms[formType]" @cancelRecurringSetup="cancelRecurringSetup" @confirmRecurringSetup="confirmRecurringSetup" > </recurring-setup> <!-- /Recurring Setup Form --> <!-- Recurring Dates Form --> <recurring-dates v-if="activeRecurringDates && !bookingCompleted" dialogClass="am-recurring-dates" :recurringData="recurringData" :availableDates="availableDates" :calendarTimeSlots="calendarTimeSlots" :isFrontend="true" :service="getServiceById(appointment.serviceId)" :selectedExtras="selectedExtras" :form-type="formType" :forms-data="forms[formType]" @confirmRecurringDates="confirmRecurringDates" @cancelRecurringDates="cancelRecurringDates" > </recurring-dates> <!-- /Recurring Dates Form --> <!-- Calendar Date Time Form --> <calendar-date-time-form v-if="!bookingCompleted && !activeRecurringSetup && !activePackage && !passedPackage && !showPackagesOnly && !packageCalendarActive" :id="id" :selected-date="selectedDate" :available-dates="availableDates" :disabled-weekdays="disabledWeekdays" :disabled-attribute="disabledAttribute" :calendar-id="calendarId" :show-times="showTimes" :appointment="appointment" :available-time-slots="availableTimeSlots" :is-recurring-available="isRecurringAvailable" :active-recurring="activeRecurring" :show-calendar-back-button="showCalendarBackButton" :show-calendar-continue-button="showCalendarContinueButton" :loading="loading" :loading-time-slots="loadingTimeSlots" :durations="getDurations()" :form-type="formType" :forms-data="forms[formType]" @selectDuration="getTimeSlots" @selectDate="selectDate" @setTimeSlots="setTimeSlots" @selectTime="selectTime" @changeMonth="changeMonth" @togglePicker="togglePicker" @showNextScreen="showNextScreen" @changeRecurring="changeActiveRecurring" ></calendar-date-time-form> <!-- /Calendar Date Time Form --> <!-- Select Package Form --> <select-package-form v-if="!bookingCompleted && !activePackage && !passedPackage && !packageListShown && !activeConfirm && showPackagesOnly" :selected-package-id="selectedPackageId" :fetched="fetched" :options="options" :form-type="formType" :forms-data="forms[formType]" @continuePackage="continuePackage" ></select-package-form> <!-- /Select Package Form --> <!-- Package Info Form --> <package-info v-if="!bookingCompleted && activePackage && !passedPackage" class="am-package-selected" :id="id" :fetched-slots="fetchedSlots" :selected-package="selectedPackage" :package-slots-fetched="packageSlotsFetched" :has-back="packagesHaveBack" :has-header="true" :form-type="formType" :forms-data="forms[formType]" @showPackageCalendar="packageServiceSelected++" @closePackage="closePackage" @packageSlotsSelectedCallback="packageSlotsSelectedCallback" > </package-info> <!-- /Package Info Form --> <!-- Package Setup Form --> <package-setup v-if="!bookingCompleted && (selectedPackage ? selectedPackage.hasSlots : true) && packageCalendarActive" class="am-select-date am-select-service-date-transition am-package-setup" :id="id" :options="options" :selectedPackage="selectedPackage" :packageServiceSelected="packageServiceSelected" :packageCreated="packageCreated" :isPassedPackage="!!passedPackage" :disableFetchSlots="disableFetchPackageSlots" :form-type="formType" :forms-data="forms[formType]" @packageSlotsFetchedCallback="packageSlotsFetchedCallback" @packageSlotsSelectedCallback="packageSlotsSelectedCallback" @packageListShow="packageListShow" > </package-setup> <!-- /Package Setup Form --> <!-- Package List Form --> <package-list v-if="!bookingCompleted && packageListShown" :container-id="'amelia-app-booking' + $root.shortcodeData.counter" :bookable="getBookablePackageData()" :has-header="!passedPackage" :quantity="selectedPackage.sharedCapacity ? selectedPackage.quantity : 0" :form-type="formType" :forms-data="forms[formType]" @cancelBooking="cancelPackageList" @packageSlotsSelectedCallback="packageSlotsSelectedCallback" > </package-list> <!-- /Package List Form --> <!-- Confirm Booking --> <confirm-booking v-if="activeConfirm && !bookingCompleted" dialogClass="am-confirm-booking am-scroll" v-bind="cacheData && (cacheData.status === 'canceled' || cacheData.status === 'failed') ? getCacheDataRequestProps() : { bookableType: selectedPackage === null ? 'appointment' : 'package', containerId: getContainerId(), bookable: getBookableData(), appointment: appointment, marketing: marketing, provider: selectedProvider, location: selectedLocation, service: selectedService, customFields: options.entities.customFields, recurringData: getRecurringAppointmentsData(), packageData: selectedPackage === null ? null : getPackageAppointmentsData(), recurringString: recurringData.recurringString + ' ' + recurringData.untilString, hasCancel: !passedPackage || passedPackage.hasSlots, hasHeader: !passedPackage, phonePopulated: phonePopulated, passedCategoryId: passedCategory ? passedCategory.id : null, queryParams: [] }" :form-type="formType" :forms-data="forms[formType]" @confirmedBooking="confirmedBooking" @cancelBooking="cancelBooking" > </confirm-booking> <!-- /Confirm Booking --> <!-- Add To Calendar --> <transition name="fade"> <add-to-calendar v-if="showAddToCalendar" ref="congratulations" :addToCalendarData="addToCalendarData" :form-type="formType" :forms-data="forms[formType]" @closeDialogAddToCalendar="closeDialogAddToCalendar" ></add-to-calendar> </transition> <!-- /Add To Calendar --> </div> <div class="am-lite-footer"> <a v-if="$root.isLite && $root.settings.general.backLink.enabled" rel="nofollow" class="am-lite-footer-link" :href="$root.settings.general.backLink.url" target="_blank" > {{ $root.settings.general.backLink.label }} </a> </div> </div> </template> <script> import moment from 'moment' import marketingMixin from '../../../js/frontend/mixins/marketingMixin' import cacheMixin from '../../../js/frontend/mixins/cacheMixin' import formsCustomizationMixin from '../../../js/common/mixins/formsCustomizationMixin' import imageMixin from '../../../js/common/mixins/imageMixin' import dateMixin from '../../../js/common/mixins/dateMixin' import priceMixin from '../../../js/common/mixins/priceMixin' import helperMixin from '../../../js/backend/mixins/helperMixin' import settingsMixin from '../../../js/common/mixins/settingsMixin' import entitiesMixin from '../../../js/common/mixins/entitiesMixin' import recurringMixin from '../../../js/common/mixins/recurringMixin' import packageMixin from '../../../js/frontend/mixins/packageMixin' import durationMixin from '../../../js/common/mixins/durationMixin' import customFieldMixin from '../../../js/common/mixins/customFieldMixin' import bookingMixin from '../../../js/frontend/mixins/bookingMixin' import slotsMixin from '../../../js/common/mixins/slotsMixin' import selectServiceForm from './components/formSteps/SelectServiceForm' import calendarDateTimeForm from './components/formSteps/CalendarDateTimeForm' import recurringSetup from '../../parts/RecurringSetup.vue' import recurringDates from '../../parts/RecurringDates.vue' import selectPackageForm from './components/formSteps/SelectPackageForm' import packageInfo from '../../parts/Empty.vue' import packageSetup from '../../parts/Empty.vue' import packageList from '../../parts/Empty.vue' import confirmBooking from '../parts/ConfirmBooking.vue' import addToCalendar from '../parts/AddToCalendar.vue' export default { mixins: [ slotsMixin, cacheMixin, formsCustomizationMixin, packageMixin, recurringMixin, imageMixin, dateMixin, entitiesMixin, bookingMixin, helperMixin, durationMixin, priceMixin, customFieldMixin, settingsMixin ], props: { id: { default: 'am-step-booking' }, showService: { type: Boolean, default: true }, addToCalendarProperty: { type: Object, default: () => { return { visible: true } } }, passedService: { default: () => {}, type: Object }, passedPackage: { default: () => {}, type: Object }, passedCategory: { default: () => {}, type: Object }, passedEntities: { default: () => {}, type: Object }, passedEntitiesRelations: { default: () => {}, type: Object }, formType: { type: String, default: 'stepByStepForm' } }, data () { return { empty: false, packagesHaveBack: true, showPackagesOnly: false, showEmptyCalendar: false, selectedWeekIndex: 0, selectedProvider: null, selectedLocation: null, selectedService: null, loading: false, isRecurringAvailable: false, initialRecurringData: null, recurringData: { dates: [], startAppointment: null, startDate: null, startTime: null, pageRecurringDates: [], pagination: { show: this.$root.settings.general.itemsPerPage, page: 1, count: 0 }, recurringString: '', untilString: '', datesCallback: null, setupCallback: null }, activeRecurring: false, activeRecurringSetup: false, activeRecurringDates: false, selectedMonth: moment().format('YYYY-MM'), isServiceChanged: true, calendarId: '', activeConfirm: false, bookingCompleted: false, activePicker: false, packageCalendarActive: false, addToCalendarData: null, appointment: { bookingStart: '', bookingStartTime: '', bookings: [{ customer: { email: '', externalId: null, firstName: '', id: null, lastName: '', phone: '' }, customFields: {}, customerId: 0, extras: [], persons: 1, haveMandatoryExtras: false, minSelectedExtras: 0 }], duration: 0, group: false, notifyParticipants: this.$root.settings.notifications.notifyCustomers, payment: { amount: 0, gateway: '', data: {} }, categoryId: null, providerId: 0, serviceId: null, locationId: null }, availableDates: [], availableTimeSlots: [], calendar: '', calendarTimeSlots: {}, occupiedTimeSlots: {}, calendarVisible: false, customer: { name: '', email: '', phone: '', paymentMethod: '' }, customerRules: { name: [ {required: true, message: 'Please input name', trigger: 'submit'}, {min: 3, max: 50, message: 'Length should be 3 to 50', trigger: 'submit'}], email: [ {required: true, message: 'Please input name', trigger: 'submit'}, {min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'submit'}], phone: '', paymentMethod: '' }, disabledAttribute: { contentStyle: { color: '#ccc', opacity: 0.4, textDecoration: 'line-through' } }, disabledWeekdays: null, fetched: false, fetchedSlots: false, group: { allowed: false, enabled: false, count: 1, options: [] }, loadingTimeSlots: false, slotsIndexStarted: 0, responseEntities: { categories: [], employees: [], locations: [], services: [] }, options: { availableEntitiesIds: { categories: [], employees: [], locations: [], services: [] }, entitiesRelations: {}, entities: { packages: [], services: [], employees: [], locations: [], customFields: [] } }, marketing: { service: null, employee: null, location: null, category: null, package: null, payment: null }, selectedExtras: [], previouslySelectedExtras: [], selectedDate: null, showAddToCalendar: false, showExtras: false, showFilters: false, showTimes: false, showServices: false, showEmployees: false, showLocations: false, extrasMandatory: false, showCalendarBackButton: false, showCalendarContinueButton: false, times: '', extrasError: [], forms: {} } }, created () { this.forms = this.getTranslatedForms('stepByStepForm') this.calendarId = 'am-appointment-times' + this.$root.shortcodeData.counter window.addEventListener('resize', this.handleResize) }, mounted () { if (this.addToCalendarProperty.visible) { this.setCacheData(this.getContainerId(), true) } else { this.showAddToCalendar = false } if (!this.$root.shortcodeData.hasBookingShortcode || !this.$root.shortcodeData.hasCategoryShortcode) { this.inlineBookingSVG() } if (this.passedService) { this.checkMandatoryExtras(this.passedService) } // Customization hook if ('beforeBookingLoaded' in window) { window.beforeBookingLoaded(this) } if (this.passedEntities && !this.passedPackage) { this.options.isFrontEnd = true this.options.entitiesRelations = Object.assign({}, this.passedEntitiesRelations) let shortCodeEntitiesIds = this.getShortCodeEntityIds() this.filterEntities(this.passedEntities, { categoryId: this.passedService.categoryId, serviceId: this.passedService.id, employeeId: shortCodeEntitiesIds.employeeId, locationId: shortCodeEntitiesIds.locationId }) this.fetchedEntities() this.times = document.getElementById(this.calendarId) } else if (!this.passedPackage) { let shortCodeEntitiesIds = this.getShortCodeEntityIds() if (shortCodeEntitiesIds.show === 'packages') { this.showPackagesOnly = true this.packageCalendarActive = true } else if (shortCodeEntitiesIds.show === 'services') { this.showPackagesOnly = false } let $this = this this.fetchEntities(function (success) { if (success) { $this.fetchedEntities() } }, { types: ['categories', 'employees'], isFrontEnd: true, isPanel: false }) this.times = document.getElementById(this.calendarId) } else if (this.passedPackage) { this.options.isFrontEnd = true this.options.entitiesRelations = Object.assign({}, this.passedEntitiesRelations) this.options.entities = this.passedEntities this.setBookingCustomFields() this.selectPackage(this.passedPackage) if (!this.passedPackage.hasSlots || this.loadingCacheBookingData) { this.fetched = true this.packageSlotsSelectedCallback() this.runCacheAction() } if (this.options.entities.services.length === 0 || this.options.entities.employees.length === 0) this.empty = true } }, updated () { this.handleResize() }, methods: { changeFormItem (fieldObj) { if (fieldObj.identifier === 'changeService') { this.changeService() if (marketingMixin.hasAmeliaTracking(this.$root.marketing, this.appointment.serviceId)) { this.marketing.service = this.getServiceById(this.appointment.serviceId) this.marketing.category = this.marketing.service ? this.getCategoryById(this.marketing.service.categoryId) : null marketingMixin.trackAmeliaData(this, this.$root.marketing, 'appointment', 'SelectService') } } if (fieldObj.identifier === 'selectPackage') { this.selectPackage(fieldObj.value) if (marketingMixin.hasAmeliaTracking(this.$root.marketing, fieldObj.value.id)) { this.marketing.package = this.getPackageById(fieldObj.value.id) marketingMixin.trackAmeliaData(this, this.$root.marketing, 'package', 'SelectPackage') } } if (fieldObj.identifier === 'changeLocation') { this.changeLocation(fieldObj.required) if (marketingMixin.hasAmeliaTracking(this.$root.marketing, this.appointment.locationId)) { this.marketing.location = this.getLocationById(this.appointment.locationId) marketingMixin.trackAmeliaData(this, this.$root.marketing, 'appointment', 'SelectLocation') } } if (fieldObj.identifier === 'changeEmployee') { this.changeEmployee(fieldObj.required) if (marketingMixin.hasAmeliaTracking(this.$root.marketing, this.appointment.providerId)) { this.marketing.employee = this.getProviderById(this.appointment.providerId) marketingMixin.trackAmeliaData(this, this.$root.marketing, 'appointment', 'SelectEmployee') } } if (fieldObj.identifier === 'enableGrouping') { this.enableGrouping() } if (fieldObj.identifier === 'changeNumberOfPersons') { this.changeNumberOfPersons() } if (fieldObj.identifier === 'addExtra') { this.addExtra() } if (fieldObj.identifier === 'changeSelectedExtra') { this.changeSelectedExtra(fieldObj.value, fieldObj.key) } if (fieldObj.identifier === 'deleteExtra') { this.deleteExtra(fieldObj.value) } }, changeActiveRecurring (recurring) { this.activeRecurring = recurring }, runCacheAction () { if (this.loadingCacheBookingData) { if (this.cacheData.status === 'canceled' || this.cacheData.status === 'failed') { this.showConfirmBooking() } else if (this.cacheData.status === 'paid' || this.cacheData.status === null) { this.confirmedBooking(this.cacheData.response, true, this.cacheData.request) } this.loadingCacheBookingData = false } }, getContainerId () { return 'amelia-app-booking' + this.$root.shortcodeData.counter }, showConfirmBooking () { this.activeConfirm = true this.loading = true let amContainer = document.getElementById(this.id) setTimeout(() => { this.loading = false this.loadingCacheBookingData = false amContainer.classList.toggle('am-active-picker', false) amContainer.classList.toggle('am-active-confirm', this.activeConfirm) }, 500) }, checkMandatoryExtras (service) { this.appointment.bookings[0].haveMandatoryExtras = service.mandatoryExtra this.appointment.bookings[0].minSelectedExtras = service.minSelectedExtras for (let i = 0; i < service.minSelectedExtras; i++) { if (service.mandatoryExtra) { this.extrasMandatory = true this.addExtra() } } }, getBookableData () { if (this.selectedPackage === null) { this.selectedService = this.getProviderById(this.selectedProvider.id).serviceList.find(service => service.id === this.appointment.serviceId) if (marketingMixin.hasAmeliaTracking(this)) { this.marketing.service = this.selectedService this.marketing.employee = this.selectedProvider this.marketing.location = this.selectedLocation this.marketing.category = this.getCategoryById(this.selectedService.categoryId) } return { id: this.selectedService.id, name: this.selectedService.name, price: this.getServiceDurationPrice(this.selectedService, this.appointment.serviceDuration), depositData: this.selectedService.depositPayment !== 'disabled' ? { deposit: this.selectedService.deposit, depositPayment: this.selectedService.depositPayment, depositPerPerson: this.selectedService.depositPerPerson, depositFullPayment: this.selectedService.fullPayment } : null, maxCapacity: this.selectedService.maxCapacity, pictureThumbPath: this.selectedService.pictureThumbPath, aggregatedPrice: this.selectedService.aggregatedPrice, bookingStart: this.appointment.bookingStart } } else { return { id: this.selectedPackage.id, name: this.selectedPackage.name, price: this.getPackagePrice(this.selectedPackage), depositData: this.selectedPackage.depositPayment !== 'disabled' ? { deposit: this.selectedPackage.deposit, depositPayment: this.selectedPackage.depositPayment, depositPerPerson: this.selectedPackage.depositPerPerson, depositFullPayment: this.selectedPackage.fullPayment } : null, calculatedPrice: this.selectedPackage.calculatedPrice, discount: this.selectedPackage.discount, maxCapacity: 1, pictureThumbPath: this.selectedPackage.pictureThumbPath, aggregatedPrice: this.selectedPackage.aggregatedPrice } } }, changeMonth (page) { this.selectedMonth = page ? moment().year(page.year).month(page.month - 1).date(1).format('YYYY-MM') : null }, showCalendarOnly (initCall) { let providerService = null if (this.appointment.serviceId && this.appointment.providerId) { providerService = this.getProviderService(this.appointment.providerId, this.appointment.serviceId) } let service = null if (this.appointment.serviceId) { service = this.getServiceById(this.appointment.serviceId) } let serviceProviderVisibility = true let servicePackageVisibility = true let serviceLocationVisibility = true let addExtraVisibility = true for (const key in this.forms[this.formType].selectServiceForm.itemsDraggable) { if (this.forms[this.formType].selectServiceForm.itemsDraggable[key].hasOwnProperty('visibility') && !this.forms[this.formType].selectServiceForm.itemsDraggable[key].visibility) { if (key === 'employeeFormField') { serviceProviderVisibility = false } if (key === 'servicePackageFormField') { servicePackageVisibility = false } if (key === 'locationFormField') { serviceLocationVisibility = false } if (key === 'addExtraFormField') { addExtraVisibility = false } } } let anyEmployeeHasBringingAnyone = false if (providerService === null) { this.employeesFiltered.forEach((employeeItem) => { employeeItem.serviceList.forEach((serviceItem) => { if (service && serviceItem.id === service.id && serviceItem.maxCapacity > 1 && serviceItem.bringingAnyone) { anyEmployeeHasBringingAnyone = true } }) }) } return initCall && !this.showServices && (!serviceProviderVisibility || !this.showEmployees) && (!serviceLocationVisibility || !this.showLocations) && (service && service.extras.length ? !this.extrasMandatory : true) && (service && service.extras.length ? !addExtraVisibility : true) && (!serviceProviderVisibility || this.employeesFiltered.length === 1) && ( (providerService !== null && (providerService.maxCapacity === 1 || providerService.bringingAnyone === false)) || (providerService === null && !anyEmployeeHasBringingAnyone) ) && (!servicePackageVisibility || (typeof this.passedService === 'undefined' ? this.options.entities.packages.length === 0 : true)) }, changeService () { if ('ameliaBooking' in window && 'changedEntity' in window.ameliaBooking) { window.ameliaBooking.changedEntity('service', this.appointment) } if (this.appointment.serviceId) { let selectedService = this.getServiceById(this.appointment.serviceId) this.appointment.serviceDuration = selectedService.duration if (selectedService.mandatoryExtra) { this.closePicker() } this.updateSettings(selectedService.settings) this.isServiceChanged = true // this.clearValidation() this.extrasError = [] this.appointment.bookings[0].extras = [] this.selectedExtras = [] this.checkMandatoryExtras(selectedService) this.handleCapacity(true, false, selectedService.maxExtraPeople) this.toggleRecurringActive() if (this.calendarVisible) { this.getTimeSlots() } } else { this.cancelRecurringSetup() setTimeout(() => { this.selectedDate = null this.closePicker() this.resetAppointment() this.unSelectTime() this.activeRecurringSetup = false this.showTimes = false }, 200) } }, changeEmployee (required = false) { let selectedService = null if (this.appointment.serviceId) { selectedService = this.getServiceById(this.appointment.serviceId) } this.handleCapacity(true, false, selectedService ? selectedService.maxExtraPeople : null) if (this.calendarVisible && this.appointment.providerId) { this.getTimeSlots() } if (required && !this.appointment.providerId) { this.cancelRecurringSetup() setTimeout(() => { this.selectedDate = null this.closePicker() this.resetAppointment() this.unSelectTime() this.activeRecurringSetup = false this.showTimes = false }, 200) } }, changeLocation (required = false) { if (this.calendarVisible && this.appointment.locationId) { this.getTimeSlots() } if (!this.appointment.locationId) { this.appointment.locationId = null } if (required && !this.appointment.locationId) { this.cancelRecurringSetup() setTimeout(() => { this.selectedDate = null this.closePicker() this.resetAppointment() this.unSelectTime() this.activeRecurringSetup = false this.showTimes = false }, 200) } }, enableGrouping () { let selectedService = null if (this.appointment.serviceId) { selectedService = this.getServiceById(this.appointment.serviceId) } this.handleCapacity(true, true, selectedService ? selectedService.maxExtraPeople : null) this.group.enabled === true ? this.appointment.bookings[0].persons += 1 : this.appointment.bookings[0].persons = 1 if (this.calendarVisible) { this.getTimeSlots() } }, changeNumberOfPersons () { if (this.calendarVisible) { this.getTimeSlots() } }, getSelectedExtraMaxQuantity: function () { return '' }, getAvailableExtras: function () { return [] }, addExtra: function () {}, deleteExtra: function () {}, changeSelectedExtra: function () {}, fetchedEntities () { if (this.$root.shortcodeData.booking.service && this.$root.shortcodeData.booking.service.length) { this.checkMandatoryExtras(this.options.entities.services[0]) } if (this.options.entities.services.length === 0 || this.options.entities.employees.length === 0 || (this.$root.shortcodeData.booking.show === 'packages' && this.options.entities.packages.length === 0) ) { this.empty = true } this.setBookingCustomFields() if (this.employeesFiltered.length === 1) { this.appointment.providerId = this.employeesFiltered[0].id this.$root.shortcodeData.booking.employee = this.appointment.providerId } else if (this.employeesFiltered.length > 1) { this.showEmployees = true } else { this.setUnavailableBooking() return } if (this.locationsFiltered.length === 1) { this.appointment.locationId = this.locationsFiltered[0].id this.$root.shortcodeData.booking.location = this.appointment.locationId } else if (this.locationsFiltered.length > 1) { this.showLocations = true } if (this.servicesFiltered.length === 1) { this.appointment.serviceId = this.servicesFiltered[0].id this.$root.shortcodeData.booking.service = this.appointment.serviceId } else if (this.servicesFiltered.length > 1) { this.showServices = true } else { this.setUnavailableBooking() return } this.toggleRecurringActive() let service = null; if (this.appointment.serviceId) { service = this.getServiceById(this.appointment.serviceId) this.appointment.serviceDuration = service.duration this.updateSettings(service.settings) } this.handleCapacity(true, false, service ? service.maxExtraPeople: null) if (('showAmeliaCalendarOnLoad' in window && window.showAmeliaCalendarOnLoad && this.appointment.serviceId) || (this.showCalendarOnly(true) && this.$root.shortcodeData.booking.show !== 'packages') ) { document.getElementById(this.id + '-calendar').classList.remove('am-select-service-date-transition') this.getTimeSlots() } else { let shortCodeEntitiesIds = this.getShortCodeEntityIds() if (shortCodeEntitiesIds.show === 'packages') { this.options.entities.packages.sort((a, b) => a.position - b.position) if (this.options.entities.packages.length === 1) { this.selectedPackageId = this.options.entities.packages[0].id this.packagesHaveBack = false this.selectPackage(this.getPackageById(this.selectedPackageId)) } } this.fetched = true this.runCacheAction() } }, getPossibleProvidersIfNoneSelected () { let providers = [] // If Employee is not selected, select ones that can provide the service if (!this.appointment.providerId) { // If grouping is enabled check employees capacity for selected service if (this.group.enabled) { this.employeesFiltered.forEach((employee) => { if (typeof (employee.serviceList.find(service => service.id === this.appointment.serviceId && service.maxCapacity >= this.appointment.bookings[0].persons)) !== 'undefined') { providers.push(employee) } }) } else { this.employeesFiltered.forEach((employee) => { if (typeof (employee.serviceList.find(service => service.id === this.appointment.serviceId)) !== 'undefined') { providers.push(employee) } }) } } return providers }, getTimeSlots (formValidation = true) { this.getCurrentUser() let service = this.appointment.serviceId ? this.getServiceById(this.appointment.serviceId) : null this.selectedExtras.forEach((extra, index) => { if (service && service.mandatoryExtra && !extra.id && this.appointment.bookings[0].minSelectedExtras > index) { this.extrasError.push(index) } }) if (this.extrasError.length) { return false } if (this.appointment.serviceId && formValidation) { this.loadingTimeSlots = true let extras = [] this.selectedExtras.forEach(function (extra) { if (extra.id) { extras.push({ id: extra.id, quantity: extra.quantity }) } }) // Customization hook if ('afterBookingSelectService' in window) { window.afterBookingSelectService(this.appointment, this.getServiceById(this.appointment.serviceId), this.getProviderById(this.appointment.providerId), this.getLocationById(this.appointment.locationId)) } this.slotsIndexStarted++ let currentIndex = this.slotsIndexStarted this.$http.get(`${this.$root.getAjaxUrl}/slots`, { params: this.getAppropriateUrlParams({ locationId: this.appointment.locationId, serviceId: this.appointment.serviceId, serviceDuration: this.appointment.serviceDuration, providerIds: this.appointment.providerId ? [this.appointment.providerId] : this.getPossibleProvidersIfNoneSelected().map(i => i.id), extras: JSON.stringify(extras), group: 1, page: 'booking', persons: this.appointment.bookings[0].persons }) }).then(response => { if (currentIndex < this.slotsIndexStarted) { this.fetchedSlots = true this.fetched = true this.loadingTimeSlots = false return } let dateSlots = this.$root.settings.general.showClientTimeZone ? this.getConvertedTimeSlots(response.data.data.slots) : response.data.data.slots let occupiedSlots = this.$root.settings.general.showClientTimeZone ? this.getConvertedTimeSlots(response.data.data.occupied) : response.data.data.occupied if (!this.calendarVisible) { this.activePicker = !this.activePicker document.getElementById(this.id).classList.toggle('am-active-picker', this.activePicker) } let availableDates = [] let minDate = null this.useSortedDateStrings(Object.keys(dateSlots)).forEach(function (dateString) { if (minDate === null) { minDate = dateString } availableDates.push(moment(dateString).toDate()) }) this.showFirstEventMonth(minDate) this.calendarTimeSlots = dateSlots this.occupiedTimeSlots = occupiedSlots this.disabledWeekdays = {weekdays: []} this.disabledWeekdays = (availableDates.length === 0) ? {weekdays: [1, 2, 3, 4, 5, 6, 7]} : null this.availableDates = availableDates this.calendarVisible = true if (this.availableDates.length) { this.setTimeSlots() } if (!this.availableDates.length || !this.isSelectedDateAvailable()) { this.showTimes = false let amContainer = document.getElementById(this.id) amContainer.classList.remove('am-show-times') } let dateIsNotAvailable = !this.availableDates.length || !this.isSelectedDateAvailable() let timeIsNotAvailable = (this.appointment.bookingStartTime && this.availableTimeSlots.indexOf(this.appointment.bookingStartTime) === -1) if (dateIsNotAvailable || timeIsNotAvailable) { if (dateIsNotAvailable) { this.selectedDate = null } this.unSelectTime() } if (this.activeRecurringSetup) { let amContainer = document.getElementById(this.id) amContainer.classList.add('am-show-calendar') this.activeRecurringSetup = false if (!dateIsNotAvailable) { this.showTimeSlots() } else { this.times = document.getElementById(this.calendarId) } } this.fetchedSlots = true this.fetched = true this.loadingTimeSlots = false this.runCacheAction() }).catch(e => { console.log(e.message) this.fetchedSlots = true this.fetched = true }) } }, showFirstEventMonth (minDate) { if (this.isServiceChanged && ( (this.selectedDate === null && moment(this.selectedMonth).format('YYYY-MM') !== moment(minDate, 'YYYY-MM-DD').format('YYYY-MM')) || (this.selectedDate !== null && moment(this.selectedDate).format('YYYY-MM') !== moment(minDate, 'YYYY-MM-DD').format('YYYY-MM')) )) { this.selectedDate = moment(minDate).toDate() let $this = this setTimeout(function () { $this.selectedDate = null }, 100) } this.isServiceChanged = false }, addSlotsElementToWeekRow (weekRow) { if (!this.times) { this.times = document.getElementById(this.calendarId) } weekRow.parentNode.insertBefore(this.times, weekRow.nextSibling) this.isRecurringAvailable = this.getServiceById(this.appointment.serviceId).recurringCycle !== 'disabled' setTimeout(() => { if (this.availableTimeSlots.length && this.selectedDate) { if (moment(this.selectedDate).format('YYYY-MM-DD') === moment(this.availableDates[this.availableDates.length - 1]).format('YYYY-MM-DD')) { this.activeRecurring = false this.isRecurringAvailable = false } this.showTimes = true let amContainer = document.getElementById(this.id) amContainer.classList.add('am-show-times') this.appointment.bookingStartTime = this.availableTimeSlots[0] this.selectedWeekIndex = [...weekRow.parentNode.children].indexOf(weekRow) this.selectTime() } }, 200) }, selectDate (dayInfo, calendarDate) { this.selectedDate = calendarDate this.unSelectTime() let isDisabled = false dayInfo.attributes.forEach(function (attrItem) { if (attrItem.hasOwnProperty('key') && attrItem['key'] === 'disabled') { isDisabled = true } }) if (isDisabled) { return } let amContainer = document.getElementById(this.id) amContainer.classList.remove('am-show-times') this.showTimes = false if (dayInfo.inMonth) { let weekRowInMonth = dayInfo.event.target.parentNode.parentNode.parentNode.parentNode.parentNode if (weekRowInMonth.classList.contains('c-day')) { weekRowInMonth = dayInfo.event.target.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode } if (!weekRowInMonth.classList.contains('c-week')) { weekRowInMonth = dayInfo.event.target.parentNode.parentNode.parentNode.parentNode } if (!weekRowInMonth.classList.contains('c-week')) { weekRowInMonth = dayInfo.event.target.parentNode.parentNode.parentNode } this.addSlotsElementToWeekRow(weekRowInMonth) } else { setTimeout(() => { let weekRowNotInMonth = document.getElementById(this.id + '-calendar') .getElementsByClassName('c-weeks-rows')[0].children[dayInfo.weekdayOrdinal - 1] this.addSlotsElementToWeekRow(weekRowNotInMonth) }, 1000) } }, isSelectedDateAvailable () { let momentDate = moment(this.selectedDate) return this.calendarTimeSlots.hasOwnProperty(momentDate.format('YYYY-MM-DD')) }, setTimeSlots (calendarDate) { if (typeof calendarDate !== 'undefined') { this.selectedDate = calendarDate } let momentDate = moment(this.selectedDate) let dateString = momentDate.format('YYYY-MM-DD') if (this.isSelectedDateAvailable()) { this.availableTimeSlots = Object.keys(this.calendarTimeSlots[dateString]) this.appointment.duration = this.getAppointmentDuration( this.appointment.serviceDuration ? parseInt(this.appointment.serviceDuration) : this.getServiceById(this.appointment.serviceId).duration, this.selectedExtras ) } }, togglePicker () { this.calendarVisible = false this.activePicker = !this.activePicker let amContainer = document.getElementById(this.id) amContainer.classList.toggle('am-active-picker', this.activePicker) }, closePicker () { this.calendarVisible = false this.activePicker = false let amContainer = document.getElementById(this.id) amContainer.classList.remove('am-active-picker') this.isRecurringAvailable = false this.activeRecurring = false }, selectTime () { this.appointment.bookingStart = moment(this.selectedDate).format('YYYY-MM-DD') + ' ' + this.appointment.bookingStartTime this.showCalendarContinueButton = true if ('afterBookingTimeSelected' in window) { window.afterBookingTimeSelected(this.appointment) } if ('ameliaBooking' in window && 'disableScrollView' in window.ameliaBooking && window.ameliaBooking.disableScrollView === true) { return } this.scrollView('am-button-wrapper-' + this.$root.shortcodeData.counter, 'end') }, unSelectTime () { this.appointment.bookingStartTime = null this.showCalendarContinueButton = false }, refreshCalendar () { let calendarTimeSlots = [] let availableDates = [] for (let dateKey in this.calendarTimeSlots) { for (let timeKey in this.calendarTimeSlots[dateKey]) { for (let slotInfoKey in this.calendarTimeSlots[dateKey][timeKey]) { if (this.appointment.providerId && this.calendarTimeSlots[dateKey][timeKey][slotInfoKey][0] === this.appointment.providerId) { if (!(dateKey in calendarTimeSlots)) { availableDates.push(moment(dateKey).toDate()) calendarTimeSlots[dateKey] = {} } if (!(timeKey in calendarTimeSlots[dateKey])) { calendarTimeSlots[dateKey][timeKey] = [] } calendarTimeSlots[dateKey][timeKey].push(this.calendarTimeSlots[dateKey][timeKey][slotInfoKey]) } } } } this.calendarTimeSlots = calendarTimeSlots this.disabledWeekdays = {weekdays: []} this.disabledWeekdays = (availableDates.length === 0) ? {weekdays: [1, 2, 3, 4, 5, 6, 7]} : null this.availableDates = availableDates this.availableTimeSlots = Object.keys(calendarTimeSlots[moment(this.selectedDate).format('YYYY-MM-DD')]) }, showTimeSlots () { setTimeout(() => { let weeksWrapper = document.getElementsByClassName('c-weeks-rows-wrapper')[0] let weekRow = weeksWrapper.firstChild.children.item(this.selectedWeekIndex) this.times = document.getElementById(this.calendarId) weekRow.parentNode.insertBefore(this.times, weekRow.nextSibling) this.showTimes = true }, 200) }, cancelRecurringSetup: function () {}, confirmRecurringSetup: function () {}, cancelRecurringDates: function () {}, confirmRecurringDates: function () {}, showNextScreen () { this.recurringData.dates = [] if (this.activeRecurring && this.isRecurringAvailable && !this.activeRecurringSetup) { this.loading = true this.recurringData.startDate = this.appointment.bookingStart this.recurringData.startTime = this.appointment.bookingStart.split(' ')[1] let service = this.getServiceById(this.appointment.serviceId) this.initialRecurringData = this.getDefaultRecurringSettings( this.appointment.bookingStart, service.recurringCycle, this.calendarTimeSlots ) setTimeout(() => { let amContainer = document.getElementById(this.id) amContainer.classList.remove('am-show-calendar') this.loading = false this.showTimes = false this.activeRecurringSetup = true }, 500) return } let dateString = moment(this.selectedDate).format('YYYY-MM-DD') if (!this.appointment.providerId) { let employeesIds = this.calendarTimeSlots[dateString][this.appointment.bookingStartTime].map( i => i[0] ).filter( (v, i, a) => a.indexOf(v) === i ) this.appointment.providerId = employeesIds[Math.floor(Math.random() * (employeesIds.length) + 1) - 1] } if (!this.appointment.locationId) { let locationsIds = this.calendarTimeSlots[dateString][this.appointment.bookingStartTime].filter( i => i[0] === this.appointment.providerId ).map(i => i[1]) this.appointment.locationId = locationsIds.length ? this.getPreferredEntityId( this.calendarTimeSlots[dateString], dateString in this.occupiedTimeSlots ? this.occupiedTimeSlots[dateString] : {}, this.appointment.bookingStartTime, this.appointment.providerId, locationsIds, 1 ) : null } this.selectedProvider = this.getProviderById(this.appointment.providerId) this.selectedLocation = this.getLocationById(this.appointment.locationId) this.refreshCalendar() this.appointment.bookings[0].extras = this.selectedExtras // Customization hook if ('afterBookingSelectDateAndTime' in window) { window.afterBookingSelectDateAndTime(this.appointment, this.getServiceById(this.appointment.serviceId), this.getProviderById(this.appointment.providerId), this.getLocationById(this.appointment.locationId)) } this.showConfirmBooking() }, cancelPackageList () { let amContainer = document.getElementById(this.id) this.packageListShown = false this.activePackage = true amContainer.classList.toggle('am-active-picker', true) amContainer.classList.toggle('am-active-package-list', false) }, cancelBooking () { if (this.cacheData) { this.unsetCacheData() let amContainer = document.getElementById(this.id) this.activeConfirm = false amContainer.classList.toggle('am-active-confirm', false) return } if (this.activeRecurring) { let amContainer = document.getElementById(this.id) this.activeRecurringSetup = true this.activeRecurringDates = true this.activeConfirm = false amContainer.classList.toggle('am-active-confirm', false) amContainer.classList.add('am-active-recurring-dates') return } if (this.selectedPackage) { let amContainer = document.getElementById(this.id) this.activeConfirm = false this.activePackage = true if (this.selectedPackage.hasSlots) { this.packageListShown = true this.disableFetchPackageSlots = true amContainer.classList.add('am-active-package-list') } amContainer.classList.toggle('am-active-confirm', false) return } this.activeConfirm = false let amContainer = document.getElementById(this.id) amContainer.classList.toggle('am-active-confirm', this.activeConfirm) if (this.appointment.serviceId && this.appointment.providerId) { amContainer.classList.toggle('am-active-picker', true) } if (this.showCalendarOnly(true)) { amContainer.classList.add('am-mobile-collapsed') amContainer.classList.remove('am-desktop') } }, inlineBookingSVG () { let inlineSVG = require('inline-svg') inlineSVG.init({ svgSelector: 'img.svg-booking', initClass: 'js-inlinesvg' }) }, setUnavailableBooking () { this.showEmptyCalendar = true this.appointment.locationId = null this.group.options = [] this.group.enabled = false this.group.allowed = false this.activePicker = !this.activePicker this.calendarTimeSlots = {} this.disabledWeekdays = {weekdays: [1, 2, 3, 4, 5, 6, 7]} this.availableDates = [] this.calendarVisible = true this.fetchedSlots = true this.fetched = true this.loadingTimeSlots = false document.getElementById(this.id + '-calendar').classList.remove('am-select-service-date-transition') let amContainer = document.getElementById(this.id) amContainer.classList.toggle('am-active-picker', true) amContainer.classList.toggle('am-active-confirm', false) this.showCalendarBackButton = false this.showEmptyCalendar = true }, handleResize () { let amContainer = document.getElementById(this.id) if (this.showEmptyCalendar || this.showCalendarOnly(true) || this.passedPackage) { if (amContainer) { amContainer.classList.add('am-mobile-collapsed') amContainer.classList.remove('am-desktop') } this.showCalendarBackButton = this.options.entities.packages.length > 0 return } if (amContainer) { let amContainerWidth = amContainer.offsetWidth if (this.showCalendarOnly(false) || this.passedPackage) { amContainer.classList.add('am-mobile-collapsed') amContainer.classList.remove('am-desktop') this.showCalendarBackButton = this.options.entities.packages.length > 0 } else { if (amContainerWidth < 670) { amContainer.classList.add('am-mobile-collapsed') amContainer.classList.remove('am-desktop') this.showCalendarBackButton = true } else { amContainer.classList.add('am-desktop') amContainer.classList.remove('am-mobile-collapsed') this.showCalendarBackButton = this.options.entities.packages.length > 0 } } } }, confirmedBooking (responseData, skipNotify, requestData) { this.activeConfirm = false this.bookingCompleted = true this.addToCalendarData = this.getAppointmentAddToCalendarData( responseData, skipNotify ) if (marketingMixin.hasAmeliaTracking(this)) { if (responseData.packageId) { this.marketing.package = responseData.packageId ? this.getPackageById(responseData.packageId) : null } else { this.marketing.service = responseData.appointment.serviceId ? this.getServiceById(responseData.appointment.serviceId) : null this.marketing.employee = responseData.appointment.providerId ? this.getProviderById(responseData.appointment.providerId) : null this.marketing.location = responseData.appointment.locationId ? this.getLocationById(responseData.appointment.locationId) : null this.marketing.category = responseData.appointment.serviceId ? this.getCategoryById(this.marketing.service.categoryId) : null } if (typeof requestData !== 'undefined' && requestData !== null) { this.marketing.payment = requestData.appointment.payment } else { this.marketing.payment = this.appointment.payment } if (this.appointment.payment.gateway === 'onSite') { marketingMixin.trackAmeliaData(this, this.$root.marketing, responseData.type, 'Schedule') } else { marketingMixin.trackAmeliaData(this, this.$root.marketing, responseData.type, 'Purchase') } } // Customization hook if ('beforeConfirmedBooking' in window) { window.beforeConfirmedBooking(this.addToCalendarData) } this.showAddToCalendar = true if (this.showAddToCalendar) { setTimeout(() => { if (!!Object.keys(this.$refs).length && this.$refs.congratulations) { this.$refs.congratulations.$el.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' }) } }, 500) } }, clearValidation () { if (typeof this.$refs.booking !== 'undefined') { this.$refs.booking.clearValidate() } }, closeDialogAddToCalendar () { this.showAddToCalendar = false document.getElementsByClassName('amelia-app-booking')[0].style.display = 'none' window.location.reload() }, resetAppointment () { if (this.$root.shortcodeData.booking.employee) { this.appointment.providerId = this.$root.shortcodeData.booking.employee } if (this.$root.shortcodeData.booking.location) { this.appointment.locationId = this.$root.shortcodeData.booking.location } if (this.$root.shortcodeData.booking.service) { this.appointment.serviceId = this.$root.shortcodeData.booking.service } if (this.$root.shortcodeData.booking.category) { this.appointment.categoryId = this.$root.shortcodeData.booking.category } this.appointment = { bookingStart: '', bookingStartTime: '', bookings: [{ customer: { email: '', externalId: null, firstName: '', id: null, lastName: '', phone: '' }, customFields: {}, customerId: 0, extras: [], persons: 1 }], duration: 0, serviceDuration: 0, group: false, notifyParticipants: this.$root.settings.notifications.notifyCustomers, payment: { amount: 0, gateway: '', currency: '', data: {} }, categoryId: null, providerId: this.forms[this.formType].selectServiceForm.itemsDraggable.employeeFormField.anyEmployeeVisible ? 0 : null, serviceId: this.setServiceIdOnResetAppointment(), locationId: null } this.setBookingCustomFields() }, setServiceIdOnResetAppointment () { if (this.$root.shortcodeData.booking.service) { return this.$root.shortcodeData.booking.service } if (this.passedService && this.passedService.id) { return this.passedService.id } return null }, continuePackage (selectedId) { if (!selectedId) { return false } this.selectedPackageId = selectedId this.selectPackage(this.getPackageById(selectedId)) } }, components: { moment, selectServiceForm, calendarDateTimeForm, recurringSetup, recurringDates, selectPackageForm, packageInfo, packageSetup, packageList, confirmBooking, addToCalendar } } </script> booking/components/formFields/ServiceFormField.vue 0000666 00000006365 15165376672 0016424 0 ustar 00 <template> <!-- Service --> <el-form-item v-if="showServices" :label="serviceLabel || capitalizeFirstLetter($root.labels.service) + ':'" prop="serviceId" class="am-select-service-option" :class="$root.settings.customization.forms ? `am-select-${classIdentifier}`: ''" > <el-select v-model="appointment.serviceId" :clearable="true" :loading=!fetched :popper-class="$root.settings.customization.forms ? `am-dropdown-${classIdentifier}` : ''" id="serviceId" placeholder="" @change="changeService" > <el-option v-for="service in servicesFiltered" :key="service.id" :label="service.name" :value="service.id" > </el-option> </el-select> </el-form-item> <!-- /Service --> </template> <script> import entitiesMixin from '../../../../../js/common/mixins/entitiesMixin' import helperMixin from '../../../../../js/backend/mixins/helperMixin' export default { name: 'serviceFormField', mixins: [entitiesMixin, helperMixin], props: { fetched: { type: Boolean, default: false }, showServices: { type: Boolean, default: false }, showLocations: { type: Boolean, default: false }, showEmployees: { type: Boolean, default: false }, group: { type: Object, default: () => { return { allowed: false, enabled: false, count: 1, options: [] } } }, selectedExtras: { type: Array, default: () => [] }, appointment: { type: Object, default: () => { return { bookingStart: '', bookingStartTime: '', bookings: [{ customer: { email: '', externalId: null, firstName: '', id: null, lastName: '', phone: '' }, customFields: {}, customerId: 0, extras: [], persons: 1 }], duration: 0, group: false, notifyParticipants: this.$root.settings.notifications.notifyCustomers, payment: { amount: 0, gateway: '', data: {} }, categoryId: null, providerId: 0, serviceId: null, locationId: null } } }, options: { type: Object, default: () => { return { availableEntitiesIds: { categories: [], employees: [], locations: [], services: [] }, entitiesRelations: {}, entities: { packages: [], services: [], employees: [], locations: [], customFields: [] } } } }, classIdentifier: { type: String, default: '' }, formField: { type: Object, default: () => {} } }, mounted () {}, data () { return { serviceLabel: this.formField.labels.service.value } }, methods: { changeService () { const fieldObj = { identifier: 'changeService' } this.$emit('change', fieldObj) } } } </script>