Skip to content

Commit bb55ed9

Browse files
authored
Merge pull request #65 from GenerateNU/profile-event-page
Event Flow HiFis
2 parents eb1691a + d875a54 commit bb55ed9

File tree

15 files changed

+1149
-429
lines changed

15 files changed

+1149
-429
lines changed

frontend/app/(tabs)/profile.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ export default function ProfileScreen() {
125125
<ProfileEventCard
126126
key={event.id}
127127
event={event}
128-
onPress={event => router.push(`/events/${event.id}/info`)}
128+
onPress={event =>
129+
router.push(`/events/${event.id}/info`)
130+
}
129131
onCheckIn={() => router.push('/scan?type=check-in')}
130132
onCheckOut={() => router.push('/scan?type=checkout')}
131133
/>
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { View, Text, StyleSheet, Pressable, Alert } from 'react-native';
3+
import { SafeAreaView } from 'react-native-safe-area-context';
4+
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
5+
import { Event } from '@/types/api/event';
6+
import { eventService } from '@/services/eventService';
7+
import { LoadingScreen } from '@/components/LoadingScreen';
8+
import { Ionicons } from '@expo/vector-icons';
9+
import { Image } from 'expo-image';
10+
import { getEventRegistrations, unregister as unregisterRegistration } from '@/services/registrationService';
11+
import { useAuth } from '@/context/AuthContext';
12+
import { useQueryClient } from '@tanstack/react-query';
13+
14+
export default function EventCancelPage() {
15+
const { eventId } = useLocalSearchParams();
16+
const router = useRouter();
17+
const { volunteer } = useAuth();
18+
const queryClient = useQueryClient();
19+
const [event, setEvent] = useState<Event | null>(null);
20+
const [loading, setLoading] = useState(true);
21+
const [cancelled, setCancelled] = useState(false);
22+
23+
useEffect(() => {
24+
const performCancellation = async () => {
25+
try {
26+
const eventData = await eventService.getEventById(eventId as string);
27+
setEvent(eventData);
28+
29+
if (volunteer?.id && eventData?.id) {
30+
const registrations = await getEventRegistrations(eventData.id);
31+
const myRegistration = registrations.find(
32+
r => r.volunteerId === volunteer.id
33+
);
34+
if (myRegistration) {
35+
await unregisterRegistration(myRegistration.id);
36+
await queryClient.invalidateQueries({
37+
queryKey: ['registration', 'events', volunteer.id, 'upcoming'],
38+
});
39+
setCancelled(true);
40+
}
41+
}
42+
} catch (error) {
43+
console.log('Error cancelling event:', error);
44+
Alert.alert('Error', 'Failed to cancel registration. Please try again.');
45+
} finally {
46+
setLoading(false);
47+
}
48+
};
49+
50+
performCancellation();
51+
}, [eventId, volunteer?.id]);
52+
53+
if (loading) {
54+
return <LoadingScreen text="Cancelling registration..." />;
55+
}
56+
57+
if (!event || !cancelled) {
58+
return (
59+
<SafeAreaView style={styles.container}>
60+
<Text style={styles.errorText}>Unable to cancel registration</Text>
61+
</SafeAreaView>
62+
);
63+
}
64+
65+
const start = event.startDateTime ? new Date(event.startDateTime) : null;
66+
const end = event.endDateTime ? new Date(event.endDateTime) : null;
67+
68+
const dateFormatted = start
69+
? start.toLocaleDateString(undefined, {
70+
month: 'long',
71+
day: 'numeric',
72+
})
73+
: '';
74+
75+
const timeFormatted =
76+
start && end
77+
? `${start.toLocaleTimeString(undefined, {
78+
hour: 'numeric',
79+
minute: '2-digit',
80+
})} - ${end.toLocaleTimeString(undefined, {
81+
hour: 'numeric',
82+
minute: '2-digit',
83+
})}`
84+
: '';
85+
86+
return (
87+
<>
88+
<Stack.Screen options={{ headerShown: false }} />
89+
<SafeAreaView style={styles.container}>
90+
<View style={styles.content}>
91+
<View style={styles.iconContainer}>
92+
<Image
93+
source={require('@/assets/images/ship-wheel.svg')}
94+
style={styles.wheelIcon}
95+
contentFit="contain"
96+
/>
97+
</View>
98+
99+
<Text style={styles.title}>Course Changed...</Text>
100+
101+
<View style={styles.infoSection}>
102+
<View style={styles.infoRow}>
103+
<Ionicons name="calendar-outline" size={18} color="#1D0F48" />
104+
<Text style={styles.infoLabel}>Date:</Text>
105+
<Text style={styles.infoValue}>{dateFormatted}</Text>
106+
</View>
107+
<View style={styles.infoRow}>
108+
<Ionicons name="time-outline" size={18} color="#1D0F48" />
109+
<Text style={styles.infoLabel}>Time:</Text>
110+
<Text style={styles.infoValue}>{timeFormatted}</Text>
111+
</View>
112+
</View>
113+
114+
<Text style={styles.cancelMessage}>
115+
Your sign-up is successfully cancelled.
116+
</Text>
117+
118+
<Text style={styles.apologyMessage}>
119+
We&apos;re sorry to see you go.{'\n'}
120+
Hopefully we&apos;ll see you next time!
121+
</Text>
122+
123+
<Pressable
124+
style={styles.myEventsButton}
125+
onPress={() => router.replace('/')}
126+
>
127+
<Text style={styles.myEventsButtonText}>Go To My Events</Text>
128+
</Pressable>
129+
</View>
130+
</SafeAreaView>
131+
</>
132+
);
133+
}
134+
135+
const styles = StyleSheet.create({
136+
container: {
137+
flex: 1,
138+
backgroundColor: '#FFFDFA',
139+
},
140+
content: {
141+
flex: 1,
142+
paddingHorizontal: 33,
143+
paddingTop: 60,
144+
alignItems: 'center',
145+
},
146+
iconContainer: {
147+
marginBottom: 32,
148+
alignItems: 'center',
149+
justifyContent: 'center',
150+
},
151+
wheelIcon: {
152+
width: 109,
153+
height: 109,
154+
},
155+
title: {
156+
fontFamily: 'Ubuntu',
157+
fontSize: 48,
158+
fontWeight: '700',
159+
color: '#1D0F48',
160+
textAlign: 'center',
161+
marginBottom: 40,
162+
lineHeight: 60
163+
},
164+
infoSection: {
165+
width: '100%',
166+
alignItems: 'center',
167+
gap: 12,
168+
marginBottom: 32,
169+
},
170+
infoRow: {
171+
flexDirection: 'row',
172+
alignItems: 'center',
173+
justifyContent: 'center',
174+
gap: 8,
175+
},
176+
infoLabel: {
177+
fontFamily: 'Inter',
178+
fontSize: 18,
179+
fontWeight: '700',
180+
color: '#1D0F48',
181+
},
182+
infoValue: {
183+
fontFamily: 'Inter',
184+
fontSize: 18,
185+
fontWeight: '400',
186+
color: '#1D0F48',
187+
},
188+
cancelMessage: {
189+
fontFamily: 'Inter',
190+
fontSize: 16,
191+
fontWeight: '700',
192+
color: '#1D0F48',
193+
textAlign: 'center',
194+
marginBottom: 16,
195+
},
196+
apologyMessage: {
197+
fontFamily: 'Inter',
198+
fontSize: 16,
199+
fontWeight: '400',
200+
color: '#1D0F48',
201+
textAlign: 'center',
202+
lineHeight: 24,
203+
marginBottom: 138,
204+
},
205+
myEventsButton: {
206+
backgroundColor: '#74C0EB',
207+
borderRadius: 16.333,
208+
paddingVertical: 13,
209+
paddingHorizontal: 33,
210+
alignItems: 'center',
211+
},
212+
myEventsButtonText: {
213+
fontFamily: 'Inter',
214+
fontSize: 18,
215+
fontWeight: '700',
216+
color: '#1D0F48',
217+
},
218+
errorText: {
219+
fontFamily: 'Inter',
220+
fontSize: 16,
221+
color: '#FF6B6B',
222+
textAlign: 'center',
223+
},
224+
});

0 commit comments

Comments
 (0)