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

feat: add TrailCard component for displaying trail details with measurement system support

This commit is contained in:
Sean Morley 2025-08-05 13:25:07 -04:00
parent 546ae7aa2e
commit eb3c9c2b64
2 changed files with 160 additions and 27 deletions

View file

@ -0,0 +1,155 @@
<script lang="ts">
import type { Trail } from '$lib/types';
// Icons (only those used)
import Calendar from '~icons/mdi/calendar';
import Camera from '~icons/mdi/camera';
import Clock from '~icons/mdi/clock';
import MapPin from '~icons/mdi/map-marker';
import TrendingUp from '~icons/mdi/trending-up';
import Users from '~icons/mdi/account-supervisor';
export let trail: Trail;
export let measurementSystem: 'metric' | 'imperial' = 'metric';
function getDistance(meters: number) {
return measurementSystem === 'imperial'
? `${(meters * 0.000621371).toFixed(2)} mi`
: `${(meters / 1000).toFixed(2)} km`;
}
function getElevation(meters: number) {
return measurementSystem === 'imperial'
? `${(meters * 3.28084).toFixed(1)} ft`
: `${meters.toFixed(1)} m`;
}
function getDuration(minutes: number) {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
return hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
}
function formatDate(date: string | number | Date) {
return new Date(date).toLocaleDateString();
}
function pluralize(count: number, singular: string, plural = singular + 's') {
return `${count} ${count === 1 ? singular : plural}`;
}
</script>
<div class="card bg-base-100 shadow">
<div class="card-body p-4">
<div class="flex items-start justify-between">
<div class="flex-1">
<!-- Trail Name -->
<h2 class="text-xl font-bold leading-tight mb-1">{trail.name}</h2>
<!-- Provider + Created Date -->
<div class="flex items-center gap-2 mb-2">
{#if trail.provider}
<span class="badge badge-outline badge-sm">{trail.provider}</span>
{/if}
<span class="text-sm opacity-70">
Created: {formatDate(trail.created_at)}
</span>
</div>
{#if trail.wanderer_data}
<div class="mb-4 space-y-3">
<!-- Trail Stats -->
<div class="grid grid-cols-2 gap-3">
<div class="flex items-center gap-2 text-sm">
<MapPin class="w-3 h-3 text-base-content/60" />
<span class="text-base-content/80">
{getDistance(trail.wanderer_data.distance)}
</span>
</div>
{#if trail.wanderer_data.duration > 0}
<div class="flex items-center gap-2 text-sm">
<Clock class="w-3 h-3 text-base-content/60" />
<span class="text-base-content/80">
{getDuration(trail.wanderer_data.duration)}
</span>
</div>
{/if}
{#if trail.wanderer_data.elevation_gain > 0}
<div class="flex items-center gap-2 text-sm">
<TrendingUp class="w-3 h-3 text-base-content/60" />
<span class="text-base-content/80">
{getElevation(trail.wanderer_data.elevation_gain)} gain
</span>
</div>
{/if}
<div class="flex items-center gap-2 text-sm">
<Calendar class="w-3 h-3 text-base-content/60" />
<span class="text-base-content/80">
{formatDate(trail.wanderer_data.date)}
</span>
</div>
</div>
<!-- Difficulty + Likes -->
{#if trail.wanderer_data.difficulty}
<div class="flex items-center gap-2">
<span
class="badge badge-sm"
class:badge-success={trail.wanderer_data.difficulty === 'easy'}
class:badge-warning={trail.wanderer_data.difficulty === 'moderate'}
class:badge-error={trail.wanderer_data.difficulty === 'hard'}
>
{trail.wanderer_data.difficulty}
</span>
{#if trail.wanderer_data.like_count > 0}
<div class="flex items-center gap-1 text-xs text-base-content/60">
<Users class="w-3 h-3" />
{pluralize(trail.wanderer_data.like_count, 'like')}
</div>
{/if}
</div>
{/if}
<!-- Description -->
{#if trail.wanderer_data.description}
<div class="text-sm text-base-content/70 leading-relaxed">
{@html trail.wanderer_data.description}
</div>
{/if}
<!-- Location -->
{#if trail.wanderer_data.location}
<div class="text-xs text-base-content/60 flex items-center gap-1">
<MapPin class="w-3 h-3" />
{trail.wanderer_data.location}
</div>
{/if}
<!-- Photos -->
{#if trail.wanderer_data.photos?.length > 0}
<div class="flex items-center gap-1 text-xs text-base-content/60">
<Camera class="w-3 h-3" />
{pluralize(trail.wanderer_data.photos.length, 'photo')}
</div>
{/if}
</div>
{/if}
</div>
{#if trail.link}
<a
href={trail.link}
target="_blank"
rel="noopener noreferrer"
class="btn btn-sm btn-primary"
>
🔗 View Trail
</a>
{/if}
</div>
</div>
</div>

View file

@ -21,6 +21,7 @@
import AttachmentCard from '$lib/components/AttachmentCard.svelte';
import { getBasemapUrl, isAllDay } from '$lib';
import ActivityCard from '$lib/components/ActivityCard.svelte';
import TrailCard from '$lib/components/TrailCard.svelte';
let geojson: any;
@ -504,33 +505,10 @@
<h2 class="card-title text-2xl mb-6">🥾 {$t('adventures.trails')}</h2>
<div class="grid gap-4">
{#each adventure.trails as trail}
<div class="card bg-base-100 shadow">
<div class="card-body p-4">
<div class="flex items-start justify-between">
<div class="flex-1">
<h3 class="font-bold text-lg">{trail.name}</h3>
<div class="flex items-center gap-2 mt-2">
{#if trail.provider}
<span class="badge badge-outline badge-sm">{trail.provider}</span>
{/if}
<span class="text-sm opacity-70">
Created: {new Date(trail.created_at).toLocaleDateString()}
</span>
</div>
</div>
{#if trail.link}
<a
href={trail.link}
target="_blank"
rel="noopener noreferrer"
class="btn btn-sm btn-primary"
>
🔗 View Trail
</a>
{/if}
</div>
</div>
</div>
<TrailCard
{trail}
measurementSystem={data.user?.measurement_system || 'metric'}
/>
{/each}
</div>
</div>