Browse Source

added flights page, finished workflow, everything works, needs some style tweaks

master
Your Name 3 years ago
parent
commit
0bcfeb5646
14 changed files with 25748 additions and 1764 deletions
  1. +0
    -0
      assets/css/main.css
  2. +1
    -0
      components/AirportPicker.vue
  3. +88
    -0
      components/Calendar.vue
  4. +105
    -0
      components/DatePicker.vue
  5. +52
    -0
      components/FlightsFlyout.vue
  6. +29
    -0
      components/Logo.vue
  7. +36
    -28
      components/Map.vue
  8. +0
    -11
      components/NuxtLogo.vue
  9. +30
    -3
      nuxt.config.js
  10. +23355
    -164
      package-lock.json
  11. +6
    -2
      package.json
  12. +233
    -0
      pages/flights.vue
  13. +184
    -22
      pages/go.vue
  14. +1629
    -1534
      yarn.lock

+ 0
- 0
assets/css/main.css View File


+ 1
- 0
components/AirportPicker.vue View File

@ -156,5 +156,6 @@ html {
.picker-item__iata { .picker-item__iata {
font-size: 3rem; font-size: 3rem;
margin-right: 1rem;
} }
</style> </style>

+ 88
- 0
components/Calendar.vue View File

@ -0,0 +1,88 @@
<template>
<div>
<h1>
{{ new Date(date).toLocaleString("default", { month: "long" }) }}
</h1>
<ul class="calendar">
<li
v-for="dow in daysOfWeek"
:key="dow"
:class="['calendar__day', 'calendar__day--dow']"
>
{{ dow }}
</li>
<li
v-for="index in getDayOfWeekOffset(date)"
:key="index + 'o'"
class="calendar__day"
>
<span class="day__date--blank" />
</li>
<li
v-for="day in getDaysInMonth(date)"
:key="day.toString()"
class="calendar__day"
>
<span class="day__date"> {{ day.getDate() }} </span>
<button
v-if="selectedDays.find( sched => sched['num'] == day.getDay() )"
@click="book(day)"
>
book
</button>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
date: {
type: [Date],
default () {
return new Date()
}
},
selectedDays: {
type: [Array],
default () {
return []
}
}
},
data () {
return {
daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']
}
},
methods: {
getDayOfWeekOffset (thisDate) {
const dow = new Date(thisDate.getFullYear(), thisDate.getMonth(), 1).getDay()
return dow
},
getDaysInMonth (thisDate) {
const daysInMonth = new Date(thisDate.getFullYear(), thisDate.getMonth() + 1, 0).getDate()
const days = []
for (let index = 1; index < daysInMonth + 1; index++) {
days.push(new Date(thisDate.getFullYear(), thisDate.getMonth(), index))
}
return days
},
book (thisDate) {
this.$emit('book', thisDate)
}
}
}
</script>
<style>
.calendar {
display: grid;
width: 300px;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr 1fr 1fr 1fr;
}
</style>

+ 105
- 0
components/DatePicker.vue View File

@ -0,0 +1,105 @@
<template>
<div class="calendar-wrapper">
<div>
<label for=""> Local Airline </label>
<h1>
{{ selectedSchedule.Carrier_Name }}
</h1>
</div>
<div>
<label for=""> Departs </label>
<div>
{{ selectedSchedule.Departure_TimeL }}
</div>
</div>
<div>
<label for=""> Arrives </label>
<div>
{{ selectedSchedule.Arrival_TimeL }}
</div>
</div>
<div>
<label for=""> Available Until </label>
<div>
{{ selectedSchedule.Effective_End }}
</div>
</div>
<div>
<label for="">How Many Adults?</label>
<v-select
v-model="numAdult"
:options="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
/>
</div>
<div>
<label for="">How Many Children?</label>
<v-select
v-model="numChild"
:options="[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
/>
</div>
<Calendar
v-for="date in startingDates"
:key="date.toString()"
:date="new Date(date)"
:selected-days="selectedDays"
@book="book"
/>
</div>
</template>
<script>
export default {
props: {
startingDate: {
type: [Date],
default () {
return new Date()
}
},
selectedSchedule: {
type: [Object],
default () {
return {}
}
},
selectedDays: {
type: [Array],
default () {
return []
}
}
},
data () {
return {
numberOfMonths: 4,
numAdult: 1,
numChild: 0
}
},
computed: {
startingDates () {
const listOfDates = []
for (let index = 0; index < this.numberOfMonths; index++) {
const thisDate = new Date(this.startingDate.getFullYear(), this.startingDate.getMonth() + index, 1)
listOfDates.push(thisDate)
}
return listOfDates
}
},
methods: {
book (thisDate) {
window.open(`https://www.anrdoezrs.net/links/100449149/type/am/https://www.expedia.com/go/flight/search/oneway/${this.formatDate(thisDate)}/${this.formatDate(thisDate)}?langid=1033&FromAirport=${this.selectedSchedule.Origin_IATA}&FromTime=${this.selectedSchedule.Departure_TimeL}&ToTime=${this.selectedSchedule.Arrival_TimeL}&ToAirport=${this.selectedSchedule.Destination_IATA}&Class=3&NumAdult=${this.numAdult}&NumChild=${this.numChild}`)
},
formatDate (thisDate) {
return thisDate.toLocaleDateString('en-CA')
}
}
}
</script>
<style>
.calendar-wrapper {
color: black;
}
</style>

