// Top-level App — manages stage transitions and shared state const { ScreenSearch, ScreenResults, ScreenSeats, ScreenPassenger, ScreenConfirm, TopNav, StepRail, Icon, fmtMoney, FloatingTravelAssistant } = window; function App() { const [stage, setStage] = useState("search"); // search | results | seats | passenger | confirm const [searching, setSearching] = useState(false); const [query, setQuery] = useState({ tripType: "Round trip", origin: AIRPORTS.find(a => a.code === "JFK"), dest: AIRPORTS.find(a => a.code === "HND"), departLabel: "Fri 12 Jun", departWeek: "Friday · Spring", returnLabel: "Sun 28 Jun", returnWeek: "Sunday · Summer", }); const [flights, setFlights] = useState([]); const [selectedFlight, setSelectedFlight] = useState(null); const [selectedSeat, setSelectedSeat] = useState({ row: 12, col: "A" }); const [passenger, setPassenger] = useState({ first: "", last: "", email: "", phone: "", dob: "", nat: "United States", passport: "", ff: "", addons: ["bag"] }); const [payment, setPayment] = useState({ number: "", name: "", exp: "", cvv: "", zip: "" }); // Resolve flights via the federated GraphQL supergraph (Duffel through Apollo Router). // EHT_API.searchFlights returns live Duffel offers mapped to the UI's flight shape, and // transparently falls back to the local generator when the router/token is unavailable — // so behaviour and visuals are unchanged whether or not the backend is live. function runSearch() { setSearching(true); setSelectedFlight(null); const minVeil = new Promise((r) => setTimeout(r, 900)); // preserve the search animation Promise.all([window.EHT_API.searchFlights(query), minVeil]) .then(([list]) => setFlights(list && list.length ? list : makeFlights(query.origin.code, query.dest.code, query.departLabel))) .catch(() => setFlights(makeFlights(query.origin.code, query.dest.code, query.departLabel))) .finally(() => { setSearching(false); setStage("results"); }); } function reset() { setStage("search"); setSelectedFlight(null); setSelectedSeat({ row: 12, col: "A" }); setPassenger({ first: "", last: "", email: "", phone: "", dob: "", nat: "United States", passport: "", ff: "", addons: ["bag"] }); setPayment({ number: "", name: "", exp: "", cvv: "", zip: "" }); } return (
{/* Search loading veil */} {searching && (
Searching corridors
{query.origin.city} to {query.dest.city}
Scanning 248 flights · holding price · 12 carriers
{/* animated arc */}
)} {/* Stages */}
{stage === "search" && ( )} {stage === "results" && ( setStage("seats")} onBack={() => setStage("search")} /> )} {stage === "seats" && ( setStage("passenger")} onBack={() => setStage("results")} /> )} {stage === "passenger" && ( setStage("confirm")} onBack={() => setStage("seats")} /> )} {stage === "confirm" && ( )}
); } ReactDOM.createRoot(document.getElementById("root")).render();