1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-19 12:59:36 +02:00

feat: add distance calculation to Transportation model and update TransportationCard to display distance in km and miles

This commit is contained in:
Sean Morley 2025-05-30 12:33:30 -04:00
parent 53d370297e
commit 514ee85767
3 changed files with 62 additions and 31 deletions

View file

@ -5,6 +5,7 @@ from rest_framework import serializers
from main.utils import CustomModelSerializer
from users.serializers import CustomUserDetailsSerializer
from worldtravel.serializers import CountrySerializer, RegionSerializer, CitySerializer
from geopy.distance import geodesic
class AdventureImageSerializer(CustomModelSerializer):
@ -194,15 +195,31 @@ class AdventureSerializer(CustomModelSerializer):
return instance
class TransportationSerializer(CustomModelSerializer):
distance = serializers.SerializerMethodField()
class Meta:
model = Transportation
fields = [
'id', 'user_id', 'type', 'name', 'description', 'rating',
'link', 'date', 'flight_number', 'from_location', 'to_location',
'is_public', 'collection', 'created_at', 'updated_at', 'end_date', 'origin_latitude', 'origin_longitude', 'destination_latitude', 'destination_longitude', 'start_timezone', 'end_timezone'
'is_public', 'collection', 'created_at', 'updated_at', 'end_date',
'origin_latitude', 'origin_longitude', 'destination_latitude', 'destination_longitude',
'start_timezone', 'end_timezone', 'distance' # ✅ Add distance here
]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'distance']
def get_distance(self, obj):
if (
obj.origin_latitude and obj.origin_longitude and
obj.destination_latitude and obj.destination_longitude
):
try:
origin = (float(obj.origin_latitude), float(obj.origin_longitude))
destination = (float(obj.destination_latitude), float(obj.destination_longitude))
return round(geodesic(origin, destination).km, 2)
except ValueError:
return None
return None
class LodgingSerializer(CustomModelSerializer):

View file

@ -23,6 +23,8 @@
export let user: User | null = null;
export let collection: Collection | null = null;
const toMiles = (km: any) => (Number(km) * 0.621371).toFixed(1);
let isWarningModalOpen: boolean = false;
function editTransportation() {
@ -109,14 +111,14 @@
<div
class="card w-full max-w-md bg-base-300 text-base-content shadow-2xl hover:shadow-3xl transition-all duration-300 border border-base-300 hover:border-primary/20 group"
>
<div class="card-body p-6 space-y-4">
<!-- Title & Mode -->
<div class="card-body p-6 space-y-6">
<!-- Header -->
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
<h2 class="card-title text-xl font-semibold truncate">{transportation.name}</h2>
<h2 class="text-xl font-bold truncate">{transportation.name}</h2>
<div class="flex flex-wrap gap-2">
<div class="badge badge-secondary">
{$t(`transportation.modes.${transportation.type}`)}
{' '}{getTransportationIcon(transportation.type)}
{getTransportationIcon(transportation.type)}
</div>
{#if transportation.type === 'plane' && transportation.flight_number}
<div class="badge badge-neutral">{transportation.flight_number}</div>
@ -127,50 +129,61 @@
</div>
</div>
<!-- Start Section -->
<div class="space-y-2">
<!-- Route Info -->
<div class="space-y-3">
{#if transportation.from_location}
<div class="flex items-center gap-2">
<span class="text-sm font-medium">{$t('adventures.from')}:</span>
<p class="text-sm break-words">{transportation.from_location}</p>
<div class="flex gap-2 text-sm">
<span class="font-medium whitespace-nowrap">{$t('adventures.from')}:</span>
<span class="break-words">{transportation.from_location}</span>
</div>
{/if}
{#if transportation.date}
<div class="flex items-center gap-2">
<span class="text-sm font-medium">{$t('adventures.start')}:</span>
<p class="text-sm">
{formatDateInTimezone(transportation.date, transportation.start_timezone)}
{#if transportation.start_timezone}
<span class="ml-1 text-xs opacity-60">({transportation.start_timezone})</span>
{/if}
</p>
{#if transportation.to_location}
<div class="flex gap-2 text-sm">
<span class="font-medium whitespace-nowrap">{$t('adventures.to')}:</span>
<span class="break-words">{transportation.to_location}</span>
</div>
{/if}
{#if transportation.distance && !isNaN(+transportation.distance)}
<div class="flex gap-2 text-sm">
<span class="font-medium whitespace-nowrap">{$t('adventures.distance')}:</span>
<span>
{(+transportation.distance).toFixed(1)} km / {toMiles(transportation.distance)} mi
</span>
</div>
{/if}
</div>
<!-- End Section -->
<div class="space-y-2">
{#if transportation.to_location}
<div class="flex items-center gap-2">
<span class="text-sm font-medium">{$t('adventures.to')}:</span>
<p class="text-sm break-words">{transportation.to_location}</p>
<!-- Time Info -->
<div class="space-y-3">
{#if transportation.date}
<div class="flex gap-2 text-sm">
<span class="font-medium whitespace-nowrap">{$t('adventures.start')}:</span>
<span>
{formatDateInTimezone(transportation.date, transportation.start_timezone)}
{#if transportation.start_timezone}
<span class="ml-1 text-xs opacity-60">({transportation.start_timezone})</span>
{/if}
</span>
</div>
{/if}
{#if transportation.end_date}
<div class="flex items-center gap-2">
<span class="text-sm font-medium">{$t('adventures.end')}:</span>
<p class="text-sm">
<div class="flex gap-2 text-sm">
<span class="font-medium whitespace-nowrap">{$t('adventures.end')}:</span>
<span>
{formatDateInTimezone(transportation.end_date, transportation.end_timezone)}
{#if transportation.end_timezone}
<span class="ml-1 text-xs opacity-60">({transportation.end_timezone})</span>
{/if}
</p>
</span>
</div>
{/if}
</div>
<!-- Actions -->
{#if transportation.user_id == user?.uuid || (collection && user && collection.shared_with?.includes(user.uuid))}
{#if transportation.user_id === user?.uuid || (collection && user && collection.shared_with?.includes(user.uuid))}
<div class="pt-4 border-t border-base-300 flex justify-end gap-2">
<button
class="btn btn-neutral btn-sm flex items-center gap-1"

View file

@ -170,6 +170,7 @@ export type Transportation = {
destination_latitude: number | null;
destination_longitude: number | null;
is_public: boolean;
distance: number | null; // in kilometers
collection: Collection | null | string;
created_at: string; // ISO 8601 date string
updated_at: string; // ISO 8601 date string