You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

467 lines
14 KiB

  1. <template>
  2. <div :class="['page-container', { 'nav-is-showing': isPickerVisible }]">
  3. <header>
  4. <div class="header__col">
  5. <template v-if="!isPickerVisible">
  6. <button class="btn btn--primary" @click="showPicker">
  7. tap the map or <svg
  8. xmlns="http://www.w3.org/2000/svg"
  9. class="icon icon-tabler icon-tabler-search"
  10. width="24"
  11. height="24"
  12. viewBox="0 0 24 24"
  13. stroke-width="3"
  14. stroke="#ffffff"
  15. fill="none"
  16. stroke-linecap="round"
  17. stroke-linejoin="round"
  18. >
  19. <path stroke="none" d="M0 0h24v24H0z" fill="none" />
  20. <circle cx="10" cy="10" r="7" />
  21. <line x1="21" y1="21" x2="15" y2="15" />
  22. </svg>
  23. </button>
  24. </template>
  25. <template v-if="isPickerVisible">
  26. <button class="btn btn--primary" @click="showPicker">
  27. hide search <svg
  28. xmlns="http://www.w3.org/2000/svg"
  29. class="icon icon-tabler icon-tabler-arrow-bar-to-up"
  30. width="24"
  31. height="24"
  32. viewBox="0 0 24 24"
  33. stroke-width="3"
  34. stroke="#ffffff"
  35. fill="none"
  36. stroke-linecap="round"
  37. stroke-linejoin="round"
  38. >
  39. <path stroke="none" d="M0 0h24v24H0z" fill="none" />
  40. <line x1="12" y1="10" x2="12" y2="20" />
  41. <line x1="12" y1="10" x2="16" y2="14" />
  42. <line x1="12" y1="10" x2="8" y2="14" />
  43. <line x1="4" y1="4" x2="20" y2="4" />
  44. </svg>
  45. </button>
  46. </template>
  47. </div>
  48. <div class="header__col">
  49. <div class="logo-wrapper">
  50. <Logo />
  51. <div v-if="!selectedOrig.iata && !isPickerVisible && isMapReady" class="logo-blurb">
  52. from one of <strong>{{ airports_orig.length }}</strong> <span style="color: white;">local</span> airports
  53. </div>
  54. <div v-if="selectedOrig.iata && !selectedDest.iata && airports_dest.length > 1 && !isPickerVisible && isMapReady" class="logo-blurb">
  55. to one of <strong>{{ airports_dest.length }}</strong> {{ getCompliment() }} destinations
  56. </div>
  57. </div>
  58. </div>
  59. <div class="header__col">
  60. </div>
  61. </header>
  62. <!-- <pre style="position: fixed; z-index: 500; right: 0; top: 2rem; font-size: 0.6rem; color: gray;">
  63. {{ selectedOrig }}
  64. {{ selectedDest }}
  65. </pre> -->
  66. <main>
  67. <nav v-if="isMapReady" :class="['nav', { 'nav--show': isPickerVisible },{ 'nav--hide': !isPickerVisible }]">
  68. <AirportPicker
  69. :airports="airports_orig"
  70. :selected-airport="selectedOrig"
  71. @select-airport="makeOrigin"
  72. @deselect-airport="clearOrigin"
  73. />
  74. <AirportPicker
  75. v-show="selectedOrig.iata"
  76. :airports="airports_dest"
  77. :selected-airport="selectedDest"
  78. :origin="selectedOrig"
  79. @select-airport="makeDestination"
  80. @deselect-airport="clearDestination"
  81. />
  82. </nav>
  83. <Map
  84. ref="mapComponent"
  85. :airports-orig="airports_orig"
  86. :airports-dest="airports_dest"
  87. :selected-orig="selectedOrig"
  88. :selected-dest="selectedDest"
  89. @make-origin="makeOrigin"
  90. @make-destination="makeDestination"
  91. @clear-origin="clearOrigin"
  92. @clear-destination="clearDestination"
  93. @map-ready="mapReady"
  94. />
  95. <div :class="['flyout-container',{ 'flyout-container--hide': !selectedOrig.iata || !selectedDest.iata },{ 'flyout-container--show': selectedOrig.iata && selectedDest.iata }]">
  96. <FlightsFlyout
  97. v-if="selectedOrig.iata && selectedDest.iata"
  98. :selected-orig="selectedOrig"
  99. :selected-dest="selectedDest"
  100. />
  101. </div>
  102. </main>
  103. </div>
  104. </template>
  105. <script>
  106. // import AirportPicker from '../components/AirportPicker.vue'
  107. //import cookies from cookie-universal-nuxt
  108. export default {
  109. // components: { AirportPicker },
  110. data () {
  111. return {
  112. isMapReady: false,
  113. color: 'red',
  114. isSidebarVisible: false,
  115. isPickerVisible: false,
  116. airports_orig: [],
  117. airports_dest: [],
  118. airportFetch_filterFormula: 'AND(Is_Origin=1,{IsCurrent-AsOrigin}=\'Yes\')',
  119. airportFetch_fields: [
  120. 'Airport_IATA',
  121. 'Icon_Url',
  122. 'Latitude_Deg',
  123. 'Longitude_Deg',
  124. 'Municipality',
  125. 'Airport_Name',
  126. 'Type',
  127. 'Search_Field'
  128. ],
  129. airportFetch_sort: '&sort[0][field]=Airport_IATA&sort[0][direction]=asc',
  130. selectedOrig: {
  131. iata: '',
  132. lat: '',
  133. long: '',
  134. icon: '',
  135. name: '',
  136. municipality: '',
  137. type: '',
  138. search: ''
  139. },
  140. selectedDest: {
  141. iata: '',
  142. lat: '',
  143. long: '',
  144. icon: '',
  145. name: '',
  146. municipality: '',
  147. type: '',
  148. search: ''
  149. },
  150. emptyAirport: {
  151. iata: '',
  152. lat: '',
  153. long: '',
  154. icon: '',
  155. name: '',
  156. municipality: '',
  157. type: '',
  158. search: ''
  159. },
  160. userEmail: ''
  161. }
  162. },
  163. async fetch () {
  164. let offset
  165. let data = []
  166. while (true) {
  167. const offsetParam = offset ? `&offset=${offset}` : ''
  168. const res = await fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Airports_IATA?filterByFormula=${this.airportFetch_filterFormula}${this.airportFetch_fields_string}${this.airportFetch_sort}${offsetParam}`, {
  169. method: 'GET',
  170. headers: {
  171. 'Content-Type': 'application/x-www-form-urlencoded',
  172. Authorization: 'Bearer keyJ2ht64ZSN57AG1'
  173. }
  174. })
  175. const json = await res.json()
  176. offset = await json.offset
  177. data = [...data, ...await json.records]
  178. await console.log(data.length)
  179. const emailCookie = await this.$cookies.get('email')
  180. console.log("cookie in fetch: " + emailCookie) // shows up with cookie in terminal, and undefined in browser
  181. if (await emailCookie && (typeof await emailCookie !== 'undefined')) {
  182. console.log("cookie was set")
  183. this.userEmail = await emailCookie
  184. }
  185. console.log("variable in fetch: " + this.userEmail)
  186. if (await !offset) { break } // Were done let's stop this thing
  187. }
  188. // console.log('Look ma! I waited!') // Won't run till the while is done
  189. this.airports_orig = data.map((record) => {
  190. return {
  191. iata: record.fields.Airport_IATA,
  192. lat: record.fields.Latitude_Deg,
  193. long: record.fields.Longitude_Deg,
  194. icon: record.fields.Icon_Url,
  195. name: record.fields.Airport_Name,
  196. municipality: record.fields.Municipality,
  197. type: record.fields.Type,
  198. search: record.fields.Search_Field
  199. }
  200. })
  201. },
  202. computed: {
  203. airportFetch_fields_string () {
  204. const vm = this
  205. const array = vm.airportFetch_fields.map((field) => {
  206. return '&fields[]=' + field
  207. })
  208. return array.join('')
  209. }
  210. },
  211. beforeCreate () {
  212. this.isMapReady = false
  213. },
  214. created () {
  215. // console.log(this.$route.path)
  216. if (this.$route && this.$route.params && this.$route.params.o) {
  217. this.fetchSingleAirport(this.$route.params.o, true)
  218. }
  219. if (this.$route && this.$route.params && this.$route.params.d) {
  220. this.fetchSingleAirport(this.$route.params.d, false)
  221. }
  222. // this.$cookies.set('at-go', 'set', {
  223. // path: '/',
  224. // maxAge: 60 * 60 * 24 * 7
  225. // })
  226. //this.userEmail = this.$cookies.get('at-go')
  227. },
  228. mounted () {
  229. //this.$segment.page('go');
  230. this.$segment.track('go__mount-page', {
  231. origin: this.$route.params.o,
  232. destination: this.$route.params.d
  233. });
  234. const emailCookie = this.$cookies.get('email')
  235. if(emailCookie){
  236. this.$segment.identify(emailCookie, {
  237. email: emailCookie
  238. });
  239. }
  240. // //const cookieRes = await fetch( "http://" + process.env.LOCAL_IP + ":" + process.env.LOCAL_PORT + '/login/check', { method: 'GET' })
  241. // //await console.log(cookieRes.json())
  242. //console.log('asdf')
  243. //console.log(this)
  244. //console.log(this.$cookies)
  245. //console.log("mounted " + this.$cookies.get('email'))
  246. //this.userEmail = this.$cookies.get('email')
  247. },
  248. // watch: {
  249. // history (to, from) {
  250. // console.log(`routed from ${from} to ${to}`)
  251. // }
  252. // },
  253. methods: {
  254. mapReady () {
  255. this.isMapReady = true
  256. },
  257. getCompliment () {
  258. const arr = [
  259. 'beautiful', 'gorgeous', 'breathtaking', 'wild', 'adventurous', 'amazing', 'majestic', 'uncharted', 'unpredictable', 'pristine', 'untamed'
  260. ]
  261. return arr[Math.floor(Math.random() * arr.length)]
  262. },
  263. makeOrigin (airport) {
  264. // console.log(airport)
  265. this.selectedOrig = { ...airport }
  266. this.selectedDest = { ...this.emptyAirport }
  267. this.$segment.track('go__select_orig', {
  268. origin: airport.iata
  269. });
  270. history.pushState(
  271. {},
  272. null,
  273. '/go/' + encodeURIComponent(airport.iata)
  274. )
  275. this.fetchDestinations(airport.iata)
  276. },
  277. makeDestination (airport) {
  278. this.selectedDest = { ...airport }
  279. this.$segment.track('go__select_dest', {
  280. origin: this.selectedOrig.iata,
  281. destination: airport.iata
  282. });
  283. this.isPickerVisible = false
  284. history.pushState(
  285. {},
  286. null,
  287. '/go/' + encodeURIComponent(this.selectedOrig.iata) + '/' + encodeURIComponent(airport.iata)
  288. )
  289. },
  290. clearOrigin () {
  291. this.$segment.track('go__clear_orig',{
  292. origin: this.selectedOrig.iata
  293. })
  294. this.selectedOrig = { ...this.emptyAirport }
  295. this.selectedDest = { ...this.emptyAirport }
  296. history.pushState(
  297. {},
  298. null,
  299. '/go/'
  300. )
  301. },
  302. clearDestination () {
  303. this.$segment.track('go__clear_dest', {
  304. origin: this.selectedOrig.iata,
  305. destination: this.selectedDest.iata
  306. })
  307. this.selectedDest = { ...this.emptyAirport }
  308. history.pushState(
  309. {},
  310. null,
  311. '/go/' + encodeURIComponent(this.selectedOrig.iata)
  312. )
  313. },
  314. async fetchDestinations (iata) {
  315. const airportFetchDestfilterFormula = `AND(Is_Destination=1,{IsCurrent-AsDest}="Yes",(FIND("${iata}", Associated_Origin_Search)>0))`
  316. let offset
  317. let data = []
  318. while (true) {
  319. const offsetParam = offset ? `&offset=${offset}` : ''
  320. const res = await fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Airports_IATA?filterByFormula=${airportFetchDestfilterFormula}${this.airportFetch_fields_string}${this.airportFetch_sort}${offsetParam}`, {
  321. method: 'GET',
  322. headers: {
  323. 'Content-Type': 'application/x-www-form-urlencoded',
  324. Authorization: 'Bearer keyJ2ht64ZSN57AG1'
  325. }
  326. })
  327. const json = await res.json()
  328. offset = await json.offset
  329. data = [...data, ...await json.records]
  330. // await console.log(data.length)
  331. if (await !offset) { break } // Were done let's stop this thing
  332. }
  333. // console.log('Look ma! I waited!') // Won't run till the while is done
  334. this.airports_dest = data.map((record) => {
  335. return {
  336. iata: record.fields.Airport_IATA,
  337. lat: record.fields.Latitude_Deg,
  338. long: record.fields.Longitude_Deg,
  339. icon: record.fields.Icon_Url,
  340. name: record.fields.Airport_Name,
  341. municipality: record.fields.Municipality,
  342. type: record.fields.Type,
  343. search: record.fields.Search_Field
  344. }
  345. })
  346. },
  347. async fetchSingleAirport (iata, isOrig) {
  348. const airportFetchFilterFormula =
  349. isOrig
  350. ? `AND(Is_Origin=1,{IsCurrent-AsOrigin}="Yes",Airport_IATA="${iata}")`
  351. : `AND(Is_Destination=1,{IsCurrent-AsDest}="Yes",Airport_IATA="${iata}")`
  352. const response = await fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Airports_IATA?filterByFormula=${airportFetchFilterFormula}${this.airportFetch_fields_string}`, {
  353. method: 'GET',
  354. headers: {
  355. 'Content-Type': 'application/x-www-form-urlencoded',
  356. Authorization: 'Bearer keyJ2ht64ZSN57AG1'
  357. }
  358. })
  359. const mapData = await response.json()
  360. const thisAirport = {
  361. iata: mapData.records[0].fields.Airport_IATA,
  362. lat: mapData.records[0].fields.Latitude_Deg,
  363. long: mapData.records[0].fields.Longitude_Deg,
  364. icon: mapData.records[0].fields.Icon_Url,
  365. name: mapData.records[0].fields.Airport_Name,
  366. municipality: mapData.records[0].fields.Municipality,
  367. type: mapData.records[0].fields.Type,
  368. search: mapData.records[0].fields.Search_Field
  369. }
  370. switch (isOrig) {
  371. case true:
  372. this.selectedOrig = { ...thisAirport }
  373. this.fetchDestinations(thisAirport.iata)
  374. break
  375. case false:
  376. this.selectedDest = { ...thisAirport }
  377. break
  378. default:
  379. break
  380. }
  381. },
  382. showPicker () {
  383. const vm = this
  384. vm.isPickerVisible = !vm.isPickerVisible
  385. setTimeout(function () {
  386. vm.$refs.mapComponent.resize()
  387. }, 500)
  388. if(vm.isPickerVisible) {
  389. this.$segment.track('go__show_picker')
  390. } else {
  391. this.$segment.track('go__hide_picker')
  392. }
  393. },
  394. showSidebar () {
  395. this.isSidebarVisible = !this.isSidebarVisible
  396. if(this.isSidebarVisible) {
  397. this.$segment.track('go__show_sidebar')
  398. } else {
  399. this.$segment.track('go__hide_sidebar')
  400. }
  401. }
  402. }
  403. }
  404. </script>
  405. <style scoped>
  406. main {
  407. background: black;
  408. }
  409. .logo-wrapper {
  410. position: relative;
  411. }
  412. .logo-blurb {
  413. position: absolute;
  414. font-size: clamp(10px, 1rem, 18px);
  415. color: white;
  416. --logo-v-offset: -0.65rem;
  417. --logo-h-offset: 0.35rem;
  418. top: clamp(20px + var(--logo-v-offset), 4rem + var(--logo-v-offset), 50px + var(--logo-v-offset));
  419. left: clamp(20px + var(--logo-h-offset), 4rem + var(--logo-h-offset), 50px + var(--logo-h-offset));
  420. white-space: nowrap;
  421. }
  422. </style>