{"version":3,"file":"header-D3c0qG2p.js","sources":["../../../app/javascript/src/stores/siteHeader.js","../../../app/javascript/src/utils/cookieUtils.js","../../../app/javascript/src/modules/Header/HeaderMenu.vue","../../../app/javascript/src/views/SiteHeader.vue","../../../app/javascript/entrypoints/header.js"],"sourcesContent":["import { defineStore } from \"pinia\";\n\nexport const useSiteHeaderStore = defineStore('site-header', {\n state: () => ({\n forceShow: false,\n })\n});","export const getCookieValue = (name) => document.cookie.match('(^|;)\\\\s*' + name + '\\\\s*=\\\\s*([^;]+)')?.pop() || '';\nexport const eraseCookie = (name) => document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC;`;\n","<template>\n <nav ref=\"rootEl\" class=\"menu\" :class=\"{ float }\">\n <NavLink v-if=\"shouldShowPlanTripBtn\" id=\"header-plan\" class=\"plan-trip\" href=\"/plan-a-trip\">Plan a trip</NavLink>\n\n <NavLink v-if=\"desktop && !user\" class=\"link signin\" href=\"/users/sign_in\">Sign in</NavLink>\n\n <div v-if=\"mobile || user\" class=\"user\">\n <button v-if=\"user\" class=\"user-btn\" @click=\"openPopup = !openPopup\">\n <img class=\"photo\" :src=\"user.photoUrl\" alt=\"user photo\">\n <i class=\"icon icon-arrow-down-1\" :class=\"{ up: openPopup }\" />\n </button>\n\n <button v-else class=\"menu-btn\" @click=\"openPopup = !openPopup\">\n <i class=\"icon icon-navigation-menu-1\"></i>\n </button>\n\n <Teleport :disabled=\"desktop\" to=\"body\">\n <transition name=\"popup\">\n <div v-if=\"openPopup\" ref=\"popupEl\" class=\"header-popup\" :class=\"{ loggedin: user }\">\n <div v-if=\"user\" class=\"greeting\">Hi {{ displayUser }}</div>\n <div class=\"links\">\n <NavLink v-if=\"isAdmin\" href=\"/admin\">Admin dashboard</NavLink>\n <NavLink v-if=\"editItineraryUrl\" :href=\"editItineraryUrl\">Edit itinerary</NavLink>\n <NavLink v-if=\"isOperator\" href=\"/operators\">Operator dashboard</NavLink>\n\n <NavLink v-if=\"!user\" href=\"/users/sign_in\">Sign in</NavLink>\n <NavLink v-if=\"user\" href=\"/travelers\">Dashboard</NavLink>\n <NavLink v-if=\"(isOperatorUser ? chatEnabled && hasChatrooms : chatEnabled) && user\" :href=\"chatUrl\">\n Messages <img v-if=\"hasUnreadMessages\" :src=\"GreenDot\" class=\"unread_dot\" />\n </NavLink>\n <NavLink v-if=\"user\" href=\"/users/edit\">Edit profile</NavLink>\n <NavLink v-if=\"user\" href=\"/travelers/your-trips\">Your trips</NavLink>\n\n <div v-if=\"mobile\" class=\"link-group\">\n <NavLink class=\"link\" href=\"/explore\">Explore</NavLink>\n <NavLink class=\"link\" href=\"/about\">About</NavLink>\n <NavLink class=\"link\" href=\"/en/magazine/\">Magazine</NavLink>\n <NavLink class=\"link\" href=\"/about#contact_us\">Contact</NavLink>\n </div>\n\n </div>\n <div class=\"socials\">\n <div>Follow Us</div>\n <div class=\"icons\">\n <SocialLinks />\n </div>\n </div>\n <button v-if=\"user\" class=\"logout\" @click=\"$emit('logout')\">\n <span>Logout</span>\n <i class=\"icon icon-logout menu__logout-icon\" />\n </button>\n </div>\n </transition>\n </Teleport>\n </div>\n </nav>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from \"vue\";\nimport { storeToRefs } from \"pinia\";\nimport { onClickOutside, syncRef } from '@vueuse/core'\nimport { useAuthUserState } from \"@/stores/authUser.js\";\nimport { useSiteHeaderStore } from \"@/stores/siteHeader.js\";\nimport { metaTagContent } from \"@/helpers/documentHelpers.js\";\nimport { useShared } from \"@/helpers/vueHelpers.js\";\nimport api from '@/utils/api.js';\nimport { pathnameWithoutLangPrefix } from \"@/utils/stringUtils.js\";\nimport { eraseCookie, getCookieValue } from '@/utils/cookieUtils.js';\nimport { logErrorOnSentry } from \"@/utils/errorReportingUtils.js\";\nimport { isFeatureEnabled } from '@/utils/featureUtils.js';\nimport NavLink from \"@/components/NavLink.vue\";\nimport SocialLinks from \"@/components/SocialLinks.vue\";\nimport GreenDot from 'app/assets/images/chat/green_dot.svg'\n\ndefineProps({\n float: {\n type: Boolean,\n default: false\n }\n});\ndefineEmits(['logout']);\n\nconst openPopup = ref(false);\nconst { mobile, desktop, componentInfo } = useShared();\nconst user = ref(null);\nconst rootEl = ref(null);\nconst popupEl = ref(null);\nconst authUserState = useAuthUserState();\nconst actualUser = ref('');\nconst currentUser = computed(() => user.value?.name?.split(' ')[0] || '');\nconst displayUser = computed(() => currentUser.value + (actualUser.value ? `(${actualUser.value})` : ''));\nconst isAdmin = ref(false);\nconst isOperator = ref(false);\nconst hasUnreadMessages = ref(false);\nconst hasChatrooms = ref(false);\nconst { forceShow } = storeToRefs(useSiteHeaderStore());\n\nconst shouldShowPlanTripBtn = computed(() => {\n const path = pathnameWithoutLangPrefix(location.pathname);\n if (path.startsWith('/plan-a-trip/coming-together')) {\n return true;\n }\n return path !== '/' && !path.startsWith('/plan-a-trip');\n});\nconst isOperatorUser = computed(() => isOperator.value && !isAdmin.value)\nconst chatEnabled = computed(() => isFeatureEnabled('chat_tool'))\n\nconst editItineraryUrl = computed(() => {\n const itineraryId = metaTagContent('itineraryId')\n return isAdmin.value && itineraryId ? `/admin/products/${itineraryId}?context=itinerary` : null;\n});\nconst chatUrl = computed(() => isOperatorUser.value ? \"/operators/messages\" : \"/travelers/messages\")\n\nsyncRef(openPopup, forceShow, { direction: 'ltr' });\n\nonClickOutside(popupEl, () => openPopup.value = false, { ignore: [rootEl] });\n\nwatch(openPopup, async (curr, _) => {\n if (curr) {\n const apiPath = (isOperatorUser.value ? '/api/operator/supplier' : '/api/user') + '/messages_read_status'\n\n try {\n const result = await api.get(apiPath)\n if (result) {\n hasUnreadMessages.value = result.data.has_unread_messages\n hasChatrooms.value = result.data.has_chatrooms\n }\n } catch (err) {\n if ([403].includes(err.response?.status)) return;\n logErrorOnSentry(err, componentInfo);\n }\n }\n})\n\nonMounted(async () => {\n if (getCookieValue('_tourhero_loggedin_cache') === \"loggedin\") {\n // if cookie present, then we may or may not be logged in, call API to ensure\n try {\n const result = await authUserState.fetch();\n if (result) {\n user.value = result;\n actualUser.value = user.value.meta.actualUser;\n isAdmin.value = user.value.meta.isAdmin;\n isOperator.value = user.value.meta.roles.includes('operator');\n }\n } catch (err) {\n console.error(err);\n eraseCookie('_tourhero_loggedin_cache');\n }\n }\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.menu {\n display: flex;\n align-items: center;\n gap: 12px;\n\n .link,\n .plan-trip {\n @include base-font;\n padding: 12px 20px;\n border-radius: 30px;\n\n @include mobile {\n line-height: 1;\n }\n }\n\n .link:hover {\n color: $grey-300;\n background-color: $grey-25;\n }\n\n .plan-trip {\n color: white;\n background-color: $brand-secondary;\n }\n}\n\n.menu.float {\n .link:hover {\n background-color: white;\n }\n\n .user>button {\n background-color: white;\n }\n\n .user::v-deep(.header-popup) {\n top: calc(100% + 16px);\n }\n}\n\n.user>button,\n.signin {\n border: 1px solid $grey-50;\n}\n\n.user {\n position: relative;\n\n &>button {\n display: flex;\n align-items: center;\n border-radius: 30px;\n color: $black;\n }\n\n .user-btn {\n padding: 8px 12px;\n gap: 7px;\n\n @include mobile {\n padding-block: 7px;\n }\n }\n\n .menu-btn {\n padding: 12px 20px;\n\n @include mobile {\n padding: 10px 16px;\n }\n\n &>i {\n font-size: 22px;\n line-height: 22px;\n transform: translateY(1px);\n\n @include mobile {\n font-size: 16px;\n line-height: 16px;\n }\n }\n }\n\n .photo {\n width: 30px;\n aspect-ratio: 1/1;\n border-radius: 50%;\n\n @include mobile {\n width: 24px;\n }\n }\n\n .icon-arrow-down-1 {\n font-size: 12px;\n transition: transform 200ms linear;\n\n &.up {\n transform: rotate(180deg) translateY(2px);\n }\n }\n}\n</style>\n\n<style lang=\"scss\">\n.header-popup {\n @include base-font;\n\n z-index: 2800;\n min-width: 258px;\n position: absolute;\n top: calc(100% + 28px);\n right: 0;\n border-radius: 15px;\n border: 1px solid $grey-25;\n background: #FFF;\n box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.05);\n padding: 24px;\n @include flex-column(32px);\n\n @include mobile {\n position: fixed;\n right: 0;\n top: var(--site-header-height);\n bottom: 0;\n overflow-y: auto;\n border-radius: 0;\n border: none;\n padding: 32px 24px;\n background: rgba(255, 255, 255, 0.70);\n box-shadow: -2px 0px 8px 0px rgba(0, 0, 0, 0.05);\n backdrop-filter: blur(20px);\n\n .greeting,\n .links,\n .logout {\n font-size: 16px;\n line-height: 24px;\n }\n\n &.loggedin .link-group {\n margin-top: 16px;\n }\n }\n\n .links,\n .link-group {\n display: grid;\n gap: 16px;\n }\n\n .socials {\n text-transform: uppercase;\n font-weight: 700;\n display: grid;\n gap: 16px;\n\n @include mobile {\n color: #222;\n font-size: 12px;\n font-weight: 700;\n line-height: 150%;\n /* 18px */\n }\n\n .icons {\n display: flex;\n gap: 20px;\n\n a {\n width: 20px;\n }\n }\n }\n\n .logout {\n display: flex;\n gap: 12px;\n color: $gray;\n }\n}\n\n.unread_dot {\n margin-left: 2px;\n margin-bottom: 2px;\n}\n\n.popup-enter-active,\n.popup-leave-active {\n transition-duration: 200ms;\n transition-timing-function: ease-in;\n}\n\n.popup-enter-from,\n.popup-leave-to {\n opacity: 0;\n transform: translateX(100%);\n}\n\n.popup-enter-to,\n.popup-leave-from {\n opacity: 1;\n transform: translateX(0);\n}\n</style>\n","<template>\n <div v-show=\"showSiteHeader\">\n <div class=\"spacer\" :style=\"{ height: `${dockHeight}px` }\"></div>\n\n <header ref=\"root\" class=\"site-header\" :class=\"{ dock, float }\">\n <div class=\"site-header-content\">\n <a :href=\"rootUrl\">\n <img class=\"logo\" src=\"app/assets/images/logo.png\" alt=\"TourHero Logo\">\n </a>\n\n <HeaderLinks v-if=\"desktop\" :float/>\n\n <HeaderMenu :float @logout=\"logout()\"/>\n </div>\n </header>\n </div>\n</template>\n\n<script setup>\nimport api from '@/utils/api.js';\nimport { ref, watch } from \"vue\";\nimport { storeToRefs } from \"pinia\";\nimport { useStyleTag } from \"@vueuse/core\";\nimport { useBodyClassWhen, useShared } from \"@/helpers/vueHelpers.js\";\nimport { useSiteHeaderStore } from \"@/stores/siteHeader.js\";\nimport { useElementSize, useWindowScroll } from \"@vueuse/core\";\nimport { metaTagContent } from \"@/helpers/documentHelpers.js\";\nimport HeaderLinks from \"@/modules/Header/HeaderLinks.vue\";\nimport HeaderMenu from \"@/modules/Header/HeaderMenu.vue\";\nimport { useGlobalState } from \"@/stores/global.js\";\n\nconst root = ref(null);\nconst dock = ref(true);\nconst float = ref(false);\nconst { y } = useWindowScroll()\nconst { height } = useElementSize(root);\nconst dockHeight = ref(0);\nconst rootUrl = metaTagContent('menuRootUrl');\nconst logoutUrl = metaTagContent('menuLogoutUrl');\nconst { css } = useStyleTag();\nconst { desktop } = useShared();\nconst { forceShow } = storeToRefs(useSiteHeaderStore());\nconst { showSiteHeader, siteHeaderHeight } = useGlobalState();\nimport { setBodyCssVar } from \"@/helpers/vueHelpers.js\";\n\nwatch([y, forceShow], ([newY, show], [oldY]) => {\n dock.value = newY <= dockHeight.value;\n float.value = !dock.value && (newY <= oldY || show);\n});\n\nwatch([dock, height], ([dock, height]) => {\n dockHeight.value = dock ? (height + 2) : dockHeight.value;\n css.value = `.flash { margin-top: ${dockHeight.value}px; }`;\n});\n\nwatch(height, (h) => {\n siteHeaderHeight.value = h;\n setBodyCssVar('--site-header-height', `${h}px`);\n});\n\nuseBodyClassWhen(dock, 'site-header--docked');\nuseBodyClassWhen(float, 'site-header--floating');\n\nasync function logout() {\n try {\n await api.delete(logoutUrl);\n } catch (err) {\n console.error(err);\n } finally {\n window.location.replace(rootUrl);\n }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.site-header {\n @include main-container;\n @include main-wrapper;\n position: fixed;\n z-index: 2900;\n top: 0;\n left: 0;\n right: 0;\n transform: translateY(-100%);\n transition: transform 200ms linear;\n\n @include mobile {\n border-bottom: 2px solid #F6F6F6;\n padding: 0;\n\n .site-header-content {\n padding: 10px 20px 10px 24px !important;\n border-radius: 0;\n }\n }\n}\n\n.site-header.dock {\n transform: translateY(0);\n border-bottom: 2px solid #F6F6F6;\n background-color: white;\n\n .site-header-content {\n padding: 20px 0;\n box-shadow: none;\n }\n}\n\n.site-header.float {\n @include desktop {\n transform: translateY(15px);\n border-bottom: none;\n }\n\n @include mobile {\n transform: translateY(0);\n }\n}\n\n.site-header-content {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 20px;\n border-radius: 50px;\n background: rgba(255, 255, 255, 0.70);\n backdrop-filter: blur(20px);\n box-shadow: 0 2px 12px 0 rgba(34, 34, 34, 0.10);\n\n .logo {\n height: 32px;\n // visually center\n position: relative;\n top: -4px;\n\n @include mobile {\n height: 24px;\n top: -2.5px;\n }\n }\n}\n</style>\n","import { initVueAppOnLoad } from \"@/app\";\nimport App from '@/views/SiteHeader.vue';\n\ninitVueAppOnLoad('#site-nav-app', App);"],"names":["useSiteHeaderStore","defineStore","getCookieValue","name","_a","eraseCookie","openPopup","ref","mobile","desktop","componentInfo","useShared","user","rootEl","popupEl","authUserState","useAuthUserState","actualUser","currentUser","computed","_b","displayUser","isAdmin","isOperator","hasUnreadMessages","hasChatrooms","forceShow","storeToRefs","shouldShowPlanTripBtn","path","pathnameWithoutLangPrefix","isOperatorUser","chatEnabled","isFeatureEnabled","editItineraryUrl","itineraryId","metaTagContent","chatUrl","syncRef","onClickOutside","watch","curr","_","apiPath","result","api","err","logErrorOnSentry","onMounted","root","dock","float","y","useWindowScroll","height","useElementSize","dockHeight","rootUrl","logoutUrl","css","useStyleTag","showSiteHeader","siteHeaderHeight","useGlobalState","newY","show","oldY","h","setBodyCssVar","useBodyClassWhen","logout","initVueAppOnLoad","App"],"mappings":"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"}