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.

577 lines
14 KiB

  1. <template>
  2. <div class="white-bkg">
  3. <header>
  4. <div class="header__col">
  5. <button class="btn btn--primary" @click="goBack">
  6. <svg
  7. xmlns="http://www.w3.org/2000/svg"
  8. class="icon icon-tabler icon-tabler-chevrons-left"
  9. width="24"
  10. height="24"
  11. viewBox="0 0 24 24"
  12. stroke-width="3"
  13. stroke="#ffffff"
  14. fill="none"
  15. stroke-linecap="round"
  16. stroke-linejoin="round"
  17. >
  18. <path stroke="none" d="M0 0h24v24H0z" fill="none" />
  19. <polyline points="11 7 6 12 11 17" />
  20. <polyline points="17 7 12 12 17 17" />
  21. </svg>
  22. back
  23. </button>
  24. </div>
  25. <div class="header__col">
  26. <Logo />
  27. </div>
  28. <div class="header__col" />
  29. </header>
  30. <main class="main-with-header">
  31. <ChosenFlights
  32. :selected-orig="selectedOrig"
  33. :selected-dest="selectedDest"
  34. :departure="formattedDate($route.params.departure)"
  35. :departure-time="selectedSchedule && selectedSchedule.fields && selectedSchedule.fields.Departure_TimeL"
  36. style="margin: 0 1rem;"
  37. />
  38. <div class="schedule-wrapper">
  39. <ul class="schedule-grid">
  40. <li
  41. v-for="schedule in schedules"
  42. :key="schedule.fields.Record_ID"
  43. :class="[
  44. 'grid__row',
  45. { 'grid__row--selected': selectedSchedule.id == schedule.id },
  46. ]"
  47. >
  48. <div class="row__check">
  49. <label class="radio radio--sched" :for="schedule.id">
  50. <input :id="schedule.id" type="radio" name="schedules">
  51. <span @click="selectedSchedule = { ...schedule }">
  52. <svg
  53. xmlns="http://www.w3.org/2000/svg"
  54. class="icon icon-tabler icon-tabler-check"
  55. width="24"
  56. height="24"
  57. viewBox="0 0 24 24"
  58. stroke-width="3"
  59. stroke="#ffffff"
  60. fill="none"
  61. stroke-linecap="round"
  62. stroke-linejoin="round"
  63. >
  64. <path stroke="none" d="M0 0h24v24H0z" fill="none" />
  65. <path d="M5 12l5 5l10 -10" />
  66. </svg>
  67. </span>
  68. </label>
  69. </div>
  70. <div class="row__dep">
  71. <label class="text--muni">
  72. {{ selectedOrig.municipality }}
  73. </label>
  74. <h1 class="text--time">
  75. {{ schedule.fields.Departure_TimeL }}
  76. </h1>
  77. </div>
  78. <div class="row__trip">
  79. <svg
  80. xmlns="http://www.w3.org/2000/svg"
  81. class="icon icon-tabler icon-tabler-arrow-right-circle"
  82. width="100"
  83. height="100"
  84. viewBox="0 0 24 24"
  85. stroke-width="1.5"
  86. stroke="#007fff"
  87. fill="none"
  88. stroke-linecap="round"
  89. stroke-linejoin="round"
  90. >
  91. <path stroke="none" d="M0 0h24v24H0z" fill="none" />
  92. <path d="M18 15l3 -3l-3 -3" />
  93. <circle cx="5" cy="12" r="2" />
  94. <path d="M7 12h14" />
  95. </svg>
  96. </div>
  97. <div class="row__arr">
  98. <label class="text--muni">
  99. {{ selectedDest.municipality }}
  100. </label>
  101. <h1 class="text--time">
  102. {{ schedule.fields.Arrival_TimeL }}
  103. </h1>
  104. </div>
  105. <div class="row__info">
  106. <h2 class="text--carrier">
  107. {{ schedule.fields.Carrier_Name }}
  108. </h2>
  109. <span v-if="schedule.fields.Flight_Number" class="text--flight-num">
  110. Flight No. {{ schedule.fields.Flight_Number }}
  111. </span>
  112. <span class="text--dow">
  113. {{ schedule.fields.Frequency_Convert }}
  114. </span>
  115. <span class="badge--avail">
  116. Available until
  117. <strong>{{ formattedDate(schedule.fields.Effective_End) }}</strong>
  118. </span>
  119. </div>
  120. </li>
  121. </ul>
  122. </div>
  123. <div
  124. :class="[
  125. 'flyout-container',
  126. { 'flyout-container--hide': !selectedSchedule.id },
  127. { 'flyout-container--show': selectedSchedule.id },
  128. ]"
  129. >
  130. <div class="flyout">
  131. <button
  132. v-if="
  133. selectedSchedule &&
  134. selectedSchedule.fields &&
  135. selectedSchedule.fields.IsExpedia &&
  136. selectedSchedule.fields.IsExpedia[0] !== 'true'
  137. "
  138. class="btn btn--primary"
  139. @click="goToCarrier"
  140. >
  141. see flights
  142. </button>
  143. <div
  144. v-if="
  145. selectedSchedule &&
  146. selectedSchedule.fields &&
  147. selectedSchedule.fields.IsExpedia &&
  148. selectedSchedule.fields.IsExpedia[0] === 'true'
  149. "
  150. class="book-container"
  151. >
  152. <div>
  153. <label for="">How Many Adults?</label>
  154. <v-select
  155. v-model="numAdult"
  156. :options="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
  157. auto
  158. />
  159. </div>
  160. <div>
  161. <label for="">How Many Children?</label>
  162. <v-select
  163. v-model="numChild"
  164. :options="[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
  165. :menu-props="{ top: true, offsetY: true }"
  166. />
  167. </div>
  168. <button class="btn btn--primary" @click="book">
  169. book now!
  170. </button>
  171. </div>
  172. </div>
  173. </div>
  174. </main>
  175. </div>
  176. </template>
  177. <script>
  178. export default {
  179. data () {
  180. return {
  181. schedules: [],
  182. startingDate: '',
  183. selectedSchedule: {},
  184. selectedDays: [],
  185. selectedOrig: {},
  186. selectedDest: {},
  187. numAdult: 1,
  188. numChild: 0
  189. }
  190. },
  191. async fetch () {
  192. // console.log(this.$route.params.o)
  193. const today = new Date().toLocaleDateString('en-CA')
  194. // function addDays (date, days) {
  195. // const result = new Date(date)
  196. // result.setDate(result.getDate() + days)
  197. // return result
  198. // }
  199. // const tomorrow = addDays(new Date(), 1).toLocaleDateString('en-CA')
  200. const scheduleFetchFilterFormula = `AND(Is_Current="Yes",Origin_IATA="${this.$route.params.o}",Destination_IATA="${this.$route.params.d}",Effective_End>"${today}",SEARCH("${new Date(this.$route.params.departure).getDay()}",Frequency)>0)`
  201. // console.log(scheduleFetchFilterFormula)
  202. const scheduleFetchSort = '&sort[0][field]=DepartureTimeFormatted&sort[0][direction]=asc'
  203. const response = await fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Schedule?filterByFormula=${scheduleFetchFilterFormula}${scheduleFetchSort}`, {
  204. method: 'GET',
  205. headers: {
  206. 'Content-Type': 'application/x-www-form-urlencoded',
  207. Authorization: 'Bearer keyJ2ht64ZSN57AG1'
  208. }
  209. })
  210. const data = await response.json()
  211. // console.log(data)
  212. this.schedules = [...data.records]
  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. },
  223. methods: {
  224. formattedDate (date) {
  225. const thisDate = new Date(date)
  226. const month = thisDate.toLocaleString('en-us', { month: 'short' })
  227. const year = ((thisDate.getFullYear() !== new Date().getFullYear()) ? ', ' + thisDate.getFullYear() : '')
  228. return month + ' ' + thisDate.getDate() + year
  229. },
  230. daysList (days) {
  231. const list = days.split(', ').map((day) => {
  232. const name = day.toLowerCase().slice(0, -1)
  233. let num
  234. switch (name) {
  235. case 'su':
  236. num = 0
  237. break
  238. case 'mo':
  239. num = 1
  240. break
  241. case 'tu':
  242. num = 2
  243. break
  244. case 'we':
  245. num = 3
  246. break
  247. case 'th':
  248. num = 4
  249. break
  250. case 'fr':
  251. num = 5
  252. break
  253. case 'sa':
  254. num = 6
  255. break
  256. default:
  257. break
  258. }
  259. return {
  260. name,
  261. num
  262. }
  263. })
  264. return list.sort(function (a, b) {
  265. return a.num - b.num
  266. })
  267. },
  268. goBack () {
  269. this.$router.go(-1)
  270. },
  271. // book2 (schedule) {
  272. // if (schedule.fields.IsExpedia[0] === 'true') {
  273. // this.startingDate = new Date()
  274. // this.selectedSchedule = schedule.fields
  275. // this.selectedDays = this.daysList(schedule.fields.Frequency_Convert)
  276. // } else {
  277. // window.open(schedule.fields['BookingLink (from Carriers_Alaska)'][0])
  278. // }
  279. // },
  280. book () {
  281. // add tracker
  282. window.open(`https://www.anrdoezrs.net/links/100449149/type/am/https://www.expedia.com/go/flight/search/oneway/${this.$route.params.departure}/${this.$route.params.departure}?langid=1033&FromAirport=${this.selectedSchedule.fields.Origin_IATA}&FromTime=${this.selectedSchedule.fields.Departure_TimeL}&ToTime=${this.selectedSchedule.fields.Arrival_TimeL}&ToAirport=${this.selectedSchedule.fields.Destination_IATA}&Class=3&NumAdult=${this.numAdult}&NumChild=${this.numChild}`)
  283. },
  284. goToCarrier () {
  285. // add tracker
  286. window.open(this.selectedSchedule.fields['BookingLink (from Carriers_Alaska)'][0])
  287. },
  288. clearSelectedSchedule () {
  289. // console.log('clear')
  290. this.startingDate = ''
  291. this.selectedSchedule = {}
  292. },
  293. async fetchSingleAirport (iata, isOrig) {
  294. const airportFetchFilterFormula =
  295. isOrig
  296. ? `AND(Is_Origin=1,{IsCurrent-AsOrigin}="Yes",Airport_IATA="${iata}")`
  297. : `AND(Is_Destination=1,{IsCurrent-AsDest}="Yes",Airport_IATA="${iata}")`
  298. const response = await fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Airports_IATA?filterByFormula=${airportFetchFilterFormula}`, {
  299. method: 'GET',
  300. headers: {
  301. 'Content-Type': 'application/x-www-form-urlencoded',
  302. Authorization: 'Bearer keyJ2ht64ZSN57AG1'
  303. }
  304. })
  305. const mapData = await response.json()
  306. const thisAirport = {
  307. iata: mapData.records[0].fields.Airport_IATA,
  308. lat: mapData.records[0].fields.Latitude_Deg,
  309. long: mapData.records[0].fields.Longitude_Deg,
  310. icon: mapData.records[0].fields.Icon_Url,
  311. name: mapData.records[0].fields.Airport_Name,
  312. municipality: mapData.records[0].fields.Municipality,
  313. type: mapData.records[0].fields.Type,
  314. search: mapData.records[0].fields.Search_Field
  315. }
  316. switch (isOrig) {
  317. case true:
  318. this.selectedOrig = { ...thisAirport }
  319. break
  320. case false:
  321. this.selectedDest = { ...thisAirport }
  322. break
  323. default:
  324. break
  325. }
  326. }
  327. }
  328. }
  329. </script>
  330. <style lang="scss">
  331. .schedule-wrapper {
  332. display: flex;
  333. flex-direction: column;
  334. align-items: center;
  335. }
  336. .schedule-grid {
  337. display: flex;
  338. flex-direction: column;
  339. gap: 1rem;
  340. margin: 2rem 2rem 2rem 2rem;
  341. }
  342. .grid__row {
  343. display: flex;
  344. flex-direction: row;
  345. align-items: center;
  346. gap: 1rem;
  347. padding: 1rem 1rem 1rem 4rem;
  348. flex-wrap: wrap;
  349. border-radius: 2rem;
  350. }
  351. .grid__row:nth-child(odd) {
  352. background: #eee;
  353. }
  354. label {
  355. font-size: 0.75rem;
  356. font-weight: 700;
  357. color: white;
  358. display: block;
  359. }
  360. .field__content {
  361. font-size: 1.5rem;
  362. }
  363. .dow-grid {
  364. width: 200px;
  365. display: grid;
  366. grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
  367. gap: 0.5rem;
  368. }
  369. .dow-grid__day {
  370. font-size: 0.8rem;
  371. border: 1px solid #ccc !important;
  372. display: flex;
  373. align-items: center;
  374. justify-content: center;
  375. border-radius: 3px !important;
  376. text-transform: capitalize;
  377. }
  378. .day--su {
  379. grid-column: 1 / span 1;
  380. }
  381. .day--mo {
  382. grid-column: 2 / span 1;
  383. }
  384. .day--tu {
  385. grid-column: 3 / span 1;
  386. }
  387. .day--we {
  388. grid-column: 4 / span 1;
  389. }
  390. .day--th {
  391. grid-column: 5 / span 1;
  392. }
  393. .day--fr {
  394. grid-column: 6 / span 1;
  395. }
  396. .day--sa {
  397. grid-column: 7 / span 1;
  398. }
  399. .row__trip {
  400. display: flex;
  401. align-items: center;
  402. }
  403. .row__trip svg {
  404. margin-bottom: -0.5rem;
  405. width: 3.5rem;
  406. height: 3.5rem;
  407. margin-left: -1rem;
  408. margin-right: -1rem;
  409. }
  410. .shape--trip {
  411. height: 0.4rem;
  412. width: 2rem;
  413. background: #007fff;
  414. display: block;
  415. margin-top: 1rem;
  416. position: relative;
  417. }
  418. .shape--trip::before {
  419. height: 0.8rem;
  420. width: 0.8rem;
  421. background: #fff;
  422. border: 0.02rem solid #007fff;
  423. border-radius: 999px;
  424. content: "";
  425. position: absolute;
  426. top: -0.15rem;
  427. left: -0.5rem;
  428. }
  429. .shape--trip::after {
  430. height: 0.8rem;
  431. width: 0.8rem;
  432. background: #007fff;
  433. border: 0.02rem solid #007fff;
  434. border-radius: 999px;
  435. content: "";
  436. position: absolute;
  437. top: -0.15rem;
  438. right: -0.5rem;
  439. }
  440. h1 {
  441. font-size: 3rem;
  442. line-height: 0.85;
  443. }
  444. h2 {
  445. font-size: 2rem;
  446. line-height: 1;
  447. }
  448. label.text--muni {
  449. color: #007fff;
  450. line-height: 1;
  451. padding-left: 0.3rem;
  452. }
  453. span.text--flight-num {
  454. font-weight: 700;
  455. margin-right: 0.5rem;
  456. }
  457. span.text--dow {
  458. font-weight: 300;
  459. letter-spacing: -0.1em;
  460. }
  461. span.badge--avail {
  462. border-radius: 999px;
  463. border: 1px solid gray;
  464. color: gray;
  465. padding: 0.2em 0.8em;
  466. line-height: 1;
  467. font-size: 0.8rem;
  468. display: block;
  469. width: fit-content;
  470. }
  471. .radio span {
  472. border-radius: 999px;
  473. border: 3px solid #007fff;
  474. background-color: white;
  475. height: 2rem;
  476. width: 2rem;
  477. display: flex;
  478. position: relative;
  479. transition: all 0.3s;
  480. align-items: center;
  481. justify-content: center;
  482. }
  483. .radio span svg {
  484. transition: all 0.3s;
  485. stroke: #eee;
  486. }
  487. .radio span:hover {
  488. background: #007fff;
  489. }
  490. .radio input:checked + span {
  491. background: #007fff;
  492. }
  493. .radio input:checked + span svg {
  494. stroke: white;
  495. }
  496. .radio input {
  497. display: none;
  498. }
  499. .row__info {
  500. min-width: fit-content;
  501. }
  502. .row__check {
  503. margin-left: -3rem;
  504. }
  505. .book-container {
  506. display: flex;
  507. flex-direction: column;
  508. justify-content: stretch;
  509. gap: 1rem;
  510. }
  511. .book-container label {
  512. text-transform: uppercase;
  513. font-size: 1rem;
  514. font-weight: 300;
  515. }
  516. </style>