diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py
index 3800098..629d603 100644
--- a/backend/server/adventures/views.py
+++ b/backend/server/adventures/views.py
@@ -30,6 +30,7 @@ class AdventureViewSet(viewsets.ModelViewSet):
def apply_sorting(self, queryset):
order_by = self.request.query_params.get('order_by', 'name')
order_direction = self.request.query_params.get('order_direction', 'asc')
+ include_collections = self.request.query_params.get('include_collections', 'false')
valid_order_by = ['name', 'type', 'date', 'rating']
if order_by not in valid_order_by:
@@ -50,6 +51,9 @@ class AdventureViewSet(viewsets.ModelViewSet):
print(f"Ordering by: {ordering}") # For debugging
+ if include_collections == 'false':
+ queryset = queryset.filter(collection = None)
+
return queryset.order_by(ordering)
def get_queryset(self):
@@ -76,7 +80,7 @@ class AdventureViewSet(viewsets.ModelViewSet):
for adventure_type in types:
if adventure_type in ['visited', 'planned']:
queryset |= Adventure.objects.filter(
- type=adventure_type, user_id=request.user.id, collection=None)
+ type=adventure_type, user_id=request.user.id)
queryset = self.apply_sorting(queryset)
adventures = self.paginate_and_respond(queryset, request)
@@ -86,8 +90,25 @@ class AdventureViewSet(viewsets.ModelViewSet):
def all(self, request):
if not request.user.is_authenticated:
return Response({"error": "User is not authenticated"}, status=400)
- queryset = Adventure.objects.filter(user_id=request.user.id)
+ # include_collections = request.query_params.get('include_collections', 'false')
+ # if include_collections not in ['true', 'false']:
+ # include_collections = 'false'
+
+ # if include_collections == 'true':
+ # queryset = Adventure.objects.filter(
+ # Q(is_public=True) | Q(user_id=request.user.id)
+ # )
+ # else:
+ # queryset = Adventure.objects.filter(
+ # Q(is_public=True) | Q(user_id=request.user.id), collection=None
+ # )
+ queryset = Adventure.objects.filter(
+ Q(is_public=True) | Q(user_id=request.user.id)
+ )
+
+ queryset = self.apply_sorting(queryset)
serializer = self.get_serializer(queryset, many=True)
+
return Response(serializer.data)
def paginate_and_respond(self, queryset, request):
diff --git a/frontend/src/lib/components/AdventureCard.svelte b/frontend/src/lib/components/AdventureCard.svelte
index d2685d8..6510832 100644
--- a/frontend/src/lib/components/AdventureCard.svelte
+++ b/frontend/src/lib/components/AdventureCard.svelte
@@ -10,6 +10,7 @@
import Calendar from '~icons/mdi/calendar';
import MapMarker from '~icons/mdi/map-marker';
import { addToast } from '$lib/toasts';
+ import Link from '~icons/mdi/link-variant';
export let type: string;
@@ -34,6 +35,10 @@
function editAdventure() {
dispatch('edit', adventure);
}
+
+ function link() {
+ dispatch('link', adventure);
+ }
{/if}
+ {#if type == 'link'}
+
+ {/if}
diff --git a/frontend/src/lib/components/AdventureLink.svelte b/frontend/src/lib/components/AdventureLink.svelte
new file mode 100644
index 0000000..fd0d312
--- /dev/null
+++ b/frontend/src/lib/components/AdventureLink.svelte
@@ -0,0 +1,59 @@
+
+
+
diff --git a/frontend/src/lib/components/CollectionCard.svelte b/frontend/src/lib/components/CollectionCard.svelte
index 1cd8b8a..16fc6d4 100644
--- a/frontend/src/lib/components/CollectionCard.svelte
+++ b/frontend/src/lib/components/CollectionCard.svelte
@@ -8,25 +8,28 @@
import { goto } from '$app/navigation';
import type { Collection } from '$lib/types';
+ import { addToast } from '$lib/toasts';
const dispatch = createEventDispatcher();
// export let type: String;
export let collection: Collection;
- // function remove() {
- // dispatch("remove", trip.id);
- // }
- // function edit() {}
- // function add() {
- // dispatch("add", trip);
- // }
-
- // // TODO: Implement markVisited function
- // function markVisited() {
- // console.log(trip.id);
- // dispatch("markVisited", trip);
- // }
+ async function deleteCollection() {
+ let res = await fetch(`/collections/${collection.id}?/delete`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ }
+ });
+ if (res.ok) {
+ console.log('Collection deleted');
+ addToast('info', 'Adventure deleted successfully!');
+ dispatch('delete', collection.id);
+ } else {
+ console.log('Error deleting adventure');
+ }
+ }
{collection.name}
{collection.adventures.length} Adventures
-
-
diff --git a/frontend/src/routes/adventures/+page.server.ts b/frontend/src/routes/adventures/+page.server.ts
index aa37dac..fa30f52 100644
--- a/frontend/src/routes/adventures/+page.server.ts
+++ b/frontend/src/routes/adventures/+page.server.ts
@@ -362,6 +362,13 @@ export const actions: Actions = {
const visited = formData.get('visited');
const planned = formData.get('planned');
+ let include_collections = formData.get('include_collections') as string;
+
+ if (include_collections) {
+ include_collections = 'true';
+ } else {
+ include_collections = 'false';
+ }
const order_direction = formData.get('order_direction') as string;
const order_by = formData.get('order_by') as string;
@@ -397,7 +404,7 @@ export const actions: Actions = {
console.log(filterString);
let visitedFetch = await fetch(
- `${serverEndpoint}/api/adventures/filtered?types=${filterString}&order_by=${order_by}&order_direction=${order_direction}`,
+ `${serverEndpoint}/api/adventures/filtered?types=${filterString}&order_by=${order_by}&order_direction=${order_direction}&include_collections=${include_collections}`,
{
headers: {
Cookie: `${event.cookies.get('auth')}`
@@ -502,5 +509,46 @@ export const actions: Actions = {
body: { error: 'Failed to fetch data' }
};
}
+ },
+ all: async (event) => {
+ if (!event.locals.user) {
+ return {
+ status: 401,
+ body: { message: 'Unauthorized' }
+ };
+ }
+
+ const formData = await event.request.formData();
+
+ let include_collections = formData.get('include_collections') as string;
+
+ if (include_collections !== 'true' && include_collections !== 'false') {
+ include_collections = 'false';
+ }
+
+ let adventures: Adventure[] = [];
+
+ let visitedFetch = await fetch(
+ `${serverEndpoint}/api/adventures/all/?include_collections=${include_collections}`,
+ {
+ headers: {
+ Cookie: `${event.cookies.get('auth')}`,
+ 'Content-Type': 'application/json'
+ }
+ }
+ );
+ if (!visitedFetch.ok) {
+ console.error('Failed to fetch all adventures');
+ return redirect(302, '/login');
+ } else {
+ console.log('Fetched all adventures');
+ let res = await visitedFetch.json();
+ console.log(res);
+ adventures = res as Adventure[];
+ }
+
+ return {
+ adventures
+ };
}
};
diff --git a/frontend/src/routes/adventures/+page.svelte b/frontend/src/routes/adventures/+page.svelte
index 3361fe3..cc819ab 100644
--- a/frontend/src/routes/adventures/+page.svelte
+++ b/frontend/src/routes/adventures/+page.svelte
@@ -281,6 +281,16 @@
id="rating"
class="radio radio-primary"
/>
+
+
Filter
diff --git a/frontend/src/routes/adventures/[id]/+page.server.ts b/frontend/src/routes/adventures/[id]/+page.server.ts
index 1f3eb48..109c54e 100644
--- a/frontend/src/routes/adventures/[id]/+page.server.ts
+++ b/frontend/src/routes/adventures/[id]/+page.server.ts
@@ -1,4 +1,3 @@
-import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
import type { Adventure } from '$lib/types';
@@ -89,5 +88,74 @@ export const actions: Actions = {
status: 204
};
}
+ },
+ addToCollection: async (event) => {
+ const id = event.params as { id: string };
+ const adventureId = id.id;
+
+ const formData = await event.request.formData();
+ const trip_id = formData.get('collection_id');
+
+ if (!trip_id) {
+ return {
+ status: 400,
+ error: { message: 'Missing collection id' }
+ };
+ }
+
+ if (!event.locals.user) {
+ const refresh = event.cookies.get('refresh');
+ let auth = event.cookies.get('auth');
+ if (!refresh) {
+ return {
+ status: 401,
+ body: { message: 'Unauthorized' }
+ };
+ }
+ let res = await tryRefreshToken(refresh);
+ if (res) {
+ auth = res;
+ event.cookies.set('auth', auth, {
+ httpOnly: true,
+ sameSite: 'lax',
+ expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
+ path: '/'
+ });
+ } else {
+ return {
+ status: 401,
+ body: { message: 'Unauthorized' }
+ };
+ }
+ }
+ if (!adventureId) {
+ return {
+ status: 400,
+ error: new Error('Bad request')
+ };
+ }
+
+ let trip_id_number: number = parseInt(trip_id as string);
+
+ let res = await fetch(`${serverEndpoint}/api/adventures/${event.params.id}/`, {
+ method: 'PATCH',
+ headers: {
+ Cookie: `${event.cookies.get('auth')}`,
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ collection: trip_id_number })
+ });
+ let res2 = await res.json();
+ console.log(res2);
+ if (!res.ok) {
+ return {
+ status: res.status,
+ error: new Error('Failed to delete adventure')
+ };
+ } else {
+ return {
+ status: 204
+ };
+ }
}
};
diff --git a/frontend/src/routes/collections/+page.svelte b/frontend/src/routes/collections/+page.svelte
index c12177d..9594661 100644
--- a/frontend/src/routes/collections/+page.svelte
+++ b/frontend/src/routes/collections/+page.svelte
@@ -66,6 +66,10 @@
};
}
+ function deleteCollection(event: CustomEvent
) {
+ collections = collections.filter((collection) => collection.id !== event.detail);
+ }
+
function sort({ attribute, order }: { attribute: string; order: string }) {
currentSort.attribute = attribute;
currentSort.order = order;
@@ -174,7 +178,7 @@
{#if currentView == 'cards'}
{#each collections as collection}
-
+
{/each}
{/if}
diff --git a/frontend/src/routes/collections/[id]/+page.server.ts b/frontend/src/routes/collections/[id]/+page.server.ts
index 3a2de9d..80d7ef9 100644
--- a/frontend/src/routes/collections/[id]/+page.server.ts
+++ b/frontend/src/routes/collections/[id]/+page.server.ts
@@ -1,7 +1,7 @@
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
-import type { Adventure } from '$lib/types';
+import type { Adventure, Collection } from '$lib/types';
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
export const load = (async (event) => {
@@ -19,7 +19,7 @@ export const load = (async (event) => {
}
};
} else {
- let collection = (await request.json()) as Adventure;
+ let collection = (await request.json()) as Collection;
return {
props: {
@@ -71,18 +71,19 @@ export const actions: Actions = {
};
}
- let res = await fetch(`${serverEndpoint}/api/adventures/${event.params.id}`, {
+ let res = await fetch(`${serverEndpoint}/api/collections/${event.params.id}`, {
method: 'DELETE',
headers: {
Cookie: `${event.cookies.get('auth')}`,
'Content-Type': 'application/json'
}
});
+
console.log(res);
if (!res.ok) {
return {
status: res.status,
- error: new Error('Failed to delete adventure')
+ error: new Error('Failed to delete collection')
};
} else {
return {
diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte
index ad938e9..fee2bae 100644
--- a/frontend/src/routes/collections/[id]/+page.svelte
+++ b/frontend/src/routes/collections/[id]/+page.svelte
@@ -1,40 +1,65 @@
-
-
+{#if isShowingCreateModal}
+ {
+ isShowingCreateModal = false;
+ }}
+ on:add={addAdventure}
+ />
+{/if}
+
{#if notFound}
{/if}
-{#if !adventure && !notFound}
+{#if !collection && !notFound}
{/if}
-{#if adventure}
- {#if adventure.name}
-
- {/if}
- {#if adventure.location}
-
- {adventure.location}
-
- {/if}
- {#if adventure.date}
-
- Visited on: {adventure.date}
-
- {/if}
- {#if adventure.rating !== undefined && adventure.rating !== null}
-
-
- {#each Array.from({ length: 5 }, (_, i) => i + 1) as star}
-
- {/each}
+{#if collection}
+
+
+
+
+
+
+ Link new...
+ {
+ isShowingCreateModal = true;
+ }}
+ >
+ Adventure
+
+
+
+
+ {#if collection.name}
+
{/if}
- {#if adventure.description}
-
{adventure.description}
- {/if}
- {#if adventure.link}
-
- {/if}
- {#if adventure.activity_types && adventure.activity_types.length > 0}
-
-
Activities: 
-
- {#each adventure.activity_types as activity}
-
- {activity}
-
- {/each}
-
-
- {/if}
- {#if adventure.image}
-
-
-

-
+
Linked Adventures
+
+ {#each adventures as adventure}
+
+ {/each}
+
+
+ {#if collection.description}
+
{collection.description}
{/if}
{/if}
diff --git a/frontend/src/routes/profile/+page.svelte b/frontend/src/routes/profile/+page.svelte
index d3ff303..75b1553 100644
--- a/frontend/src/routes/profile/+page.svelte
+++ b/frontend/src/routes/profile/+page.svelte
@@ -75,7 +75,7 @@
-
Trips
+
Collections
{stats.trips_count}