All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m21s
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>
110 lines
3.2 KiB
Svelte
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>
|