Skip to content

Commit 7af7e80

Browse files
authored
Permalinks for events from @BigSpaceships
Added links for different events
2 parents 4134e12 + 9699f27 commit 7af7e80

File tree

12 files changed

+758
-541
lines changed

12 files changed

+758
-541
lines changed

src/frontend/package-lock.json

Lines changed: 583 additions & 479 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/frontend/src/components/CreateEventModal.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export default defineComponent({
137137
};
138138
eventStore.addEvent(newEvent);
139139
eventStore.sortEvents(false);
140-
eventStore.selectEvent(newEvent);
140+
this.$router.push('/' + result);
141141
142142
popupStore.addPopup(PopupType.Success, 'Event Created!');
143143
this.closeModal();

src/frontend/src/components/DeleteEventModal.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,16 @@ export default defineComponent({
4646
const popupStore = usePopupStore();
4747
try {
4848
const eventStore = useEventStore();
49-
const response = await fetch(`/api/v1/event/${eventStore.selectedEvent!.id}`, {
49+
const response = await fetch(`/api/v1/event/${eventStore.id}`, {
5050
method: 'DELETE'
5151
});
5252
5353
if (!response.ok) {
5454
popupStore.addPopup(PopupType.Danger, `Failed to Delete Event (${response.status})`);
5555
return;
5656
}
57-
eventStore.removeEvent(eventStore.selectedEvent);
58-
eventStore.selectedEvent = null;
57+
eventStore.removeEvent(eventStore.id);
58+
eventStore.id = null;
5959
popupStore.addPopup(PopupType.Success, 'Event Deleted!');
6060
this.closeModal();
6161
} catch (error) {

src/frontend/src/components/EventCard.vue

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
<script setup lang="ts">
22
import CaretRight from './icons/CaretRight.vue';
3+
import { RouterLink } from 'vue-router';
34
</script>
45

56
<template>
6-
<div class="card mb-3">
7+
<RouterLink :to="eventPath" class="card mb-3">
78
<div class="card-body d-flex justify-content-between align-items-center">
89
<div>
910
<h5 class="card-title">{{ event!.name }}</h5>
1011
<h6 class="card-time">{{ formattedStart }}</h6>
1112
</div>
1213
<CaretRight v-if="screenStore.mobile" />
1314
</div>
14-
</div>
15+
</RouterLink>
1516
</template>
1617

1718
<script lang="ts">
18-
import { defineComponent, type PropType } from 'vue';
19+
import { defineComponent, inject, type PropType } from 'vue';
1920
import { type Event } from '@/models';
2021
import { format } from 'date-fns';
2122
import { useScreenStore } from '@/stores/screen';
@@ -27,13 +28,21 @@ export default defineComponent({
2728
data() {
2829
let screenStore = useScreenStore();
2930
return {
31+
historyMode: inject('historyMode') as Boolean,
3032
screenStore
3133
};
3234
},
3335
computed: {
3436
formattedStart() {
3537
let data = this.event?.startTime.toLocaleString();
3638
return data ? format(data, 'MM/dd/yyyy hh:mm a') : 'N/A';
39+
},
40+
eventPath() {
41+
if (this.historyMode) {
42+
return `/history/${this.$props.event?.id}`;
43+
} else {
44+
return `${this.$props.event?.id}`;
45+
}
3746
}
3847
}
3948
});

src/frontend/src/components/EventDetails.vue

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,19 @@ import EditCarButton from './EditCarButton.vue';
1212
<div>
1313
<h3 class="card-title">{{ event?.name }}</h3>
1414
<h5 class="card-text">
15-
<a target="_blank" v-bind:href="'https://maps.google.com/?q=' + event?.location"
16-
><IconPin /> {{ event?.location }}</a
17-
>
15+
<a target="_blank" v-bind:href="'https://maps.google.com/?q=' + event?.location">
16+
<IconPin /> {{ event?.location }}
17+
</a>
1818
</h5>
1919
<div v-if="screenStore.mobile">
2020
<h5><b>Start: </b>{{ startTime }}</h5>
2121
<h5><b>End: </b>{{ endTime }}</h5>
2222
</div>
2323
<h5 class="card-text" v-else>{{ startTime }} - {{ endTime }}</h5>
24-
<NeedsRideButton />
24+
<div class="d-flex flex-wrap gap-2 pb-2">
25+
<NeedsRideButton />
26+
<ShareEventButton :event-id="id" />
27+
</div>
2528
</div>
2629
<div class="mt-4 mb-3">
2730
<div class="d-flex justify-content-between align-items-center">
@@ -53,26 +56,31 @@ import EditCarButton from './EditCarButton.vue';
5356
</template>
5457

5558
<script lang="ts">
56-
import { defineComponent, inject, type PropType } from 'vue';
57-
import { type Event } from '@/models';
59+
import { defineComponent, inject } from 'vue';
5860
import { format } from 'date-fns';
5961
import { useAuthStore } from '@/stores/auth';
6062
import { useScreenStore } from '@/stores/screen';
6163
import { useEventStore } from '@/stores/events';
6264
import NeedsRideButton from './NeedsRideButton.vue';
65+
import ShareEventButton from './ShareEventButton.vue';
6366
6467
export default defineComponent({
6568
props: {
66-
event: Object as PropType<Event>
69+
id: Number
6770
},
6871
data() {
6972
let screenStore = useScreenStore();
73+
let eventStore = useEventStore();
7074
return {
7175
historyMode: inject('historyMode'),
76+
eventStore,
7277
screenStore
7378
};
7479
},
7580
computed: {
81+
event() {
82+
return this.eventStore.selectedEvent;
83+
},
7684
startTime() {
7785
let data = this.event!.startTime.toLocaleString();
7886
return format(data, this.screenStore.mobile ? 'MM/dd/yy hh:mm a' : 'LLLL dd, yyyy hh:mm a');
@@ -92,6 +100,9 @@ export default defineComponent({
92100
?.filter((car) => car.driver.id === authStore.user?.id)
93101
.pop();
94102
}
103+
},
104+
created() {
105+
this.eventStore.setEventId(this.$props.id!);
95106
}
96107
});
97108
</script>

src/frontend/src/components/NeedsRideButton.vue

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,12 @@ import BellSlash from './icons/BellSlash.vue';
44
</script>
55

66
<template>
7-
<button type="button" class="btn btn-danger mb-2" @click="sendDataRemove" v-if="userInNeedsRide">
7+
<button type="button" class="btn btn-danger" @click="sendDataRemove" v-if="userInNeedsRide">
88
<BellSlash style="vertical-align: text-top" /> No Longer Need Ride ({{
99
eventStore.selectedEvent?.needsRide.length
1010
}})
1111
</button>
12-
<button
13-
type="button"
14-
class="btn btn-primary mb-2"
15-
@click="sendDataAdd"
16-
:disabled="userInCar"
17-
v-else
18-
>
12+
<button type="button" class="btn btn-primary" @click="sendDataAdd" :disabled="userInCar" v-else>
1913
<BellAlert style="vertical-align: text-top" /> Need Ride ({{
2014
eventStore.selectedEvent?.needsRide.length
2115
}})
@@ -38,8 +32,11 @@ export default defineComponent({
3832
},
3933
computed: {
4034
userInCar() {
41-
let allCars = this.eventStore.selectedEvent?.cars;
35+
let allCars = this.eventStore.selectedEventCars;
4236
let userId = this.authStore.user?.id;
37+
38+
if (!allCars) return false;
39+
4340
return allCars!.some(
4441
(car) => car.riders.some((rider) => rider.id === userId) || car.driver.id === userId
4542
);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<p>Select an Event to see details</p>
3+
</template>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<template>
2+
<button type="button" class="btn btn-primary" @click="copyLink">Copy Link to Event</button>
3+
</template>
4+
5+
<script lang="ts">
6+
import { PopupType } from '@/models';
7+
import { usePopupStore } from '@/stores/popup';
8+
import { defineComponent } from 'vue';
9+
10+
export default defineComponent({
11+
props: {
12+
eventId: Number
13+
},
14+
methods: {
15+
copyLink() {
16+
const popupStore = usePopupStore();
17+
const baseUrl = window.location.host;
18+
19+
const urlToCopy = baseUrl + '/event/' + this.eventId;
20+
21+
try {
22+
navigator.clipboard.writeText(urlToCopy);
23+
24+
popupStore.addPopup(PopupType.Success, 'Copied url for event to clipboard!');
25+
} catch (_err) {
26+
popupStore.addPopup(PopupType.Danger, `Failed to copy url ${urlToCopy} to clipboard`);
27+
}
28+
}
29+
}
30+
});
31+
</script>

src/frontend/src/router/index.ts

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,71 @@
1-
import { createRouter, createWebHistory } from 'vue-router';
1+
import {
2+
createRouter,
3+
createWebHistory,
4+
type RouteLocationNormalizedGeneric,
5+
type RouteLocationNormalizedLoadedGeneric
6+
} from 'vue-router';
27
import HomeView from '../views/HomeView.vue';
38
import LoginView from '../views/LoginView.vue';
49
import { useAuthStore } from '@/stores/auth';
510
import { type UserData } from '@/models';
11+
import EventDetails from '@/components/EventDetails.vue';
12+
import NoEventDetails from '@/components/NoEventDetails.vue';
13+
14+
import { type Event } from '@/models';
15+
16+
async function handleEventIdRedirects(
17+
to: RouteLocationNormalizedGeneric,
18+
_from: RouteLocationNormalizedLoadedGeneric
19+
) {
20+
const response = await fetch(`/api/v1/event/${to.params.id}`);
21+
22+
if (response.status == 404) {
23+
return {
24+
path: '/'
25+
};
26+
}
27+
28+
if (response.status != 200) {
29+
throw Error('Bad Return Code');
30+
}
31+
32+
const jsonData: Event = await response.json();
33+
34+
const isInPast = new Date(jsonData.startTime).getTime() < Date.now();
35+
36+
const isGoingToPast = to.path.includes('history');
37+
38+
if ((isGoingToPast && isInPast) || (!isGoingToPast && !isInPast)) {
39+
return true;
40+
}
41+
42+
if (isInPast) {
43+
return { path: `/history/${to.params.id}` };
44+
} else {
45+
return { path: `${to.params.id}` };
46+
}
47+
}
648

749
const router = createRouter({
850
history: createWebHistory(import.meta.env.BASE_URL),
951
routes: [
1052
{
1153
path: '/',
12-
name: 'home',
1354
component: HomeView,
14-
props: { showPast: false }
55+
props: { showPast: false },
56+
children: [
57+
{
58+
path: '',
59+
name: 'home',
60+
component: NoEventDetails
61+
},
62+
{
63+
path: ':id',
64+
component: EventDetails,
65+
beforeEnter: handleEventIdRedirects,
66+
props: (route) => ({ id: Number(route.params.id) })
67+
}
68+
]
1569
},
1670
{
1771
path: '/login',
@@ -20,9 +74,26 @@ const router = createRouter({
2074
},
2175
{
2276
path: '/history',
23-
name: 'history',
2477
component: HomeView,
25-
props: { showPast: true }
78+
props: { showPast: true },
79+
children: [
80+
{
81+
path: '',
82+
name: 'history',
83+
component: NoEventDetails
84+
},
85+
{
86+
path: ':id',
87+
component: EventDetails,
88+
beforeEnter: handleEventIdRedirects,
89+
props: (route) => ({ id: Number(route.params.id) })
90+
}
91+
]
92+
},
93+
{
94+
path: '/event/:id',
95+
beforeEnter: handleEventIdRedirects,
96+
component: NoEventDetails // This never gets used but has to be here for the beforeEnter to be called
2697
}
2798
]
2899
});

src/frontend/src/stores/events.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,17 @@ function sortByStartDate(a: Event, b: Event) {
88
export const useEventStore = defineStore('events', {
99
state: () => ({
1010
events: [] as Event[],
11-
selectedEvent: null as Event | null
11+
id: null as number | null
1212
}),
1313
getters: {
14-
selectedEventCars: (state) => state.selectedEvent?.cars
14+
selectedEvent(state) {
15+
return state.events.find((event) => {
16+
return event.id == state.id;
17+
});
18+
},
19+
selectedEventCars(): Car[] | undefined {
20+
return this.selectedEvent?.cars;
21+
}
1522
},
1623
actions: {
1724
addEvent(event: Event) {
@@ -20,29 +27,25 @@ export const useEventStore = defineStore('events', {
2027
setEvents(events: Event[]) {
2128
this.events = events;
2229
},
30+
setEventId(id: number) {
31+
this.id = id;
32+
},
2333
sortEvents(past: Boolean) {
2434
this.events.sort(sortByStartDate);
2535

2636
if (past) {
27-
this.events.reverse();
37+
this.events.reverse();
2838
}
2939
},
30-
removeEvent(event: Event | null) {
31-
if (event == null) {
40+
removeEvent(id: number | null) {
41+
if (id == null) {
3242
return;
3343
}
34-
const index = this.events.indexOf(event);
44+
const index = this.events.findIndex((event) => event.id == id);
3545
if (index > -1) {
3646
this.events.splice(index, 1);
3747
}
3848
},
39-
selectEvent(event: Event) {
40-
if (this.selectedEvent == event) {
41-
return;
42-
}
43-
this.selectedEvent = event;
44-
this.selectedEvent.cars = [];
45-
},
4649
addCar(car: Car) {
4750
this.selectedEvent?.cars?.push(car);
4851
},

0 commit comments

Comments
 (0)