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:
parent
31630de4fd
commit
65f99c5a50
6 changed files with 79 additions and 16 deletions
|
@ -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}
|
||||
|
|
|
@ -294,6 +294,7 @@
|
|||
steps[2].selected = true;
|
||||
}}
|
||||
on:close={() => close()}
|
||||
measurementSystem={user?.measurement_system || 'metric'}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -613,6 +613,7 @@
|
|||
readOnly={true}
|
||||
trails={adventure.trails}
|
||||
{visit}
|
||||
measurementSystem={data.user?.measurement_system || 'metric'}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue