diff --git a/backend/server/adventures/migrations/0010_collection_link.py b/backend/server/adventures/migrations/0010_collection_link.py new file mode 100644 index 0000000..85a5341 --- /dev/null +++ b/backend/server/adventures/migrations/0010_collection_link.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.8 on 2024-10-08 03:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('adventures', '0009_alter_adventure_type'), + ] + + operations = [ + migrations.AddField( + model_name='collection', + name='link', + field=models.URLField(blank=True, max_length=2083, null=True), + ), + ] diff --git a/backend/server/adventures/models.py b/backend/server/adventures/models.py index e79ef24..5dc49f7 100644 --- a/backend/server/adventures/models.py +++ b/backend/server/adventures/models.py @@ -117,6 +117,7 @@ class Collection(models.Model): updated_at = models.DateTimeField(auto_now=True) is_archived = models.BooleanField(default=False) shared_with = models.ManyToManyField(User, related_name='shared_with', blank=True) + link = models.URLField(blank=True, null=True, max_length=2083) # if connected adventures are private and collection is public, raise an error diff --git a/backend/server/adventures/serializers.py b/backend/server/adventures/serializers.py index 4bf7e8c..f2a10de 100644 --- a/backend/server/adventures/serializers.py +++ b/backend/server/adventures/serializers.py @@ -176,8 +176,7 @@ class CollectionSerializer(serializers.ModelSerializer): 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', 'notes', 'updated_at', 'checklists', 'is_archived', 'shared_with'] + fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations', 'notes', 'updated_at', 'checklists', 'is_archived', 'shared_with', 'link'] read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] def to_representation(self, instance): diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index 7f90f73..5acb2a0 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -511,10 +511,8 @@ class StatsViewSet(viewsets.ViewSet): @action(detail=False, methods=['get']) def counts(self, request): - visited_count = Adventure.objects.filter( - type='visited', user_id=request.user.id).count() - planned_count = Adventure.objects.filter( - type='planned', user_id=request.user.id).count() + adventure_count = Adventure.objects.filter( + user_id=request.user.id).count() trips_count = Collection.objects.filter( user_id=request.user.id).count() visited_region_count = VisitedRegion.objects.filter( @@ -524,8 +522,7 @@ class StatsViewSet(viewsets.ViewSet): user_id=request.user.id).values('region__country').distinct().count() total_countries = Country.objects.count() return Response({ - 'visited_count': visited_count, - 'planned_count': planned_count, + 'adventure_count': adventure_count, 'trips_count': trips_count, 'visited_region_count': visited_region_count, 'total_regions': total_regions, diff --git a/frontend/src/lib/components/AdventureCard.svelte b/frontend/src/lib/components/AdventureCard.svelte index d5b2f06..e6952f0 100644 --- a/frontend/src/lib/components/AdventureCard.svelte +++ b/frontend/src/lib/components/AdventureCard.svelte @@ -18,8 +18,8 @@ import CollectionLink from './CollectionLink.svelte'; import DotsHorizontal from '~icons/mdi/dots-horizontal'; import DeleteWarning from './DeleteWarning.svelte'; - import ImageDisplayModal from './ImageDisplayModal.svelte'; import { isAdventureVisited, typeToString } from '$lib'; + import CardCarousel from './CardCarousel.svelte'; export let type: string; export let user: User | null; @@ -28,7 +28,6 @@ let isCollectionModalOpen: boolean = false; let isWarningModalOpen: boolean = false; - let image_url: string | null = null; export let adventure: Adventure; let activityTypes: string[] = []; @@ -120,12 +119,6 @@ dispatch('edit', adventure); } - let currentSlide = 0; - - function goToSlide(index: number) { - currentSlide = index; - } - function link() { dispatch('link', adventure); } @@ -146,48 +139,10 @@ /> {/if} -{#if image_url} - (image_url = null)} {adventure} /> -{/if} -
-
- {#if adventure.images && adventure.images.length > 0} - - {:else} - - No image available - {/if} -
+
diff --git a/frontend/src/lib/components/CardCarousel.svelte b/frontend/src/lib/components/CardCarousel.svelte new file mode 100644 index 0000000..ca9e652 --- /dev/null +++ b/frontend/src/lib/components/CardCarousel.svelte @@ -0,0 +1,87 @@ + + +{#if image_url} + (image_url = null)} + /> +{/if} + +
+ {#if adventure_images && adventure_images.length > 0} + + {:else} + + No image available + {/if} +
diff --git a/frontend/src/lib/components/CollectionCard.svelte b/frontend/src/lib/components/CollectionCard.svelte index 519ddfd..f34f00c 100644 --- a/frontend/src/lib/components/CollectionCard.svelte +++ b/frontend/src/lib/components/CollectionCard.svelte @@ -9,7 +9,7 @@ import ArchiveArrowUp from '~icons/mdi/archive-arrow-up'; import { goto } from '$app/navigation'; - import type { Collection } from '$lib/types'; + import type { Adventure, Collection } from '$lib/types'; import { addToast } from '$lib/toasts'; import Plus from '~icons/mdi/plus'; @@ -17,14 +17,14 @@ import TrashCan from '~icons/mdi/trashcan'; import DeleteWarning from './DeleteWarning.svelte'; import ShareModal from './ShareModal.svelte'; + import CardCarousel from './CardCarousel.svelte'; const dispatch = createEventDispatcher(); export let type: String | undefined | null; + export let adventures: Adventure[] = []; let isShareModalOpen: boolean = false; - // export let type: String; - function editAdventure() { dispatch('edit', collection); } @@ -86,6 +86,7 @@
+
+
+
+ +

{ modal = document.getElementById('my_modal_1') as HTMLDialogElement; @@ -42,34 +42,36 @@ diff --git a/frontend/src/lib/components/NewCollection.svelte b/frontend/src/lib/components/NewCollection.svelte index 49bbbc9..93b0827 100644 --- a/frontend/src/lib/components/NewCollection.svelte +++ b/frontend/src/lib/components/NewCollection.svelte @@ -13,7 +13,9 @@ name: '', description: '', adventures: [] as Adventure[], - is_public: false + is_public: false, + shared_with: [], + link: '' }; const dispatch = createEventDispatcher(); @@ -151,6 +153,16 @@ class="input input-bordered w-full max-w-xs mt-1" />
+
+
+ +
diff --git a/frontend/src/lib/components/RegionCard.svelte b/frontend/src/lib/components/RegionCard.svelte index 44b8878..cdfa041 100644 --- a/frontend/src/lib/components/RegionCard.svelte +++ b/frontend/src/lib/components/RegionCard.svelte @@ -57,11 +57,7 @@ class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-neutral text-neutral-content shadow-xl overflow-hidden" >
- {#if region.name_en && region.name !== region.name_en} -

{region.name} ({region.name_en})

- {:else} -

{region.name}

- {/if} +

{region.name}

{region.id}

diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index ac862fb..f02822e 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -87,6 +87,7 @@ export type Collection = { checklists?: Checklist[]; is_archived?: boolean; shared_with: string[]; + link?: string | null; }; export type OpenStreetMapPlace = { diff --git a/frontend/src/routes/collections/+page.server.ts b/frontend/src/routes/collections/+page.server.ts index 90bce78..9d2477f 100644 --- a/frontend/src/routes/collections/+page.server.ts +++ b/frontend/src/routes/collections/+page.server.ts @@ -53,6 +53,11 @@ export const actions: Actions = { const description = formData.get('description') as string | null; const start_date = formData.get('start_date') as string | null; const end_date = formData.get('end_date') as string | null; + let link = formData.get('link') as string | null; + + if (link) { + link = checkLink(link); + } if (!name) { return { @@ -66,6 +71,7 @@ export const actions: Actions = { formDataToSend.append('description', description || ''); formDataToSend.append('start_date', start_date || ''); formDataToSend.append('end_date', end_date || ''); + formDataToSend.append('link', link || ''); let auth = event.cookies.get('auth'); if (!auth) { @@ -142,6 +148,7 @@ export const actions: Actions = { let is_public = formData.get('is_public') as string | null | boolean; const start_date = formData.get('start_date') as string | null; const end_date = formData.get('end_date') as string | null; + let link = formData.get('link') as string | null; if (is_public) { is_public = true; @@ -149,6 +156,10 @@ export const actions: Actions = { is_public = false; } + if (link) { + link = checkLink(link); + } + if (!name) { return { status: 400, @@ -162,6 +173,7 @@ export const actions: Actions = { formDataToSend.append('is_public', is_public.toString()); formDataToSend.append('start_date', start_date || ''); formDataToSend.append('end_date', end_date || ''); + formDataToSend.append('link', link || ''); let auth = event.cookies.get('auth'); diff --git a/frontend/src/routes/collections/+page.svelte b/frontend/src/routes/collections/+page.svelte index d675948..1563367 100644 --- a/frontend/src/routes/collections/+page.svelte +++ b/frontend/src/routes/collections/+page.svelte @@ -180,6 +180,7 @@ {collection} on:delete={deleteCollection} on:edit={editCollection} + adventures={collection.adventures} /> {/each}
diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index 97fe05d..ed8addb 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -20,7 +20,8 @@ groupAdventuresByDate, groupNotesByDate, groupTransportationsByDate, - groupChecklistsByDate + groupChecklistsByDate, + isAdventureVisited } from '$lib'; import ChecklistCard from '$lib/components/ChecklistCard.svelte'; import ChecklistModal from '$lib/components/ChecklistModal.svelte'; @@ -43,13 +44,12 @@ let numberOfDays: number = NaN; $: { - numAdventures = adventures.filter((a) => a.type === 'visited' || a.type === 'planned').length; - numVisited = adventures.filter((a) => a.type === 'visited').length; + numAdventures = adventures.length; + numVisited = adventures.filter(isAdventureVisited).length; } let notFound: boolean = false; let isShowingLinkModal: boolean = false; - let isShowingCreateModal: boolean = false; let isShowingTransportationModal: boolean = false; let isShowingChecklistModal: boolean = false; @@ -371,6 +371,14 @@ {#if collection.name}

{collection.name}

{/if} + {#if collection.link} + + {/if} + {#if collection.description}

{collection.description}

{/if} diff --git a/frontend/src/routes/profile/+page.svelte b/frontend/src/routes/profile/+page.svelte index 4265d7f..8f3f5c5 100644 --- a/frontend/src/routes/profile/+page.svelte +++ b/frontend/src/routes/profile/+page.svelte @@ -3,10 +3,9 @@ let stats: { country_count: number; - planned_count: number; total_regions: number; trips_count: number; - visited_count: number; + adventure_count: number; visited_region_count: number; total_countries: number; } | null; @@ -19,16 +18,6 @@ console.log(stats); - - - - {#if data.user.profile_pic}
@@ -65,17 +54,10 @@
-
Completed Adventures
-
{stats.visited_count}
+
Adventures
+
{stats.adventure_count}
- -
-
Planned Adventures
-
{stats.planned_count}
- -
-
Collections
{stats.trips_count}
diff --git a/frontend/src/routes/shared/+page.svelte b/frontend/src/routes/shared/+page.svelte index 7aa0e6c..231935e 100644 --- a/frontend/src/routes/shared/+page.svelte +++ b/frontend/src/routes/shared/+page.svelte @@ -25,3 +25,8 @@ {/if}

{/if} + + + Shared Collections + + diff --git a/screenshots/adventures.png b/screenshots/adventures.png index b2e6fc9..f49fbc3 100644 Binary files a/screenshots/adventures.png and b/screenshots/adventures.png differ diff --git a/screenshots/countries.png b/screenshots/countries.png index 7a51945..7b37ec8 100644 Binary files a/screenshots/countries.png and b/screenshots/countries.png differ diff --git a/screenshots/details.png b/screenshots/details.png index ed104ec..0dee567 100644 Binary files a/screenshots/details.png and b/screenshots/details.png differ diff --git a/screenshots/edit.png b/screenshots/edit.png index 25add10..8aea3a7 100644 Binary files a/screenshots/edit.png and b/screenshots/edit.png differ diff --git a/screenshots/itinerary.png b/screenshots/itinerary.png index 7b19478..e6107a6 100644 Binary files a/screenshots/itinerary.png and b/screenshots/itinerary.png differ diff --git a/screenshots/map.png b/screenshots/map.png index f60bf5f..e52799b 100644 Binary files a/screenshots/map.png and b/screenshots/map.png differ diff --git a/screenshots/regions.png b/screenshots/regions.png index 94577ff..f04a37f 100644 Binary files a/screenshots/regions.png and b/screenshots/regions.png differ