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.

613 lines
15 KiB

3 years ago
3 years ago
3 years ago
  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. />
  158. </div>
  159. <div>
  160. <label for="">How Many Children?</label>
  161. <v-select
  162. v-model="numChild"
  163. :options="[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
  164. />
  165. </div>
  166. <button class="btn btn--primary" @click="book">
  167. book now!
  168. </button>
  169. </div>
  170. </div>
  171. </div>
  172. </main>
  173. </div>
  174. </template>
  175. <script>
  176. export default {
  177. data () {
  178. return {
  179. schedules: [],
  180. startingDate: '',
  181. selectedSchedule: {},
  182. selectedDays: [],
  183. selectedOrig: {},
  184. selectedDest: {},
  185. numAdult: 1,
  186. numChild: 0
  187. }
  188. },
  189. async fetch () {
  190. // console.log(this.$route.params.o)
  191. const today = new Date().toLocaleDateString('en-CA')
  192. // function addDays (date, days) {
  193. // const result = new Date(date)
  194. // result.setDate(result.getDate() + days)
  195. // return result
  196. // }
  197. // const tomorrow = addDays(new Date(), 1).toLocaleDateString('en-CA')
  198. 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)`
  199. // console.log(scheduleFetchFilterFormula)
  200. const scheduleFetchSort = '&sort[0][field]=Departure_TimeL&sort[0][direction]=asc'
  201. const response = await fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Schedule?filterByFormula=${scheduleFetchFilterFormula}${scheduleFetchSort}`, {
  202. method: 'GET',
  203. headers: {
  204. 'Content-Type': 'application/x-www-form-urlencoded',
  205. Authorization: 'Bearer keyJ2ht64ZSN57AG1'
  206. }
  207. })
  208. const data = await response.json()
  209. // console.log(data)
  210. this.schedules = [...data.records]
  211. },
  212. created () {
  213. // console.log(this.$route.path)
  214. if (this.$route && this.$route.params && this.$route.params.o) {
  215. this.fetchSingleAirport(this.$route.params.o, true)
  216. }
  217. if (this.$route && this.$route.params && this.$route.params.d) {
  218. this.fetchSingleAirport(this.$route.params.d, false)
  219. }
  220. },
  221. methods: {
  222. formattedDate (date) {
  223. const thisDate = new Date(date)
  224. const month = thisDate.toLocaleString('en-us', { month: 'short' })
  225. const year = ((thisDate.getFullYear() !== new Date().getFullYear()) ? ', ' + thisDate.getFullYear() : '')
  226. return month + ' ' + thisDate.getUTCDate() + year
  227. },
  228. daysList (days) {
  229. const list = days.split(', ').map((day) => {
  230. const name = day.toLowerCase().slice(0, -1)
  231. let num
  232. switch (name) {
  233. case 'su':
  234. num = 0
  235. break
  236. case 'mo':
  237. num = 1
  238. break
  239. case 'tu':
  240. num = 2
  241. break
  242. case 'we':
  243. num = 3
  244. break
  245. case 'th':
  246. num = 4
  247. break
  248. case 'fr':
  249. num = 5
  250. break
  251. case 'sa':
  252. num = 6
  253. break
  254. default:
  255. break
  256. }
  257. return {
  258. name,
  259. num
  260. }
  261. })
  262. return list.sort(function (a, b) {
  263. return a.num - b.num
  264. })
  265. },
  266. goBack () {
  267. this.$router.go(-1)
  268. },
  269. // book2 (schedule) {
  270. // if (schedule.fields.IsExpedia[0] === 'true') {
  271. // this.startingDate = new Date()
  272. // this.selectedSchedule = schedule.fields
  273. // this.selectedDays = this.daysList(schedule.fields.Frequency_Convert)
  274. // } else {
  275. // window.open(schedule.fields['BookingLink (from Carriers_Alaska)'][0])
  276. // }
  277. // },
  278. book () {
  279. // add tracker
  280. 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}`)
  281. },
  282. goToCarrier () {
  283. // add tracker
  284. window.open(this.selectedSchedule.fields['BookingLink (from Carriers_Alaska)'][0])
  285. },
  286. clearSelectedSchedule () {
  287. // console.log('clear')
  288. this.startingDate = ''
  289. this.selectedSchedule = {}
  290. },
  291. async fetchSingleAirport (iata, isOrig) {
  292. const airportFetchFilterFormula =
  293. isOrig
  294. ? `AND(Is_Origin=1,{IsCurrent-AsOrigin}="Yes",Airport_IATA="${iata}")`
  295. : `AND(Is_Destination=1,{IsCurrent-AsDest}="Yes",Airport_IATA="${iata}")`
  296. const response = await fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Airports_IATA?filterByFormula=${airportFetchFilterFormula}`, {
  297. method: 'GET',
  298. headers: {
  299. 'Content-Type': 'application/x-www-form-urlencoded',
  300. Authorization: 'Bearer keyJ2ht64ZSN57AG1'
  301. }
  302. })
  303. const mapData = await response.json()
  304. const thisAirport = {
  305. iata: mapData.records[0].fields.Airport_IATA,
  306. lat: mapData.records[0].fields.Latitude_Deg,
  307. long: mapData.records[0].fields.Longitude_Deg,
  308. icon: mapData.records[0].fields.Icon_Url,
  309. name: mapData.records[0].fields.Airport_Name,
  310. municipality: mapData.records[0].fields.Municipality,
  311. type: mapData.records[0].fields.Type,
  312. search: mapData.records[0].fields.Search_Field
  313. }
  314. switch (isOrig) {
  315. case true:
  316. this.selectedOrig = { ...thisAirport }
  317. break
  318. case false:
  319. this.selectedDest = { ...thisAirport }
  320. break
  321. default:
  322. break
  323. }
  324. }
  325. }
  326. }
  327. </script>
  328. <style lang="scss" scoped>
  329. .main-with-header {
  330. margin-top: clamp(20px + 1.75rem, 4rem + 1.75rem, 50px + 1.75rem);
  331. }
  332. .schedule-wrapper {
  333. display: flex;
  334. flex-direction: column;
  335. align-items: center;
  336. }
  337. .schedule-grid {
  338. display: flex;
  339. flex-direction: column;
  340. gap: 1rem;
  341. margin: 2rem 2rem 2rem 2rem;
  342. }
  343. .grid__row {
  344. display: flex;
  345. flex-direction: row;
  346. align-items: center;
  347. gap: 1rem;
  348. padding: 1rem 1rem 1rem 4rem;
  349. flex-wrap: wrap;
  350. border-radius: 2rem;
  351. }
  352. .grid__row:nth-child(odd) {
  353. background: #eee;
  354. }
  355. label {
  356. font-size: 0.75rem;
  357. font-weight: 700;
  358. color: white;
  359. display: block;
  360. }
  361. .field__content {
  362. font-size: 1.5rem;
  363. }
  364. .dow-grid {
  365. width: 200px;
  366. display: grid;
  367. grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
  368. gap: 0.5rem;
  369. }
  370. .dow-grid__day {
  371. font-size: 0.8rem;
  372. border: 1px solid #ccc !important;
  373. display: flex;
  374. align-items: center;
  375. justify-content: center;
  376. border-radius: 3px !important;
  377. text-transform: capitalize;
  378. }
  379. .day--su {
  380. grid-column: 1 / span 1;
  381. }
  382. .day--mo {
  383. grid-column: 2 / span 1;
  384. }
  385. .day--tu {
  386. grid-column: 3 / span 1;
  387. }
  388. .day--we {
  389. grid-column: 4 / span 1;
  390. }
  391. .day--th {
  392. grid-column: 5 / span 1;
  393. }
  394. .day--fr {
  395. grid-column: 6 / span 1;
  396. }
  397. .day--sa {
  398. grid-column: 7 / span 1;
  399. }
  400. .row__trip {
  401. display: flex;
  402. align-items: center;
  403. }
  404. .row__trip svg {
  405. margin-bottom: -0.5rem;
  406. width: 3.5rem;
  407. height: 3.5rem;
  408. margin-left: -1rem;
  409. margin-right: -1rem;
  410. }
  411. .shape--trip {
  412. height: 0.4rem;
  413. width: 2rem;
  414. background: #007fff;
  415. display: block;
  416. margin-top: 1rem;
  417. position: relative;
  418. }
  419. .shape--trip::before {
  420. height: 0.8rem;
  421. width: 0.8rem;
  422. background: #fff;
  423. border: 0.02rem solid #007fff;
  424. border-radius: 999px;
  425. content: "";
  426. position: absolute;
  427. top: -0.15rem;
  428. left: -0.5rem;
  429. }
  430. .shape--trip::after {
  431. height: 0.8rem;
  432. width: 0.8rem;
  433. background: #007fff;
  434. border: 0.02rem solid #007fff;
  435. border-radius: 999px;
  436. content: "";
  437. position: absolute;
  438. top: -0.15rem;
  439. right: -0.5rem;
  440. }
  441. h1 {
  442. font-size: 3rem;
  443. line-height: 0.85;
  444. }
  445. h2 {
  446. font-size: 2rem;
  447. line-height: 1;
  448. }
  449. label.text--muni {
  450. color: #007fff;
  451. line-height: 1;
  452. padding-left: 0.3rem;
  453. }
  454. span.text--flight-num {
  455. font-weight: 700;
  456. margin-right: 0.5rem;
  457. }
  458. span.text--dow {
  459. font-weight: 300;
  460. font-size: 0.8rem;
  461. color: #007fff;
  462. }
  463. span.badge--avail {
  464. border-radius: 999px;
  465. border: 1px solid gray;
  466. color: gray;
  467. padding: 0.2em 0.8em;
  468. line-height: 1;
  469. font-size: 0.8rem;
  470. display: block;
  471. width: fit-content;
  472. }
  473. .radio span {
  474. border-radius: 999px;
  475. border: 3px solid #007fff;
  476. background-color: white;
  477. height: 2rem;
  478. width: 2rem;
  479. display: flex;
  480. position: relative;
  481. transition: all 0.3s;
  482. align-items: center;
  483. justify-content: center;
  484. }
  485. .radio span svg {
  486. transition: all 0.3s;
  487. stroke: #eee;
  488. }
  489. .radio span:hover {
  490. background: #007fff;
  491. }
  492. .radio input:checked + span {
  493. background: #007fff;
  494. }
  495. .radio input:checked + span svg {
  496. stroke: white;
  497. }
  498. .radio input {
  499. display: none;
  500. }
  501. .row__info {
  502. min-width: fit-content;
  503. }
  504. .row__check {
  505. margin-left: -3rem;
  506. }
  507. .book-container {
  508. display: flex;
  509. flex-direction: column;
  510. justify-content: stretch;
  511. gap: 1rem;
  512. }
  513. .book-container label {
  514. text-transform: uppercase;
  515. font-size: 1rem;
  516. font-weight: 300;
  517. }
  518. </style>
  519. <style>
  520. .flyout .v-select {
  521. font-size: 1.5rem;
  522. }
  523. .flyout .vs__dropdown-menu {
  524. display: flex;
  525. flex-direction: row;
  526. flex-wrap: wrap;
  527. }
  528. .flyout .vs__dropdown-menu li {
  529. border-radius: 999px;
  530. transition: 0.3 all;
  531. }
  532. .flyout .vs__dropdown-menu li:hover {
  533. border-radius: 999px;
  534. background-color: #007fff;
  535. }
  536. .flyout .vs__dropdown-toggle {
  537. background: #eee;
  538. border-radius: 1.2rem 1.2rem 1.2rem 1.2rem;
  539. }
  540. .flyout .vs--open .vs__dropdown-toggle {
  541. background: #eee;
  542. border-radius: 1.2rem 1.2rem 0 0;
  543. }
  544. </style>