import { useState, useEffect } from "preact/hooks"; import type { Trip } from "../lib/types"; import { useLocation } from "../lib/use-location"; import { useTripTime } from "../lib/use-trip-time"; import { NowBanner } from "./NowBanner"; import { ItineraryTab } from "./tabs/ItineraryTab"; import { MapTab } from "./tabs/MapTab"; import { FoodTab } from "./tabs/FoodTab"; import { OnRoadTab } from "./tabs/OnRoadTab"; import { SafetyTab } from "./tabs/SafetyTab"; import { LandmarksTab } from "./tabs/LandmarksTab"; import { WeatherTab } from "./tabs/WeatherTab"; import { ActualTab } from "./tabs/ActualTab"; import { WrapupTab } from "./tabs/WrapupTab"; const PLAN_TABS = [ { id: "itinerary", label: "Trip" }, { id: "map", label: "Map" }, { id: "weather", label: "Weather" }, { id: "landmarks", label: "Landmarks" }, { id: "food", label: "Food" }, { id: "onroad", label: "On Road" }, { id: "safety", label: "Safety" }, ] as const; type PlanTabId = typeof PLAN_TABS[number]["id"]; type TabId = `day-${number}` | PlanTabId | "wrapup"; type Group = "Review Trip" | "During Trip" | "Plan Trip"; const GROUP_SLUGS: Record = { "Review Trip": "review-trip", "During Trip": "during-trip", "Plan Trip": "plan-trip", }; const SLUG_TO_GROUP: Record = { "review-trip": "Review Trip", "during-trip": "During Trip", "plan-trip": "Plan Trip", }; function parsePath(tripId: string): { groupSlug: string; tabSlug: string } { const prefix = `/travel/${tripId}/`; const rel = window.location.pathname.startsWith(prefix) ? window.location.pathname.slice(prefix.length).replace(/\/$/, "") : ""; const parts = rel ? rel.split("/") : []; return { groupSlug: parts[0] || "", tabSlug: parts[1] || "" }; } export function TripView({ trip, onBack }: { trip: Trip; onBack?: () => void }) { const dayTabs = trip.actuals.map((d, i) => ({ id: `day-${i}` as TabId, label: d.label })); const reversedDayTabs = [...dayTabs].reverse(); const tabsByGroup: Record = { "Review Trip": [{ id: "wrapup", label: "Wrap Up" }], "During Trip": reversedDayTabs, "Plan Trip": PLAN_TABS as unknown as { id: TabId; label: string }[], }; function resolveFromUrl(): { group: Group; tab: TabId } { const hasWrapup = trip.wrapup && trip.wrapup.todos.length > 0; const { groupSlug, tabSlug } = parsePath(trip.id); const group: Group = SLUG_TO_GROUP[groupSlug] ?? (hasWrapup ? "Review Trip" : trip.actuals.length > 0 ? "During Trip" : "Plan Trip"); const validTabs = tabsByGroup[group].map(t => t.id as string); const tab = (tabSlug && validTabs.includes(tabSlug) ? tabSlug : tabsByGroup[group][0]?.id ?? "wrapup") as TabId; return { group, tab }; } const [group, setGroup] = useState(() => resolveFromUrl().group); const [tab, setTab] = useState(() => resolveFromUrl().tab); const { location } = useLocation(); const time = useTripTime(trip); useEffect(() => { // Replace URL with canonical form on first load (e.g. /travel/superbloom/ → /travel/superbloom/review-trip/wrapup/) const { groupSlug, tabSlug } = parsePath(trip.id); if (!groupSlug || !tabSlug) { history.replaceState(null, "", `/travel/${trip.id}/${GROUP_SLUGS[group]}/${tab}/`); } const handler = () => { const { group: g, tab: t } = resolveFromUrl(); setGroup(g); setTab(t); }; window.addEventListener("popstate", handler); return () => window.removeEventListener("popstate", handler); }, []); const visibleTabs = tabsByGroup[group]; function pushUrl(g: Group, t: TabId) { history.pushState(null, "", `/travel/${trip.id}/${GROUP_SLUGS[g]}/${t}/`); } function selectGroup(g: Group) { const firstTab = tabsByGroup[g][0]?.id ?? "wrapup"; setGroup(g); setTab(firstTab); pushUrl(g, firstTab); } function selectTab(t: TabId) { setTab(t); pushUrl(group, t); } const activeDayIdx = tab.startsWith("day-") ? parseInt(tab.split("-")[1], 10) : -1; return (
{onBack && (
)}

{trip.meta.title}

{trip.meta.subtitle}

{trip.meta.dates.map(d => { const date = new Date(d + "T12:00:00"); return date.toLocaleDateString("en-US", { month: "short", day: "numeric" }); }).join(" — ")} {trip.meta.travelers.length > 0 && <> {trip.meta.travelers.join(", ")} }
{trip.meta.notes &&

{trip.meta.notes}

}
{(["Review Trip", "During Trip", "Plan Trip"] as Group[]).map(g => ( ))}
{group === "During Trip" && } {activeDayIdx >= 0 && activeDayIdx < trip.actuals.length && ( )} {tab === "wrapup" && } {tab === "itinerary" && } {tab === "map" && } {tab === "landmarks" && } {tab === "food" && } {tab === "weather" && } {tab === "onroad" && } {tab === "safety" && }
); }