+ 52
- 0
components/FlightsFlyout.vue View File

@ -0,0 +1,52 @@
<template>
<div class="flyout flyout--book">
<NuxtLink
:to="'/flights/' + selectedOrig.iata + '/' + selectedDest.iata"
:class="['btn btn--primary btn--large',{'btn--empty': !selectedOrig.iata || !selectedDest.iata}]"
>
<span :class="['btn__content']">
see fights from <strong>{{ selectedOrig.municipality }}</strong> to <strong>{{ selectedDest.municipality }}</strong>
</span>
</NuxtLink>
</div>
</template>
<script>
export default {
props: {
selectedOrig: {
type: [Object],
default () {
return {}
}
},
selectedDest: {
type: [Object],
default () {
return {}
}
}
}
}
</script>
<style>
.flyout {
background: white;
border-radius: 3rem 3rem 0 0;
padding: 3rem;
pointer-events: all;
}
.btn__content {
transition: width 0s 5s;
}
.btn.btn--empty {
background: lightgray;
}
.btn.btn--large {
font-size: 2rem;
}
</style>

+ 29
- 0
components/Logo.vue View File

@ -0,0 +1,29 @@
<template>
<svg id="flylocal-logo" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1107.17 373.96">
<path class="cls-1" d="M.1,242.9a55.68,55.68,0,0,0,111.24-2.42,54.61,54.61,0,0,0-.44-8.21l-.05,0c-3.86-37.55-29.44-82.66-32.6-90.5-6.84,5.84-48.77,34.79-68.78,66.73l-.05,0A55.95,55.95,0,0,0,.1,242.9Z" /><path class="cls-2" d="M141.36,52.27A55.68,55.68,0,0,0,33.8,35.74a57.55,57.55,0,0,0-2.27,7.42,55.82,55.82,0,0,0-1.41,11.53,54.75,54.75,0,0,0,.45,8.22h.05c3.86,37.55,29.79,83.67,32.59,90.5,6.2-4.83,48.77-34.79,68.79-66.73h.05a55.89,55.89,0,0,0,9.31-34.41ZM85.88,56a13,13,0,1,1,15.59-9.74A13,13,0,0,1,85.88,56Z" /><path class="cls-3" d="M156.34,293.15l34.58-173.28H166.33l5.36-26.81h24.6l5.18-25.71q6.83-34,24.5-50.3C242.74,1.6,268.46-2.8,290,3.55c1.12.33,2.24.69,3.34,1.08-.58,2.8-7.2,29.89-7,30-7.63-2.37-16.2-3.35-24-1.43C246.75,37,241.44,53.31,238.64,67.35l-5.18,25.71h32.73l-5.36,26.81H228.09L193.51,293.15Z" /><path class="cls-3" d="M290.8,293.15h-37L311.51,4.66h37Z" /><path class="cls-3" d="M393,224.17l.19,11.1,1.11.18,64-142.39H498.6L385.79,324.22a140.35,140.35,0,0,1-25.06,35.13Q346.21,374,324.58,374a47.75,47.75,0,0,1-9.34-1q-5.08-1-9.89-2.31L315,342.89q2,.38,5.27.74a46.84,46.84,0,0,0,5.09.37q9.44,0,17.85-9.71a91.86,91.86,0,0,0,14-21.72l10.17-20L348.44,93.06h40.31Z" /><path class="cls-3" d="M511.33,293.15h-37L532,4.66h37Z" /><path class="cls-3" d="M553.53,180.53q6.47-41.43,30.14-66.3t58.81-24.87q33.09,0,49.56,25.89t10.17,65.28l-4.07,25.52q-6.66,41.61-30.24,66.29T609.56,297q-33.46,0-50-25.7t-10.08-65.28Zm32.91,25.52q-4.62,28.48,1.85,45.49t25.71,17q17.75,0,30.14-17.75T661,206.05L665,180.53q4.44-27.92-1.94-45.12t-25.25-17.2q-18.3,0-30.69,17.75t-16.65,44.57Z" /><path class="cls-3" d="M779.81,268.55q12.57,0,23.49-10.81t13.87-29h33.1l.37,1.11q-4.26,29.41-26,48.27T775.37,297q-34.39,0-50-25.61t-9.52-64.26l4.44-27.56q6.47-40.48,29.31-65.37t58.9-24.87q28.1,0,43.45,20.53t10.17,53.26H827.52q3.33-20.72-3.14-32.83t-20.53-12.11q-19.23,0-30.79,17.57T757.44,179.6L753,207.16q-4.44,28.11,1,44.75T779.81,268.55Z" /><path class="cls-3" d="M956.91,293.15q-.37-7.39-.37-13.69a101.53,101.53,0,0,1,.74-12.39,93.25,93.25,0,0,1-23.95,21.64Q919.92,297,905.68,297q-22.56,0-33.93-16.27t-6-42.53q6.1-30.53,27.46-46T949,176.83h25.89l4.06-20.53q3.71-19.41-1.57-29.12t-19.51-9.71q-11.64,0-21.45,9.34a41.72,41.72,0,0,0-12.39,23l-35.32-.18-.37-1.11q4.07-24.6,25.8-41.89t51.68-17.29q26.64,0,41.61,17.75t8.69,49.56l-18.67,92.84a183.7,183.7,0,0,0-3.15,22.56,133.93,133.93,0,0,0,.19,21.08Zm-37-25q11.28,0,23-7.76a63.41,63.41,0,0,0,18.95-19.24L970,200.13H943.78q-14.81,0-26.26,11.28A49.81,49.81,0,0,0,903.1,238q-3,14.43,1.29,22.28T919.92,268.18Z" /><path class="cls-3" d="M1049.48,293.15h-37l57.7-288.49h37Z" />
</svg>
</template>
<style>
#flylocal-logo {
height: 4rem;
}
.cls-1,
.cls-2 {
fill: #2f80ed;
}
.cls-1 {
opacity: 0.57;
}
.cls-3 {
fill: #fff;
transition: all 0.3s;
}
.nav-is-showing .cls-3 {
fill: #000;
}
</style>

