Files
trips/src/lib/components/TourTimelineCard.svelte
AI Agent ae39f7961a
All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m21s
trip) add timeline view to trip page (#48)
Co-authored-by: Shaun Campbell <shaun@campbellwireless.net>
Reviewed-on: #48
Co-authored-by: AI Agent <ai-agent@campbellwireless.net>
Co-committed-by: AI Agent <ai-agent@campbellwireless.net>
2026-02-23 03:17:44 +00:00

110 lines
3.2 KiB
Svelte

<script lang="ts">
import TourBadge from '$lib/components/TourBadge.svelte';
import type { Plan } from '$lib/server/plans.js';
import type { PackageTour } from '$lib/server/package-tours.js';
interface Props {
plan: Plan;
tour: PackageTour & { highlight_color: string | null };
onEdit?: () => void;
onDelete?: () => void;
}
let { plan, tour, onEdit, onDelete }: Props = $props();
function formatDate(d: string | null): string {
if (!d) return '';
return new Date(d + 'T00:00:00').toLocaleDateString(undefined, {
day: 'numeric',
month: 'short',
year: 'numeric'
});
}
function formatTime(t: string | null): string {
if (!t) return '';
return t.slice(0, 5);
}
const statusConfig = {
idea: { label: 'Idea', class: 'bg-gray-100 text-gray-600' },
tentative: { label: 'Tentative', class: 'bg-yellow-100 text-yellow-800' },
confirmed: { label: 'Confirmed', class: 'bg-green-100 text-green-800' }
};
const hasStart = $derived(tour.start_date || tour.start_time);
const hasEnd = $derived(tour.end_date || tour.end_time);
</script>
<div
class="rounded-xl border-2 bg-white p-4 shadow-sm"
style="border-color: {tour.highlight_color ?? '#E5E7EB'}"
>
<div class="flex items-start justify-between gap-3">
<TourBadge
operatorName={tour.operator_name}
tourName={tour.tour_name}
highlightColor={tour.highlight_color}
size="md"
/>
<div class="flex shrink-0 items-center gap-1">
<span
class="rounded-full px-2.5 py-0.5 text-xs font-medium {statusConfig[plan.status].class}"
>
{statusConfig[plan.status].label}
</span>
{#if onEdit}
<button
type="button"
onclick={onEdit}
class="rounded-md p-1.5 text-gray-400 hover:bg-gray-100 hover:text-gray-600"
aria-label="Edit tour"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
</svg>
</button>
{/if}
{#if onDelete}
<button
type="button"
onclick={onDelete}
class="rounded-md p-1.5 text-gray-400 hover:bg-gray-100 hover:text-red-600"
aria-label="Remove tour"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
{/if}
</div>
</div>
{#if hasStart || hasEnd}
<div class="mt-3 grid grid-cols-2 gap-4 border-t border-gray-100 pt-3">
<div>
<p class="text-xs font-medium tracking-wide text-gray-400 uppercase">Start</p>
<p class="mt-0.5 text-sm text-gray-900">
{#if tour.start_date}
{formatDate(tour.start_date)} {formatTime(tour.start_time)}
{:else}
{formatTime(tour.start_time)}
{/if}
</p>
</div>
<div>
<p class="text-xs font-medium tracking-wide text-gray-400 uppercase">End</p>
<p class="mt-0.5 text-sm text-gray-900">
{#if tour.end_date}
{formatDate(tour.end_date)} {formatTime(tour.end_time)}
{:else}
{formatTime(tour.end_time)}
{/if}
</p>
</div>
</div>
{/if}
</div>