aboutsummaryrefslogtreecommitdiffstats
path: root/pages
diff options
context:
space:
mode:
authorLeonardo Bishop <me@leonardobishop.com>2025-01-24 01:53:42 +0000
committerLeonardo Bishop <me@leonardobishop.com>2025-01-24 01:53:42 +0000
commited3faf85e6a6e8083128ec68894533738c762b04 (patch)
treee8bb6ddc4b0d470c024667ab3ccacd3e2f85cf50 /pages
parent0248517c6845a6c755d40c89d3d769ce7d60bd03 (diff)
Add /live
Diffstat (limited to 'pages')
-rw-r--r--pages/agenda.vue5
-rw-r--r--pages/events.vue5
-rw-r--r--pages/index.vue18
-rw-r--r--pages/live.vue100
-rw-r--r--pages/login.vue34
-rw-r--r--pages/register.vue35
-rw-r--r--pages/tracks/[slug].vue3
-rw-r--r--pages/tracks/index.vue3
8 files changed, 161 insertions, 42 deletions
diff --git a/pages/agenda.vue b/pages/agenda.vue
index 9f83210..5e0c643 100644
--- a/pages/agenda.vue
+++ b/pages/agenda.vue
@@ -1,6 +1,7 @@
<script setup lang="ts">
import Panel from '~/components/Panel.vue';
import Dialog from '~/components/Dialog.vue';
+import { Calendar } from 'lucide-vue-next';
const favouritesStore = useFavouritesStore();
const scheduleStore = useScheduleStore();
@@ -79,9 +80,9 @@ function deleteCalendar() {
<span>Updating favourites...</span>
</Panel>
- <template v-else-if="favouriteEvents.length > 0">
+ <template v-else-if="favouriteEvents.length > 0" >
<div class="page">
- <Panel title="Agenda">
+ <Panel title="Agenda" :icon="Calendar">
<ul class="agenda-list">
<li v-for="event in favouriteEvents" :key="event.id" class="agenda-item" >
<EventListing :event="event" />
diff --git a/pages/events.vue b/pages/events.vue
index 9ae35ed..093e959 100644
--- a/pages/events.vue
+++ b/pages/events.vue
@@ -1,4 +1,5 @@
<script setup lang="ts">
+import { Calendar, SquareGanttChart } from 'lucide-vue-next';
import { useScheduleStore } from '~/stores/schedule';
const scheduleStore = useScheduleStore();
@@ -6,11 +7,11 @@ const scheduleStore = useScheduleStore();
</script>
<template>
- <Panel v-if="scheduleStore.schedule" title="Events">
+ <Panel title="Events" :icon="SquareGanttChart" v-if="scheduleStore.schedule">
<div v-for="[day, events] of Object.entries(scheduleStore.eventsPerDay)" :key="day" class="events-container">
<ul class="events-list">
<li v-for="event in events" :key="event.id" class="event-item" :data-index="event.id">
- <EventListing :event="event" :show-relative-time="true" />
+ <EventListing :event="event" />
</li>
</ul>
</div>
diff --git a/pages/index.vue b/pages/index.vue
index 941a378..c455678 100644
--- a/pages/index.vue
+++ b/pages/index.vue
@@ -1,6 +1,22 @@
<script setup lang="ts">
-navigateTo('/events');
+const scheduleStore = useScheduleStore();
+
+const destination = ref()
+
+if (scheduleStore.isConferenceOngoing()) {
+ destination.value = "/live";
+ navigateTo('/live');
+} else {
+ destination.value = "/events";
+ navigateTo('/events');
+}
</script>
<template>
+ <Panel kind="success">
+ <span class="text-icon">
+ <Spinner />
+ <span>Successfully logged in. Navigating to {{ destination }}...</span>
+ </span>
+ </Panel>
</template>
diff --git a/pages/live.vue b/pages/live.vue
index 1c5679f..092f99f 100644
--- a/pages/live.vue
+++ b/pages/live.vue
@@ -1,29 +1,52 @@
<script setup lang="ts">
+import { Radio } from 'lucide-vue-next';
+import EventListing from '~/components/EventListing.vue';
import Panel from '~/components/Panel.vue';
import { type Event } from '~/stores/schedule';
const favouritesStore = useFavouritesStore();
const scheduleStore = useScheduleStore();
-const errorStore = useErrorStore();
-const favouriteEvents = computed(() => {
- return scheduleStore.events.filter((event) => favouritesStore.isFavourite(event));
-});
+const showAllHappeningNow = ref(false);
+const showAllUpcoming = ref(false);
+const timer = ref();
+const refreshKey = ref(0);
+
const todayEvents = computed(() => {
return scheduleStore.events.filter((event) => isToday(new Date(event.start)));
});
const happeningNow = computed(() => {
+ refreshKey.value;
return todayEvents.value.filter((event) => isEventHappeningNow(event));
});
const favouritesHappeningNow = computed(() => {
+ refreshKey.value;
return happeningNow.value.filter((event) => favouritesStore.isFavourite(event));
});
+const upcomingToday = computed(() => {
+ refreshKey.value;
+ return todayEvents.value.filter((event) => !isEventHappeningNow(event) && isInFuture(event));
+});
+const favouritesUpcomingToday = computed(() => {
+ refreshKey.value;
+ return upcomingToday.value.filter((event) => favouritesStore.isFavourite(event));
+});
+
+onMounted(() => {
+ timer.value = setInterval(() => {
+ refreshKey.value++;
+ }, 1000);
+});
function isEventHappeningNow(event: Event): boolean {
const now = new Date();
return event.start <= now && event.end >= now;
}
+function isInFuture(event: Event): boolean {
+ return event.start > new Date();
+}
+
function isToday(date: Date): boolean {
const today = new Date();
return date.getDate() === today.getDate() &&
@@ -33,11 +56,78 @@ function isToday(date: Date): boolean {
</script>
<template>
- <Panel kind="success" class="ongoing" v-if="happeningNow.length > 0">
+ <Panel kind="emphasis" class="ongoing" v-if="happeningNow.length > 0" title="Now" :icon="Radio">
+ <ul class="events-list">
+ <li v-for="event in showAllHappeningNow ? happeningNow : favouritesHappeningNow" :key="event.id">
+ <EventListing :event="event" />
+ </li>
+ <span class="other" v-if="!showAllHappeningNow && happeningNow.length != favouritesHappeningNow.length">{{ happeningNow.length }} other events happening right now</span>
+ </ul>
+ <template #actions>
+ <div class="actions">
+ <span class="status" v-if="!showAllHappeningNow">Showing only agenda items</span>
+ <Button @click="showAllHappeningNow = !showAllHappeningNow" :kind="showAllHappeningNow ? 'secondary' : 'primary'">
+ {{ showAllHappeningNow ? 'Show only agenda items' : 'Show all' }}
+ </Button>
+ </div>
+ </template>
+ </Panel>
+
+ <Panel class="upcoming" v-if="upcomingToday.length > 0" title="Upcoming">
+ <ul class="events-list">
+ <li v-for="event in showAllUpcoming ? upcomingToday : favouritesUpcomingToday" :key="event.id">
+ <EventListing :event="event" :show-relative-time="true" />
+ </li>
+ <span class="other" v-if="!showAllUpcoming">{{ upcomingToday.length }} other upcoming events today</span>
+ </ul>
+
+ <template #actions>
+ <div class="actions">
+ <span class="status" v-if="!showAllUpcoming">Showing only agenda items</span>
+ <Button @click="showAllUpcoming = !showAllUpcoming" :kind="showAllUpcoming ? 'secondary' : 'primary'">
+ {{ showAllUpcoming ? 'Show only agenda items' : 'Show all' }}
+ </Button>
+ </div>
+ </template>
+ </Panel>
+
+ <Panel kind="error" v-if="todayEvents.length === 0">
+ <span>There are no more events today</span>
</Panel>
</template>
<style scoped>
+ul.events-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: grid;
+ margin-top: -1rem;
+}
+
+ul.events-list > .other {
+ font-style: italic;
+}
+
+ul.events-list > li {
+ border-bottom: 1px solid var(--color-hover);
+}
+
+ul.events-list > li:last-of-type {
+ border-bottom: none;
+}
+
+div.actions {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+div.actions > span.status {
+ color: var(--color-text);
+ font-style: italic;
+ font-size: var(--text-small);
+}
</style> \ No newline at end of file
diff --git a/pages/login.vue b/pages/login.vue
index 06d1b77..2900a5e 100644
--- a/pages/login.vue
+++ b/pages/login.vue
@@ -28,7 +28,7 @@ const handleSubmit = async (e: Event) => {
server: false,
});
- navigateTo("/events");
+ navigateTo("/");
} catch (e: any) {
if ((e as FetchError).data) {
error.value = e.data.message
@@ -102,7 +102,7 @@ onMounted(() => {
</template>
<style scoped>
-.auth-container {
+div.auth-container {
min-height: 100vh;
background-color: var(--color-background-muted);
display: flex;
@@ -111,7 +111,7 @@ onMounted(() => {
gap: 1rem;
}
-.auth-header {
+div.auth-header {
margin: 0 auto;
width: 100%;
max-width: 28rem;
@@ -121,53 +121,61 @@ onMounted(() => {
flex-direction: column;
}
-.auth-title {
+h2.auth-title {
margin-top: 1.5rem;
font-size: 1.875rem;
font-weight: 800;
color: #1f2937;
}
-.auth-body {
+div.auth-body {
margin-top: 2rem;
margin: 0 auto;
width: 100%;
max-width: 28rem;
}
-.auth-form {
+form.auth-form {
display: grid;
gap: 1.5rem;
}
-.auth-error {
+div.auth-error {
color: var(--color-text-error);
font-style: oblique;
}
-.form-group {
+div.form-group {
display: flex;
flex-direction: column;
}
-.form-label {
+label.form-label {
display: block;
font-size: 0.875rem;
font-weight: 500;
color: #374151;
}
-.form-input-container {
+div.form-input-container {
margin-top: 0.25rem;
}
-.form-footer {
+div.form-footer {
display: flex;
justify-content: flex-end;
margin: 0 auto;
max-width: 28rem;
}
+div.form-submit {
+ display: flex;
+}
+
+div.form-submit button {
+ width: 100%;
+}
+
.version {
font-size: var(--text-smaller);
margin: 0 auto;
@@ -179,10 +187,6 @@ onMounted(() => {
font-weight: 500;
}
-.form-submit {
- display: flex;
-}
-
input[name="username"] {
text-transform: lowercase;
}
diff --git a/pages/register.vue b/pages/register.vue
index 35e77dd..33a02a7 100644
--- a/pages/register.vue
+++ b/pages/register.vue
@@ -94,7 +94,7 @@ const handleSubmit = async (e: Event) => {
</template>
<style scoped>
-.auth-container {
+div.auth-container {
min-height: 100vh;
background-color: var(--color-background-muted);
display: flex;
@@ -103,7 +103,7 @@ const handleSubmit = async (e: Event) => {
gap: 1rem;
}
-.auth-header {
+div.auth-header {
margin: 0 auto;
width: 100%;
max-width: 28rem;
@@ -113,61 +113,66 @@ const handleSubmit = async (e: Event) => {
flex-direction: column;
}
-.auth-title {
+h2.auth-title {
margin-top: 1.5rem;
font-size: 1.875rem;
font-weight: 800;
color: #1f2937;
}
-.auth-body {
+div.auth-body {
margin-top: 2rem;
margin: 0 auto;
width: 100%;
max-width: 28rem;
}
-.auth-form {
+form.auth-form {
display: grid;
gap: 1.5rem;
}
-.auth-error {
- color: var(--color-error-light);
+div.auth-error {
+ color: var(--color-text-error);
+ font-style: oblique;
}
-.form-group {
+div.form-group {
display: flex;
flex-direction: column;
}
-.form-label {
+label.form-label {
display: block;
font-size: 0.875rem;
font-weight: 500;
color: #374151;
}
-.form-input-container {
+div.form-input-container {
margin-top: 0.25rem;
}
-.form-footer {
+div.form-footer {
display: flex;
justify-content: flex-end;
margin: 0 auto;
max-width: 28rem;
}
+div.form-submit {
+ display: flex;
+}
+
+div.form-submit button {
+ width: 100%;
+}
+
.register-link {
font-size: var(--text-small);
font-weight: 500;
}
-.form-submit {
- display: flex;
-}
-
.auth-error {
color: var(--color-text-error);
font-style: oblique;
diff --git a/pages/tracks/[slug].vue b/pages/tracks/[slug].vue
index 85f00b6..27fb97d 100644
--- a/pages/tracks/[slug].vue
+++ b/pages/tracks/[slug].vue
@@ -1,4 +1,5 @@
<script setup lang="ts">
+import { TrainTrack } from 'lucide-vue-next';
import { useScheduleStore } from '~/stores/schedule';
const route = useRoute();
@@ -8,7 +9,7 @@ const track = scheduleStore.schedule?.tracks.find((track) => track.slug === rout
</script>
<template>
- <Panel v-if="track" :title="track.name" :breadcrumbs="[{ text: 'Tracks', to: '/tracks' }]">
+ <Panel v-if="track" :title="track.name" :breadcrumbs="[{ text: 'Tracks', to: '/tracks' }]" :icon="TrainTrack">
<ul class="events-list">
<li
v-for="event in scheduleStore.eventsPerTrack[track.name]"
diff --git a/pages/tracks/index.vue b/pages/tracks/index.vue
index 544847b..8d7534e 100644
--- a/pages/tracks/index.vue
+++ b/pages/tracks/index.vue
@@ -1,11 +1,12 @@
<script setup lang="ts">
+import { TrainTrack } from 'lucide-vue-next';
import Panel from '~/components/Panel.vue';
const scheduleStore = useScheduleStore();
</script>
<template>
- <Panel v-if="scheduleStore.schedule" title="Tracks">
+ <Panel v-if="scheduleStore.schedule" title="Tracks" :icon="TrainTrack">
<ul class="tracks-list">
<li
v-for="track in scheduleStore.schedule.tracks"