+ 36
- 28
components/Map.vue View File

@ -118,7 +118,7 @@
</div> </div>
</l-popup> </l-popup>
</l-marker> </l-marker>
<!-- <l-polyline :lat-lngs="polyline.latlngs" :color="polyline.color" /> -->
<l-polyline v-if="selectedOrigNorm.key && selectedDestNorm.key" :lat-lngs="[selectedOrigNorm['lat-lng'],selectedDestNorm['lat-lng']]" color="#007fff" />
<!-- <l-tile-layer v-bind="tileLayer" :url="mapBoxUrl" /> --> <!-- <l-tile-layer v-bind="tileLayer" :url="mapBoxUrl" /> -->
<l-tile-layer :url="osmUrl" :attribution="osmAttribution" /> <l-tile-layer :url="osmUrl" :attribution="osmAttribution" />
</l-map> </l-map>
@ -160,6 +160,7 @@ export default {
zoom: 3, zoom: 3,
minZoom: 3, minZoom: 3,
maxZoom: 13, maxZoom: 13,
zoomSnap: 0.1,
center: { center: {
lat: 63.8333, lat: 63.8333,
lng: -152 lng: -152
@ -216,22 +217,40 @@ export default {
} }
}, },
updated () { updated () {
/* selected none */
if (!this.selectedOrigNorm.key && !this.selectedDestNorm.key) {
const o = this.airportsOrig.map((m) => { return [m.lat, m.long] })
this.$refs.flMap.mapObject.fitBounds([...o])
}
/* selected org */
if (this.selectedOrigNorm.key && !this.selectedDestNorm.key) {
const so = [this.selectedOrig.lat, this.selectedOrig.long]
const d = this.airportsDest.map((m) => { return [m.lat, m.long] })
this.$refs.flMap.mapObject.fitBounds([so, ...d])
}
/* selected both */
if (this.selectedOrigNorm.key && this.selectedDestNorm.key) {
const so = [this.selectedOrig.lat, this.selectedOrig.long]
const sd = [this.selectedDest.lat, this.selectedDest.long]
this.$refs.flMap.mapObject.fitBounds([so, sd])
if (this.$refs.flMap && this.$refs.flMap.mapObject) {
/* selected none */
if (!this.selectedOrigNorm.key && !this.selectedDestNorm.key && this.airportsOrig.length > 0) {
const o = this.airportsOrig.map((m) => { return [m.lat, m.long] })
// console.log('origins:')
// console.log(o)
if (o) {
this.$refs.flMap.mapObject.fitBounds([...o], { padding: [50, 70] })
}
}
/* selected org */
if (this.selectedOrigNorm.key && !this.selectedDestNorm.key) {
const so = [this.selectedOrig.lat, this.selectedOrig.long]
const d = this.airportsDest.map((m) => { return [m.lat, m.long] })
// console.log('so:')
// console.log(so)
// console.log('dests:')
// console.log(d)
if (so && d) {
this.$refs.flMap.mapObject.fitBounds([so, ...d], { padding: [50, 70] })
}
}
/* selected both */
if (this.selectedOrigNorm.key && this.selectedDestNorm.key) {
const so = [this.selectedOrig.lat, this.selectedOrig.long]
const sd = [this.selectedDest.lat, this.selectedDest.long]
// console.log('so:')
// console.log(so)
// console.log('sd:')
// console.log(sd)
if (so && sd) {
this.$refs.flMap.mapObject.fitBounds([so, sd], { padding: [50, 70] })
}
}
} }
// const o = this.airportsOrig.map((m) => { return [m.lat, m.long] }) // const o = this.airportsOrig.map((m) => { return [m.lat, m.long] })
@ -240,17 +259,6 @@ export default {
// const sd = [this.selectedDest.lat, this.selectedDest.long] // const sd = [this.selectedDest.lat, this.selectedDest.long]
// this.$refs.flMap.mapObject.fitBounds([...o, ...d, ...so, ...sd]) // this.$refs.flMap.mapObject.fitBounds([...o, ...d, ...so, ...sd])
// this.$refs.flMap.mapObject.fitBounds([[40, -73], [5, -55]]) // this.$refs.flMap.mapObject.fitBounds([[40, -73], [5, -55]])
},
methods: {
onReady () {
// const o = this.airportsOrig.map((m) => { return [m.lat, m.long] })
// const d = this.airportsDest.map((m) => { return [m.lat, m.long] })
// const so = [this.selectedOrig.lat, this.selectedOrig.long]
// const sd = [this.selectedDest.lat, this.selectedDest.long]
// this.$refs.flMap.mapObject.fitBounds(...o, ...d, ...so, ...sd)
this.$refs.flMap.mapObject.fitBounds([[40, -73], [5, -55]])
}
} }
} }
</script> </script>

