From 7c9afd8931684cbfa4ff6cb22ca7c4fe1b70a599 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 27 Jul 2024 19:22:01 -0400 Subject: [PATCH] transportation --- backend/server/adventures/serializers.py | 19 +-- backend/server/adventures/views.py | 22 +++ .../lib/components/TransportationCard.svelte | 83 +++++++++++ frontend/src/lib/types.ts | 19 +++ .../src/routes/collections/[id]/+page.svelte | 130 ++++++++++-------- 5 files changed, 206 insertions(+), 67 deletions(-) create mode 100644 frontend/src/lib/components/TransportationCard.svelte diff --git a/backend/server/adventures/serializers.py b/backend/server/adventures/serializers.py index 1057eee..53541b4 100644 --- a/backend/server/adventures/serializers.py +++ b/backend/server/adventures/serializers.py @@ -23,14 +23,6 @@ class AdventureSerializer(serializers.ModelSerializer): return [activity.lower() for activity in value] return value -class CollectionSerializer(serializers.ModelSerializer): - adventures = AdventureSerializer(many=True, read_only=True, source='adventure_set') - - class Meta: - model = Collection - # fields are all plus the adventures field - fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date'] - class TransportationSerializer(serializers.ModelSerializer): class Meta: @@ -65,4 +57,15 @@ class TransportationSerializer(serializers.ModelSerializer): validated_data['user_id'] = self.context['request'].user return super().create(validated_data) + +class CollectionSerializer(serializers.ModelSerializer): + adventures = AdventureSerializer(many=True, read_only=True, source='adventure_set') + transportations = TransportationSerializer(many=True, read_only=True, source='transportation_set') + + class Meta: + model = Collection + # fields are all plus the adventures field + fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations'] + + \ No newline at end of file diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index 8d427b4..c15bfe0 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -261,6 +261,10 @@ class CollectionViewSet(viewsets.ModelViewSet): Prefetch('adventure_set', queryset=Adventure.objects.filter( Q(is_public=True) | Q(user_id=self.request.user.id) )) + ).prefetch_related( + Prefetch('transportation_set', queryset=Transportation.objects.filter( + Q(is_public=True) | Q(user_id=self.request.user.id) + )) ) return self.apply_sorting(collections) @@ -296,6 +300,14 @@ class CollectionViewSet(viewsets.ModelViewSet): serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) + # make a view to return all of the associated transportations with a collection + @action(detail=True, methods=['get']) + def transportations(self, request, pk=None): + collection = self.get_object() + transportations = Transportation.objects.filter(collection=collection) + serializer = TransportationSerializer(transportations, many=True) + return Response(serializer.data) + class StatsViewSet(viewsets.ViewSet): permission_classes = [IsAuthenticated] @@ -395,6 +407,16 @@ class TransportationViewSet(viewsets.ModelViewSet): return Response({"detail": "Listing all adventures is not allowed."}, status=status.HTTP_403_FORBIDDEN) + @action(detail=False, methods=['get']) + def all(self, request): + if not request.user.is_authenticated: + return Response({"error": "User is not authenticated"}, status=400) + queryset = Transportation.objects.filter( + Q(user_id=request.user.id) + ) + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + def get_queryset(self): diff --git a/frontend/src/lib/components/TransportationCard.svelte b/frontend/src/lib/components/TransportationCard.svelte new file mode 100644 index 0000000..1b6df55 --- /dev/null +++ b/frontend/src/lib/components/TransportationCard.svelte @@ -0,0 +1,83 @@ + + +
+
+

{collection.name}

+

{collection.adventures.length} Adventures

+ {#if collection.start_date && collection.end_date} +

+ Dates: {new Date(collection.start_date).toLocaleDateString('en-US', { timeZone: 'UTC' })} - {new Date( + collection.end_date + ).toLocaleDateString('en-US', { timeZone: 'UTC' })} +

+ +

+ Duration: {Math.floor( + (new Date(collection.end_date).getTime() - new Date(collection.start_date).getTime()) / + (1000 * 60 * 60 * 24) + ) + 1}{' '} + days +

{/if} +
+ {#if type != 'link'} + + + + {/if} + {#if type == 'link'} + + {/if} +
+
+
diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 08db959..a53fc15 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -66,6 +66,7 @@ export type Collection = { created_at?: string; start_date?: string; end_date?: string; + transportations?: Transportation[]; }; export type OpenStreetMapPlace = { @@ -84,3 +85,21 @@ export type OpenStreetMapPlace = { display_name: string; boundingbox: string[]; }; + +export type Transportation = { + id: number; + user_id: User; + type: string; + name: string; + description: string | null; + rating: number | null; + link: string | null; + date: string | null; // ISO 8601 date string + flight_number: string | null; + from_location: string | null; + to_location: string | null; + is_public: boolean; + collection: Collection | null; + created_at: string; // ISO 8601 date string + updated_at: string; // ISO 8601 date string +}; diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index 75181ad..fdbbaf9 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -192,72 +192,74 @@ {/if} {#if collection} -
-
-
- + {/if} {#if collection.name}

{collection.name}

{/if} @@ -293,6 +295,16 @@ {/each} + {#if collection.transportations && collection.transportations.length > 0} +

Transportation

+ {#each collection.transportations as transportation} +

{transportation.id}

+

{transportation.name}

+

{transportation.type}

+

{transportation.date}

+ {/each} + {/if} + {#if collection.start_date && collection.end_date}

Itinerary by Date

{#if numberOfDays}