Skip to content

Commit 1dfe869

Browse files
implement event signup functionality with mutation and API endpoint
1 parent ca62947 commit 1dfe869

4 files changed

Lines changed: 62 additions & 5 deletions

File tree

client/src/hooks/events.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
1+
import {
2+
useMutation,
3+
useQuery,
4+
useQueryClient,
5+
UseQueryOptions,
6+
} from "@tanstack/react-query";
27

38
import api from "../lib/api";
49

@@ -55,3 +60,23 @@ export const useEvent = (
5560
...args,
5661
});
5762
};
63+
64+
// flow: ask the backend to change, then refetch the data
65+
// queryclient can mark the cached data as stale, which refetches the data
66+
export const useSignupEvent = (eventId: number) => {
67+
const queryClient = useQueryClient();
68+
69+
// post to the url
70+
return useMutation({
71+
mutationFn: async () => {
72+
const response = await api.post(`/events/${eventId}/signup/`);
73+
return response.data;
74+
},
75+
76+
// if successful, mark as stale, trigger refetch
77+
onSuccess: () => {
78+
queryClient.invalidateQueries({ queryKey: ["event", eventId] });
79+
queryClient.invalidateQueries({ queryKey: ["me"] });
80+
},
81+
});
82+
};

client/src/pages/events/[id].tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import { useRouter } from "next/router";
44

55
import { Button } from "@/components/ui/button";
66
import TagList from "@/components/ui/tag-list";
7-
import { useEvent } from "@/hooks/events";
7+
import { useEvent, useSignupEvent } from "@/hooks/events";
88

99
import ActionLayout from "../layouts/actionlayout";
1010

1111
export default function EventDetailsPage() {
1212
// useRouter allows access to URL parameters
1313
const router = useRouter();
1414
const id = Number(router.query.id);
15+
const signup = useSignupEvent(id);
1516

1617
const { data: event, isLoading } = useEvent(id);
1718
if (isLoading) {
@@ -35,7 +36,12 @@ export default function EventDetailsPage() {
3536
}
3637
primaryAction={
3738
<Link href={"/events/" + id + "/participants"} className="flex-1">
38-
<Button className="w-full rounded-full">Join Event</Button>
39+
<Button
40+
className="w-full rounded-full"
41+
onClick={() => signup.mutate()}
42+
>
43+
Join Event
44+
</Button>
3945
</Link>
4046
}
4147
>

server/event/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
urlpatterns = [
66
path("", views.EventList.as_view(), name="event-list"),
77
path("<int:pk>/", views.EventDetail.as_view(), name="event-detail"),
8+
path("<int:pk>/signup/", views.EventSignup.as_view(), name="event-signup")
89
]
910

server/event/views.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
from django.shortcuts import render
1+
from django.http import Http404
2+
from django.shortcuts import get_object_or_404, render
23
from rest_framework import generics, permissions
4+
from rest_framework.views import APIView
5+
from rest_framework.response import Response
36

47

58
from .models import Event
@@ -14,4 +17,26 @@ class EventList(generics.ListCreateAPIView):
1417

1518
class EventDetail(generics.RetrieveUpdateAPIView):
1619
queryset = Event.objects.all()
17-
serializer_class = EventSerializer
20+
serializer_class = EventSerializer
21+
22+
class EventSignup(APIView):
23+
permission_classes = [permissions.IsAuthenticated]
24+
25+
def post(self, request, pk):
26+
try:
27+
event = Event.objects.get(pk=pk)
28+
except Event.DoesNotExist:
29+
return Response(
30+
{"detail": "Event not found"},
31+
status=404
32+
)
33+
34+
if event.participants.filter(id=request.user.id).exists():
35+
return Response(
36+
{"detail": "Already signed up"},
37+
status=400
38+
)
39+
40+
event.participants.add(request.user)
41+
serializer = EventSerializer(event)
42+
return Response(serializer.data, status=200)

0 commit comments

Comments
 (0)