+ 0
- 11
components/NuxtLogo.vue View File

@ -1,11 +0,0 @@
<template>
<svg class="nuxt-logo" viewBox="0 0 45 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24.7203 29.704H41.1008C41.6211 29.7041 42.1322 29.5669 42.5828 29.3061C43.0334 29.0454 43.4075 28.6704 43.6675 28.2188C43.9275 27.7672 44.0643 27.2549 44.0641 26.7335C44.0639 26.2121 43.9266 25.6999 43.6662 25.2485L32.6655 6.15312C32.4055 5.70162 32.0315 5.32667 31.581 5.06598C31.1305 4.8053 30.6195 4.66805 30.0994 4.66805C29.5792 4.66805 29.0682 4.8053 28.6177 5.06598C28.1672 5.32667 27.7932 5.70162 27.5332 6.15312L24.7203 11.039L19.2208 1.48485C18.9606 1.03338 18.5864 0.658493 18.1358 0.397853C17.6852 0.137213 17.1741 0 16.6538 0C16.1336 0 15.6225 0.137213 15.1719 0.397853C14.7213 0.658493 14.3471 1.03338 14.0868 1.48485L0.397874 25.2485C0.137452 25.6999 0.000226653 26.2121 2.8053e-07 26.7335C-0.000226092 27.2549 0.136554 27.7672 0.396584 28.2188C0.656614 28.6704 1.03072 29.0454 1.48129 29.3061C1.93185 29.5669 2.44298 29.7041 2.96326 29.704H13.2456C17.3195 29.704 20.3239 27.9106 22.3912 24.4118L27.4102 15.7008L30.0986 11.039L38.1667 25.0422H27.4102L24.7203 29.704ZM13.0779 25.0374L5.9022 25.0358L16.6586 6.36589L22.0257 15.7008L18.4322 21.9401C17.0593 24.2103 15.4996 25.0374 13.0779 25.0374Z" fill="#00DC82" />
</svg>
</template>
<style>
.nuxt-logo {
height: 180px;
}
</style>

+ 30
- 3
nuxt.config.js View File

