@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |