feat: refine itinerary flow and add OSRM connector metrics
This commit is contained in:
@@ -1,24 +1,35 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageServerLoad } from './$types';
|
||||
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
||||
import type { Location } from '$lib/types';
|
||||
import type { SlimCollection } from '$lib/types';
|
||||
|
||||
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
||||
|
||||
const defaultStats = {
|
||||
visited_country_count: 0,
|
||||
visited_region_count: 0,
|
||||
visited_city_count: 0,
|
||||
location_count: 0,
|
||||
trips_count: 0
|
||||
};
|
||||
|
||||
export const load = (async (event) => {
|
||||
if (!event.locals.user) {
|
||||
return redirect(302, '/login');
|
||||
} else {
|
||||
let adventures: Location[] = [];
|
||||
let collections: SlimCollection[] = [];
|
||||
|
||||
let initialFetch = await event.fetch(`${serverEndpoint}/api/locations/`, {
|
||||
headers: {
|
||||
Cookie: `sessionid=${event.cookies.get('sessionid')}`
|
||||
},
|
||||
credentials: 'include'
|
||||
});
|
||||
let initialFetch = await event.fetch(
|
||||
`${serverEndpoint}/api/collections/?order_by=updated_at&order_direction=desc&nested=true`,
|
||||
{
|
||||
headers: {
|
||||
Cookie: `sessionid=${event.cookies.get('sessionid')}`
|
||||
},
|
||||
credentials: 'include'
|
||||
}
|
||||
);
|
||||
|
||||
let stats = null;
|
||||
let stats = { ...defaultStats };
|
||||
|
||||
let res = await event.fetch(
|
||||
`${serverEndpoint}/api/stats/counts/${event.locals.user.username}/`,
|
||||
@@ -31,24 +42,27 @@ export const load = (async (event) => {
|
||||
if (!res.ok) {
|
||||
console.error('Failed to fetch user stats');
|
||||
} else {
|
||||
stats = await res.json();
|
||||
const statsPayload = await res.json();
|
||||
stats = {
|
||||
...defaultStats,
|
||||
...(statsPayload || {})
|
||||
};
|
||||
}
|
||||
|
||||
if (!initialFetch.ok) {
|
||||
let error_message = await initialFetch.json();
|
||||
console.error(error_message);
|
||||
console.error('Failed to fetch visited adventures');
|
||||
console.error('Failed to fetch recent collections');
|
||||
return redirect(302, '/login');
|
||||
} else {
|
||||
let res = await initialFetch.json();
|
||||
let visited = res.results as Location[];
|
||||
// only get the first 3 adventures or less if there are less than 3
|
||||
adventures = visited.slice(0, 3);
|
||||
let recentCollections = res.results as SlimCollection[];
|
||||
collections = recentCollections.slice(0, 3);
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
adventures,
|
||||
collections,
|
||||
stats
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import LocationCard from '$lib/components/cards/LocationCard.svelte';
|
||||
import CollectionCard from '$lib/components/cards/CollectionCard.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
export let data: PageData;
|
||||
|
||||
const user = data.user;
|
||||
const recentAdventures = data.props.adventures;
|
||||
const recentCollections = (data.props as any).collections ?? [];
|
||||
const stats = data.props.stats;
|
||||
|
||||
// Calculate completion percentage
|
||||
@@ -153,8 +153,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Adventures Section -->
|
||||
{#if recentAdventures.length > 0}
|
||||
<!-- Recent Collections Section -->
|
||||
{#if recentCollections.length > 0}
|
||||
<div class="mb-8">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div class="flex items-center gap-3">
|
||||
@@ -162,20 +162,20 @@
|
||||
<CalendarClock class="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-3xl font-bold">{$t('dashboard.recent_adventures')}</h2>
|
||||
<h2 class="text-3xl font-bold">{$t('navbar.collections')}</h2>
|
||||
<p class="text-base-content/60">{$t('home.latest_travel_experiences')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/locations" class="btn btn-ghost gap-2">
|
||||
<a href="/collections" class="btn btn-ghost gap-2">
|
||||
{$t('dashboard.view_all')}
|
||||
<span class="badge badge-primary">{stats.location_count}</span>
|
||||
<span class="badge badge-primary">{stats.trips_count ?? 0}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||
{#each recentAdventures as adventure}
|
||||
{#each recentCollections as collection}
|
||||
<div class="adventure-card">
|
||||
<LocationCard {adventure} readOnly user={null} />
|
||||
<CollectionCard {collection} type="viewonly" {user} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -183,7 +183,7 @@
|
||||
{/if}
|
||||
|
||||
<!-- Empty State / Inspiration -->
|
||||
{#if recentAdventures.length === 0}
|
||||
{#if recentCollections.length === 0}
|
||||
<div
|
||||
class="empty-state card bg-gradient-to-br from-base-100 to-base-200 shadow-2xl border border-base-300"
|
||||
>
|
||||
@@ -197,19 +197,19 @@
|
||||
<h2
|
||||
class="text-3xl font-bold mb-4 bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent"
|
||||
>
|
||||
{$t('dashboard.no_recent_adventures')}
|
||||
{$t('collection.no_collections_yet')}
|
||||
</h2>
|
||||
<p class="text-lg text-base-content/60 mb-8 max-w-md mx-auto leading-relaxed">
|
||||
{$t('dashboard.document_some_adventures')}
|
||||
{$t('collection.create_first')}
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<a
|
||||
href="/locations"
|
||||
href="/collections"
|
||||
class="btn btn-primary btn-lg gap-2 shadow-lg hover:shadow-xl transition-all duration-300"
|
||||
>
|
||||
<Plus class="w-5 h-5" />
|
||||
{$t('map.add_location')}
|
||||
{$t('collection.new_collection')}
|
||||
</a>
|
||||
<a href="/worldtravel" class="btn btn-outline btn-lg gap-2">
|
||||
<FlagCheckeredVariantIcon class="w-5 h-5" />
|
||||
|
||||
Reference in New Issue
Block a user