@ -15,6 +15,7 @@ export default {
// Global CSS: https://go.nuxtjs.dev/config-css // Global CSS: https://go.nuxtjs.dev/config-css
css: [ css: [
'~assets/css/main.css'
], ],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
@ -30,13 +31,23 @@ export default {
routes.push( routes.push(
{ {
name: 'go-orig-dest', name: 'go-orig-dest',
path: '/go/*/*',
path: '/go/:o/:d',
component: resolve(__dirname, 'pages/go.vue') component: resolve(__dirname, 'pages/go.vue')
}, },
{ {
name: 'go-orig', name: 'go-orig',
path: '/go/*',
path: '/go/:o',
component: resolve(__dirname, 'pages/go.vue') component: resolve(__dirname, 'pages/go.vue')
},
{
name: 'flights-orig-dest',
path: '/flights/:o/:d',
component: resolve(__dirname, 'pages/flights.vue')
},
{
name: 'flights-orig',
path: '/flights/:o',
component: resolve(__dirname, 'pages/flights.vue')
} }
) )
} }
@ -49,9 +60,17 @@ export default {
// https://go.nuxtjs.dev/stylelint // https://go.nuxtjs.dev/stylelint
'@nuxtjs/stylelint-module', '@nuxtjs/stylelint-module',
// https://go.nuxtjs.dev/tailwindcss // https://go.nuxtjs.dev/tailwindcss
'@nuxtjs/tailwindcss'
'@nuxtjs/tailwindcss',
'@nuxtjs/style-resources'
], ],
styleResources: {
scss: [
'~assets/scss/mixins.scss',
'~assets/scss/variables.scss'
]
},
// Modules: https://go.nuxtjs.dev/config-modules // Modules: https://go.nuxtjs.dev/config-modules
modules: [ modules: [
// https://go.nuxtjs.dev/axios // https://go.nuxtjs.dev/axios
@ -78,6 +97,14 @@ export default {
if (ctx.isDev) { if (ctx.isDev) {
config.devtool = ctx.isClient ? 'source-map' : 'inline-source-map' config.devtool = ctx.isClient ? 'source-map' : 'inline-source-map'
} }
},
loaders: {
sass: {
implementation: require('sass')
},
scss: {
implementation: require('sass')
}
} }
} }
} }

+ 23355
- 164
package-lock.json
File diff suppressed because it is too large
View File


+ 6
- 2
package.json View File

@ -14,19 +14,21 @@
"dependencies": { "dependencies": {
"@nuxtjs/axios": "^5.13.6", "@nuxtjs/axios": "^5.13.6",
"@nuxtjs/pwa": "^3.3.5", "@nuxtjs/pwa": "^3.3.5",
"@nuxtjs/style-resources": "^1.2.1",
"airtable": "^0.11.1", "airtable": "^0.11.1",
"autoprefixer": "^10.3.4", "autoprefixer": "^10.3.4",
"core-js": "^3.15.1", "core-js": "^3.15.1",
"fibers": "^5.0.0",
"leaflet": "^1.7.1", "leaflet": "^1.7.1",
"node-sass": "^6.0.1", "node-sass": "^6.0.1",
"nuxt": "^2.15.7", "nuxt": "^2.15.7",
"nuxt-leaflet": "^0.0.25", "nuxt-leaflet": "^0.0.25",
"nuxt-vue-select": "^0.1.4", "nuxt-vue-select": "^0.1.4",
"sass-loader": "^12.1.0",
"tailwindcss": "^2.2.15", "tailwindcss": "^2.2.15",
"vue-select": "^3.13.0", "vue-select": "^3.13.0",
"vue2-leaflet": "^2.7.1", "vue2-leaflet": "^2.7.1",
"vue2-leaflet-markercluster": "^3.1.0"
"vue2-leaflet-markercluster": "^3.1.0",
"vue2-leaflet-polylinedecorator": "^2.0.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.14.7", "@babel/eslint-parser": "^7.14.7",
@ -38,6 +40,8 @@
"eslint-plugin-nuxt": "^2.0.0", "eslint-plugin-nuxt": "^2.0.0",
"eslint-plugin-vue": "^7.12.1", "eslint-plugin-vue": "^7.12.1",
"postcss": "^8.3.6", "postcss": "^8.3.6",
"sass": "^1.41.0",
"sass-loader": "10.1.1",
"stylelint": "^13.13.1", "stylelint": "^13.13.1",
"stylelint-config-standard": "^22.0.0" "stylelint-config-standard": "^22.0.0"
} }

+ 233
- 0
pages/flights.vue View File

