generated from hutchic-org/template-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(token): if the token got invalidated have the user log back in
- Loading branch information
Showing
1 changed file
with
163 additions
and
145 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,158 +1,176 @@ | ||
<template> | ||
<UContainer> | ||
<UCard class="mt-10"> | ||
<h1>Boat Booking</h1> | ||
<p>Boat Availability Calendar:</p> | ||
<div v-if="loading"> | ||
<p>Loading calendar events...</p> | ||
</div> | ||
<div v-else> | ||
<div class="calendar"> | ||
<div class="day" v-for="day in calendarDays" :key="day.date"> | ||
<span :class="{ booked: day.isBooked }">{{ day.date }}</span> | ||
<template v-if="day.isBooked"> | ||
<UTooltip :text="day.tooltipText" :popper="{ placement: 'top', arrow: true }"> | ||
<template v-if="day.isUserBooking"> | ||
<UIcon name="i-heroicons-user-circle" dynamic class="icon-user-booked" /> | ||
</template> | ||
<template v-else> | ||
<UIcon name="i-heroicons-x-circle" dynamic class="icon-booked" /> | ||
</template> | ||
</UTooltip> | ||
</template> | ||
<template v-else> | ||
<UIcon name="i-heroicons-check-circle" dynamic class="icon-available" /> | ||
</template> | ||
</div> | ||
<UContainer> | ||
<UCard class="mt-10"> | ||
<h1>Boat Booking</h1> | ||
<p>Boat Availability Calendar:</p> | ||
<div v-if="loading"> | ||
<p>Loading calendar events...</p> | ||
</div> | ||
<div v-else> | ||
<div class="calendar"> | ||
<div class="day" v-for="day in calendarDays" :key="day.date"> | ||
<span :class="{ booked: day.isBooked }">{{ day.date }}</span> | ||
<template v-if="day.isBooked"> | ||
<UTooltip :text="day.tooltipText" :popper="{ placement: 'top', arrow: true }"> | ||
<template v-if="day.isUserBooking"> | ||
<UIcon name="i-heroicons-user-circle" dynamic class="icon-user-booked" /> | ||
</template> | ||
<template v-else> | ||
<UIcon name="i-heroicons-x-circle" dynamic class="icon-booked" /> | ||
</template> | ||
</UTooltip> | ||
</template> | ||
<template v-else> | ||
<UIcon name="i-heroicons-check-circle" dynamic class="icon-available" /> | ||
</template> | ||
</div> | ||
</div> | ||
</UCard> | ||
</UContainer> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
interface CalendarEvent { | ||
start: { date: string }; | ||
end: { date: string }; | ||
creator: { email: string }; | ||
} | ||
interface CalendarDay { | ||
date: string; | ||
isBooked: boolean; | ||
isUserBooking: boolean; | ||
tooltipText: string; | ||
} | ||
const loading: Ref<boolean> = ref(true); | ||
const events: Ref<CalendarEvent[]> = ref([]); | ||
const calendarDays: Ref<CalendarDay[]> = ref([]); | ||
const userEmail: Ref<string> = ref(''); | ||
const config = useRuntimeConfig(); | ||
const listCalendarEvents = async () => { | ||
const accessToken = sessionStorage.getItem('googleAccessToken'); | ||
if (!accessToken) { | ||
console.error('No access token found'); | ||
return; | ||
</div> | ||
</UCard> | ||
</UContainer> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { ref, onMounted } from 'vue'; | ||
import { useRuntimeConfig } from '#app'; | ||
import { useRouter } from 'vue-router'; | ||
interface CalendarEvent { | ||
start: { date: string }; | ||
end: { date: string }; | ||
creator: { email: string }; | ||
} | ||
interface CalendarDay { | ||
date: string; | ||
isBooked: boolean; | ||
isUserBooking: boolean; | ||
tooltipText: string; | ||
} | ||
const loading = ref(true); | ||
const events = ref<CalendarEvent[]>([]); | ||
const calendarDays = ref<CalendarDay[]>([]); | ||
const userEmail = ref<string>(''); | ||
const router = useRouter(); | ||
const config = useRuntimeConfig(); | ||
const listCalendarEvents = async () => { | ||
const accessToken = sessionStorage.getItem('googleAccessToken'); | ||
if (!accessToken) { | ||
console.error('No access token found'); | ||
router.push('/'); | ||
return; | ||
} | ||
try { | ||
const response = await fetch(`https://www.googleapis.com/calendar/v3/calendars/${config.public.googleCalendarId}/events`, { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
try { | ||
const response = await fetch(`https://www.googleapis.com/calendar/v3/calendars/${config.public.googleCalendarId}/events`, { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
if (!response.ok) { | ||
throw new Error('Failed to fetch calendar events'); | ||
} | ||
const data = await response.json(); | ||
events.value = data.items; | ||
generateCalendarDays(); | ||
} catch (error) { | ||
console.error('Error fetching calendar events', error); | ||
} finally { | ||
loading.value = false; | ||
} | ||
}; | ||
if (response.status === 401) { | ||
sessionStorage.removeItem('googleAccessToken'); | ||
router.push('/'); | ||
return; | ||
} | ||
const fetchUserProfile = async () => { | ||
const accessToken = sessionStorage.getItem('googleAccessToken'); | ||
if (!accessToken) { | ||
console.error('No access token found'); | ||
return; | ||
} | ||
if (!response.ok) { | ||
throw new Error('Failed to fetch calendar events'); | ||
} | ||
try { | ||
const response = await fetch('https://www.googleapis.com/oauth2/v1/userinfo?alt=json', { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
if (!response.ok) { | ||
throw new Error('Failed to fetch user profile'); | ||
} | ||
const data = await response.json(); | ||
userEmail.value = data.email; | ||
} catch (error) { | ||
console.error('Error fetching user profile', error); | ||
const data = await response.json(); | ||
events.value = data.items; | ||
generateCalendarDays(); | ||
} catch (error) { | ||
console.error('Error fetching calendar events', error); | ||
} finally { | ||
loading.value = false; | ||
} | ||
}; | ||
const fetchUserProfile = async () => { | ||
const accessToken = sessionStorage.getItem('googleAccessToken'); | ||
if (!accessToken) { | ||
console.error('No access token found'); | ||
router.push('/'); | ||
return; | ||
} | ||
try { | ||
const response = await fetch('https://www.googleapis.com/oauth2/v1/userinfo?alt=json', { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}; | ||
const generateCalendarDays = () => { | ||
const today = new Date(); | ||
const daysInMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0).getDate(); | ||
calendarDays.value = Array.from({ length: daysInMonth }, (_, i) => { | ||
const date = new Date(today.getFullYear(), today.getMonth(), i + 1).toISOString().split('T')[0]; | ||
return { date, isBooked: false, isUserBooking: false, tooltipText: '' }; | ||
}); | ||
events.value.forEach(event => { | ||
const startDate = new Date(event.start.date); | ||
const endDate = new Date(event.end.date); | ||
for (let d = startDate; d < endDate; d.setDate(d.getDate() + 1)) { | ||
const dayIndex = d.getDate() - 1; | ||
if (calendarDays.value[dayIndex]) { | ||
calendarDays.value[dayIndex].isBooked = true; | ||
calendarDays.value[dayIndex].isUserBooking = event.creator.email === userEmail.value; | ||
calendarDays.value[dayIndex].tooltipText = `Booked by: ${event.creator.email}`; | ||
} | ||
} | ||
}); | ||
}; | ||
onMounted(async () => { | ||
await fetchUserProfile(); | ||
await listCalendarEvents(); | ||
}); | ||
</script> | ||
<style> | ||
.calendar { | ||
display: grid; | ||
grid-template-columns: repeat(7, 1fr); | ||
gap: 1rem; | ||
} | ||
.day { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
} | ||
.icon-booked { | ||
color: red; | ||
if (response.status === 401) { | ||
sessionStorage.removeItem('googleAccessToken'); | ||
router.push('/'); | ||
return; | ||
} | ||
.icon-user-booked { | ||
color: blue; | ||
} | ||
.icon-available { | ||
color: green; | ||
if (!response.ok) { | ||
throw new Error('Failed to fetch user profile'); | ||
} | ||
.booked { | ||
font-weight: bold; | ||
const data = await response.json(); | ||
userEmail.value = data.email; | ||
} catch (error) { | ||
console.error('Error fetching user profile', error); | ||
} | ||
}; | ||
const generateCalendarDays = () => { | ||
const today = new Date(); | ||
const daysInMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0).getDate(); | ||
calendarDays.value = Array.from({ length: daysInMonth }, (_, i) => { | ||
const date = new Date(today.getFullYear(), today.getMonth(), i + 1).toISOString().split('T')[0]; | ||
return { date, isBooked: false, isUserBooking: false, tooltipText: '' }; | ||
}); | ||
events.value.forEach(event => { | ||
const startDate = new Date(event.start.date); | ||
const endDate = new Date(event.end.date); | ||
for (let d = startDate; d < endDate; d.setDate(d.getDate() + 1)) { | ||
const dayIndex = d.getDate() - 1; | ||
if (calendarDays.value[dayIndex]) { | ||
calendarDays.value[dayIndex].isBooked = true; | ||
calendarDays.value[dayIndex].isUserBooking = event.creator.email === userEmail.value; | ||
calendarDays.value[dayIndex].tooltipText = `Booked by: ${event.creator.email}`; | ||
} | ||
} | ||
</style> | ||
}); | ||
}; | ||
onMounted(async () => { | ||
await fetchUserProfile(); | ||
await listCalendarEvents(); | ||
}); | ||
</script> | ||
|
||
<style> | ||
.calendar { | ||
display: grid; | ||
grid-template-columns: repeat(7, 1fr); | ||
gap: 1rem; | ||
} | ||
.day { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
} | ||
.icon-booked { | ||
color: red; | ||
} | ||
.icon-user-booked { | ||
color: blue; | ||
} | ||
.icon-available { | ||
color: green; | ||
} | ||
.booked { | ||
font-weight: bold; | ||
} | ||
</style> |