1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-08-07 22:25:19 +02:00

feat: add measurement system support across ActivityCard, StravaActivityCard, NewLocationModal, LocationVisits, and related utility functions

This commit is contained in:
Sean Morley 2025-08-05 12:42:43 -04:00
parent 31630de4fd
commit 65f99c5a50
6 changed files with 79 additions and 16 deletions

View file

@ -6,11 +6,14 @@
import FileIcon from '~icons/mdi/file';
import TrashIcon from '~icons/mdi/trash-can';
import { formatDateInTimezone } from '$lib/dateUtils';
import { getDistance, getElevation } from '$lib';
export let activity: Activity;
export let trails: Trail[];
export let visit: Visit | TransportationVisit;
export let measurementSystem: 'metric' | 'imperial' = 'metric';
export let readOnly: boolean = false;
$: trail = activity.trail ? trails.find((t) => t.id === activity.trail) : null;
@ -35,7 +38,10 @@
<div class="text-xs text-base-content/70 space-y-1">
{#if activity.distance}
<div class="flex items-center gap-4">
<span>Distance: {activity.distance} km</span>
<span
>Distance: {getDistance(measurementSystem, activity.distance)}
{measurementSystem === 'imperial' ? 'miles' : 'km'}</span
>
{#if activity.moving_time}
<span>Time: {activity.moving_time}</span>
{/if}
@ -45,10 +51,16 @@
{#if activity.elevation_gain || activity.elevation_loss}
<div class="flex items-center gap-4">
{#if activity.elevation_gain}
<span>{activity.elevation_gain}m</span>
<span
>↗ {getElevation(measurementSystem, activity.elevation_gain)}
{measurementSystem === 'imperial' ? 'feet' : 'm'}</span
>
{/if}
{#if activity.elevation_loss}
<span>{activity.elevation_loss}m</span>
<span
>↘ {getElevation(measurementSystem, activity.elevation_loss)}
{measurementSystem === 'imperial' ? 'feet' : 'm'}</span
>
{/if}
</div>
{/if}

View file

@ -294,6 +294,7 @@
steps[2].selected = true;
}}
on:close={() => close()}
measurementSystem={user?.measurement_system || 'metric'}
/>
{/if}
</div>

View file

@ -6,6 +6,7 @@
const dispatch = createEventDispatcher();
export let activity: StravaActivity;
export let measurementSystem: 'metric' | 'imperial' = 'metric';
interface SportConfig {
color: string;
@ -48,10 +49,21 @@
});
}
function formatPace(seconds: number): string {
function formatPace(seconds: number, system: 'metric' | 'imperial'): string {
const minutes = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${minutes}:${secs.toString().padStart(2, '0')}`;
const unit = system === 'metric' ? 'km' : 'mi';
return `${minutes}:${secs.toString().padStart(2, '0')}/${unit}`;
}
function convertElevation(
meters: number,
system: 'metric' | 'imperial'
): { value: number; unit: string } {
if (system === 'imperial') {
return { value: meters * 3.28084, unit: 'ft' };
}
return { value: meters, unit: 'm' };
}
function handleImportActivity() {
@ -59,6 +71,21 @@
}
$: typeConfig = getTypeConfig(activity.sport_type);
$: distance =
measurementSystem === 'metric'
? { value: activity.distance_km, unit: 'km' }
: { value: activity.distance_miles, unit: 'mi' };
$: speed =
measurementSystem === 'metric'
? { value: activity.average_speed_kmh, unit: 'km/h' }
: { value: activity.average_speed_mph, unit: 'mph' };
$: maxSpeed =
measurementSystem === 'metric'
? { value: activity.max_speed_kmh, unit: 'km/h' }
: { value: activity.max_speed_mph, unit: 'mph' };
$: elevation = convertElevation(activity.total_elevation_gain, measurementSystem);
$: paceSeconds =
measurementSystem === 'metric' ? activity.pace_per_km_seconds : activity.pace_per_mile_seconds;
</script>
<div class="card bg-base-50 border border-base-200 hover:shadow-md transition-shadow">
@ -147,8 +174,8 @@
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
<div class="stat bg-base-100 rounded-lg p-3">
<div class="stat-title text-xs">Distance</div>
<div class="stat-value text-lg">{activity.distance_km.toFixed(2)}</div>
<div class="stat-desc">km ({activity.distance_miles.toFixed(2)} mi)</div>
<div class="stat-value text-lg">{distance.value.toFixed(2)}</div>
<div class="stat-desc">{distance.unit}</div>
</div>
<div class="stat bg-base-100 rounded-lg p-3">
<div class="stat-title text-xs">Time</div>
@ -157,13 +184,13 @@
</div>
<div class="stat bg-base-100 rounded-lg p-3">
<div class="stat-title text-xs">Avg Speed</div>
<div class="stat-value text-lg">{activity.average_speed_kmh.toFixed(1)}</div>
<div class="stat-desc">km/h ({activity.average_speed_mph.toFixed(1)} mph)</div>
<div class="stat-value text-lg">{speed.value.toFixed(1)}</div>
<div class="stat-desc">{speed.unit}</div>
</div>
<div class="stat bg-base-100 rounded-lg p-3">
<div class="stat-title text-xs">Elevation</div>
<div class="stat-value text-lg">{activity.total_elevation_gain.toFixed(0)}</div>
<div class="stat-desc">m gain</div>
<div class="stat-value text-lg">{elevation.value.toFixed(0)}</div>
<div class="stat-desc">{elevation.unit} gain</div>
</div>
</div>
@ -197,15 +224,16 @@
</div>
<!-- Footer with pace and max speed -->
{#if activity.pace_per_km_seconds}
{#if paceSeconds}
<div class="flex justify-between items-center mt-3 pt-3 border-t border-base-300">
<div class="text-sm">
<span class="font-medium">Pace:</span>
{formatPace(activity.pace_per_km_seconds)}/km
{formatPace(paceSeconds, measurementSystem)}
</div>
<div class="text-sm">
<span class="font-medium">Max Speed:</span>
{activity.max_speed_kmh.toFixed(1)} km/h
{maxSpeed.value.toFixed(1)}
{maxSpeed.unit}
</div>
</div>
{/if}

View file

@ -45,6 +45,7 @@
export let visits: (Visit | TransportationVisit)[] | null = null;
export let objectId: string;
export let trails: Trail[] = [];
export let measurementSystem: 'metric' | 'imperial' = 'metric';
const dispatch = createEventDispatcher();
@ -627,7 +628,7 @@
name: stravaActivity.name,
type: stravaActivity.type,
sport_type: stravaActivity.sport_type || stravaActivity.type,
distance: stravaActivity.distance ? stravaActivity.distance / 1000 : null, // Convert to km
distance: stravaActivity.distance || null, // Keep in meters
moving_time: stravaActivity.moving_time ? formatDuration(stravaActivity.moving_time) : '',
elapsed_time: stravaActivity.elapsed_time
? formatDuration(stravaActivity.elapsed_time)
@ -1175,7 +1176,6 @@
class="input input-bordered input-sm w-full mt-1"
placeholder="Morning Run"
bind:value={activityForm.name}
readonly={!!pendingStravaImport[visit.id]}
/>
</div>
@ -1588,6 +1588,7 @@
{activity}
{trails}
{visit}
{measurementSystem}
on:delete={(event) =>
deleteActivity(event.detail.visitId, event.detail.activityId)}
/>
@ -1619,6 +1620,7 @@
<StravaActivityCard
{activity}
on:import={(event) => handleStravaActivityImport(event, visit.id)}
{measurementSystem}
/>
</div>
{/each}

View file

@ -623,3 +623,22 @@ export function getBasemapUrl() {
}
return 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json';
}
export function getDistance(measurementSystem: 'metric' | 'imperial', meters: number): string {
if (measurementSystem === 'imperial') {
const miles = meters / 1609.34;
return miles.toFixed(2);
} else {
const km = meters / 1000;
return km.toFixed(2);
}
}
export function getElevation(measurementSystem: 'metric' | 'imperial', elevation: number): string {
if (measurementSystem === 'imperial') {
const feet = elevation * 3.28084;
return Math.round(feet).toString();
} else {
return Math.round(elevation).toString();
}
}

View file

@ -613,6 +613,7 @@
readOnly={true}
trails={adventure.trails}
{visit}
measurementSystem={data.user?.measurement_system || 'metric'}
/>
{/each}
</div>