{"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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"}