@ -0,0 +1,233 @@
<template>
<main>
<ul class="schedule-grid">
<li
v-for="schedule in schedules"
:key="schedule.fields.Record_ID"
class="grid__row"
>
<div class="grid__field">
<label> Local Airline </label>
<div class="field__content">
{{ schedule.fields.Carrier_Name }}
</div>
</div>
<div class="grid__field">
<label> Days of the Week </label>
<div class="field__content">
<div class="dow-grid">
<div
v-for="day in daysList(schedule.fields.Frequency_Convert)"
:key="day.name"
:class="['dow-grid__day', 'day--' + day.name]"
>
<span> {{ day.name }} </span>
</div>
</div>
</div>
</div>
<div class="grid__field">
<label> Departing </label>
<div class="field__content">
{{ schedule.fields.Departure_TimeL }}
</div>
</div>
<div class="grid__field">
<label> Arriving </label>
<div class="field__content">
{{ schedule.fields.Arrival_TimeL }}
</div>
</div>
<div class="grid__field">
<label> Available Until </label>
<div class="field__content">
{{ schedule.fields.Effective_End }}
</div>
</div>
<div class="grid__field">
<div class="field__content">
{{ schedule.fields.Price_USD }}
</div>
<button class="btn btn--primary" @click="book(schedule)">
{{ schedule.fields.IsExpedia[0] === 'true' ? 'book it!' : 'see flights' }}
</button>
</div>
</li>
</ul>
<div v-if="startingDate">
<DatePicker
:starting-date="startingDate"
:selected-schedule="selectedSchedule"
:selected-days="selectedDays"
/>
</div>
</main>
</template>
<script>
export default {
data () {
return {
schedules: [],
startingDate: '',
selectedSchedule: {},
selectedDays: []
}
},
async fetch () {
// console.log(this.$route.params.o)
const scheduleFetchFilterFormula = `AND(Is_Current="Yes",AND(Origin_IATA="${this.$route.params.o}",Destination_IATA="${this.$route.params.d}"))`
const scheduleFetchSort = '&sort[0][field]=DepartureTimeFormatted&sort[0][direction]=asc'
const response = await fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Schedule?filterByFormula=${scheduleFetchFilterFormula}${scheduleFetchSort}`, {
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: 'Bearer keyJ2ht64ZSN57AG1'
}
})
const data = await response.json()
// console.log(data)
this.schedules = [...data.records]
},
computed: {
},
methods: {
daysList (days) {
const list = days.split(', ').map((day) => {
const name = day.toLowerCase().slice(0, -1)
let num
switch (name) {
case 'su':
num = 0
break
case 'mo':
num = 1
break
case 'tu':
num = 2
break
case 'we':
num = 3
break
case 'th':
num = 4
break
case 'fr':
num = 5
break
case 'sa':
num = 6
break
default:
break
}
return {
name,
num
}
})
return list.sort(function (a, b) {
return a.num - b.num
})
},
book (schedule) {
if (schedule.fields.IsExpedia[0] === 'true') {
this.startingDate = new Date()
this.selectedSchedule = schedule.fields
this.selectedDays = this.daysList(schedule.fields.Frequency_Convert)
} else {
window.open(schedule.fields['BookingLink (from Carriers_Alaska)'][0])
}
}
}
}
</script>
<style lang="scss">
.schedule-grid {
display: flex;
flex-direction: column;
gap: 1rem;
}
.grid__row {
display: grid;
grid-template-columns: 2fr 2fr 1fr 1fr 2fr 1fr;
grid-template-rows: auto;
gap: 1rem;
}
// .grid__field {
// }
label {
font-size: 0.5rem;
font-weight: 700;
color: gray;
display: block;
}
.field__content {
font-size: 1.5rem;
}
.dow-grid {
width: 200px;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
gap: 0.5rem;
}
.dow-grid__day {
font-size: 0.8rem;
border: 1px solid #ccc !important;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px !important;
text-transform: capitalize;
}
.day--su {
grid-column: 1 / span 1;
}
.day--mo {
grid-column: 2 / span 1;
}
.day--tu {
grid-column: 3 / span 1;
}
.day--we {
grid-column: 4 / span 1;
}
.day--th {
grid-column: 5 / span 1;
}
.day--fr {
grid-column: 6 / span 1;
}
.day--sa {
grid-column: 7 / span 1;
}
</style>

+ 184
- 22
pages/go.vue View File

@ -1,12 +1,19 @@
<template> <template>
<div class="page-container">
<div :class="['page-container', { 'nav-is-showing': isPickerVisible }]">
<header> <header>
<div>logo</div>
<div>
<div class="header__col">
<Logo />
<div v-if="!selectedOrig.iata" class="logo-blurb">
from one of {{ airports_orig.length }} airports
</div>
<div v-if="selectedOrig.iata && !selectedDest.iata && airports_dest.length > 1" class="logo-blurb">
to one of {{ airports_dest.length }} beautiful destinations
</div>
</div>
<div class="header__col">
<div v-show="!isPickerVisible"> <div v-show="!isPickerVisible">
tap an airport on the map, or&nbsp;
<button class="btn btn--primary" @click="showPicker"> <button class="btn btn--primary" @click="showPicker">
search here
tap the map or search 🔎
</button> </button>
</div> </div>
<div v-show="isPickerVisible"> <div v-show="isPickerVisible">
@ -15,13 +22,21 @@
</button> </button>
</div> </div>
</div> </div>
<div class="header__col">
<button
class="btn btn--primary"
@click="showSidebar"
>
</button>
</div>
</header> </header>
<!-- <pre style="position: fixed; z-index: 500; right: 0; top: 2rem; font-size: 0.6rem; color: gray;"> <!-- <pre style="position: fixed; z-index: 500; right: 0; top: 2rem; font-size: 0.6rem; color: gray;">
{{ selectedOrig }} {{ selectedOrig }}
{{ selectedDest }} {{ selectedDest }}
</pre> --> </pre> -->
<main> <main>
<aside v-show="isPickerVisible" class="nav">
<nav :class="['nav', { 'nav--show': isPickerVisible },{ 'nav--hide': !isPickerVisible }]">
<AirportPicker <AirportPicker
:airports="airports_orig" :airports="airports_orig"
:selected-airport="selectedOrig" :selected-airport="selectedOrig"
@ -35,7 +50,7 @@
@select-airport="makeDestination" @select-airport="makeDestination"
@deselect-airport="clearDestination" @deselect-airport="clearDestination"
/> />
</aside>
</nav>
<Map <Map
:airports-orig="airports_orig" :airports-orig="airports_orig"
:airports-dest="airports_dest" :airports-dest="airports_dest"
@ -46,14 +61,27 @@
@clear-origin="clearOrigin" @clear-origin="clearOrigin"
@clear-destination="clearDestination" @clear-destination="clearDestination"
/> />
<aside
:class="['sidebar-container',{ 'sidebar-container--hide': !isSidebarVisible },{ 'sidebar-container--show': isSidebarVisible }]"
>
<div class="sidebar">
sidebar info
</div>
</aside>
<div :class="['flyout-container',{ 'flyout-container--hide': !selectedOrig.iata || !selectedDest.iata },{ 'flyout-container--show': selectedOrig.iata && selectedDest.iata }]">
<FlightsFlyout
:selected-orig="selectedOrig"
:selected-dest="selectedDest"
/>
</div>
</main> </main>
</div> </div>
</template> </template>
<script> <script>
import AirportPicker from '../components/AirportPicker.vue'
// import AirportPicker from '../components/AirportPicker.vue'
export default { export default {
components: { AirportPicker },
// components: { AirportPicker },
data () { data () {
return { return {
color: 'red', color: 'red',
@ -63,6 +91,7 @@ export default {
title: 'titly' title: 'titly'
} }
], ],
isSidebarVisible: false,
isPickerVisible: false, isPickerVisible: false,
airports_orig: [], airports_orig: [],
airports_dest: [], airports_dest: [],
@ -170,6 +199,21 @@ export default {
return array.join('') return array.join('')
} }
}, },
created () {
// console.log(this.$route.path)
if (this.$route && this.$route.params && this.$route.params.o) {
this.fetchSingleAirport(this.$route.params.o, true)
}
if (this.$route && this.$route.params && this.$route.params.d) {
this.fetchSingleAirport(this.$route.params.d, false)
}
},
// watch: {
// history (to, from) {
// console.log(`routed from ${from} to ${to}`)
// }
// },
methods: { methods: {
makeOrigin (airport) { makeOrigin (airport) {
// console.log(airport) // console.log(airport)
@ -178,24 +222,21 @@ export default {
this.selectedDest = { ...this.emptyAirport } this.selectedDest = { ...this.emptyAirport }
// this.$router.push('/go/' + airport.iata)
history.pushState( history.pushState(
{}, {},
null, null,
this.$route.path + encodeURIComponent(airport.iata)
'/go/' + encodeURIComponent(airport.iata)
) )
this.fetchDestinations(airport.iata, this)
this.fetchDestinations(airport.iata)
}, },
makeDestination (airport) { makeDestination (airport) {
this.selectedDest = { ...airport } this.selectedDest = { ...airport }
// this.$router.push('/go/' + this.selectedOrig.iata + '/' + airport.iata)
history.pushState( history.pushState(
{}, {},
null, null,
this.$route.path + encodeURIComponent(this.selectedOrig.iata) + '/' + encodeURIComponent(airport.iata)
'/go/' + encodeURIComponent(this.selectedOrig.iata) + '/' + encodeURIComponent(airport.iata)
) )
}, },
clearOrigin () { clearOrigin () {
@ -204,7 +245,7 @@ export default {
history.pushState( history.pushState(
{}, {},
null, null,
this.$route.path
'/go/'
) )
}, },
clearDestination () { clearDestination () {
@ -212,7 +253,7 @@ export default {
history.pushState( history.pushState(
{}, {},
null, null,
this.$route.path + encodeURIComponent(this.selectedOrig.iata)
'/go/' + encodeURIComponent(this.selectedOrig.iata)
) )
}, },
async fetchDestinations (iata) { async fetchDestinations (iata) {
@ -231,7 +272,6 @@ export default {
// console.log('mapData.records length: ' + mapData.records.length) // console.log('mapData.records length: ' + mapData.records.length)
// console.log('this.airports_dest length (before): ' + this.airports_dest.length) // console.log('this.airports_dest length (before): ' + this.airports_dest.length)
this.color = 'fetchDestinations'
this.airports_dest = mapData.records.map((record) => { this.airports_dest = mapData.records.map((record) => {
return { return {
iata: record.fields.Airport_IATA, iata: record.fields.Airport_IATA,
@ -249,8 +289,52 @@ export default {
// console.log('this.airports_dest length (after)' + this.airports_dest.length) // console.log('this.airports_dest length (after)' + this.airports_dest.length)
}, },
async fetchSingleAirport (iata, isOrig) {
const airportFetchFilterFormula =
isOrig
? `AND(Is_Origin=1,{IsCurrent-AsOrigin}="Yes",Airport_IATA="${iata}")`
: `AND(Is_Destination=1,{IsCurrent-AsDest}="Yes",Airport_IATA="${iata}")`
const response = await fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Airports_IATA?filterByFormula=${airportFetchFilterFormula}${this.airportFetch_fields_string}`, {
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: 'Bearer keyJ2ht64ZSN57AG1'
}
})
const mapData = await response.json()
const thisAirport = {
iata: mapData.records[0].fields.Airport_IATA,
lat: mapData.records[0].fields.Latitude_Deg,
long: mapData.records[0].fields.Longitude_Deg,
icon: mapData.records[0].fields.Icon_Url,
name: mapData.records[0].fields.Airport_Name,
municipality: mapData.records[0].fields.Municipality,
type: mapData.records[0].fields.Type,
search: mapData.records[0].fields.Search_Field
}
switch (isOrig) {
case true:
this.selectedOrig = { ...thisAirport }
this.fetchDestinations(thisAirport.iata)
break
case false:
this.selectedDest = { ...thisAirport }
break
default:
break
}
},
showPicker () { showPicker () {
this.isPickerVisible = !this.isPickerVisible this.isPickerVisible = !this.isPickerVisible
},
showSidebar () {
this.isSidebarVisible = !this.isSidebarVisible
} }
} }
} }
@ -270,12 +354,27 @@ header {
position: fixed; position: fixed;
top: 0; top: 0;
width: 100vw; width: 100vw;
z-index: 402;
display: flex;
flex-direction: row;
z-index: 403;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
padding: 1rem; padding: 1rem;
} }
.header__col:nth-child(2) {
text-align: center;
}
.header__col:nth-child(3) {
text-align: right;
}
.logo-blurb {
position: relative;
left: 4.5rem;
color: white;
top: -0.8rem;
}
main { main {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -294,12 +393,75 @@ main {
background: white; background: white;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-items: center;
justify-content: flex-end;
}
:root {
--m-s: 0.1s;
--h-s: 0.3s;
--d: calc(var(--h-s) - var(--m-s));
}
.nav--hide {
transition: margin-top var(--m-s) linear var(--h-s), max-height var(--h-s) linear 0s;
margin-top: 0;
max-height: 0;
}
.nav--show {
margin-top: 3.5rem; margin-top: 3.5rem;
max-height: 250px;
transition: margin-top var(--m-s) linear 0s, max-height var(--h-s) linear var(--m-s);
} }
.btn--nav-open { .btn--nav-open {
padding: 1rem; padding: 1rem;
display: block; display: block;
} }
.flyout-container {
z-index: 401;
position: fixed;
bottom: 0;
display: flex;
justify-content: center;
width: 100vw;
transition: all 0.3s;
pointer-events: none;
}
.flyout-container.flyout-container--hide {
transform: translateY(200px);
}
.flyout-container.flyout-container--show {
transform: translateY(0);
}
.sidebar-container {
z-index: 402;
position: fixed;
top: 0;
right: 0;
display: flex;
justify-content: flex-start;
height: 100vh;
transition: all 0.3s;
pointer-events: none;
}
.sidebar-container.sidebar-container--hide {
transform: translateX(200px);
}
.sidebar-container.sidebar-container--show {
transform: translateX(0);
}
.sidebar {
background: white;
padding: 3rem;
border-radius: 3rem 0 0 3rem;
pointer-events: all;
}
</style> </style>

+ 1629
- 1534
yarn.lock
File diff suppressed because it is too large
View File


Loading…
Cancel
Save