/* ======================================================================== demoScript.jsx, single source of truth for all scripted content. Phase 1: scripted demo. Phase 2: same shape, fed by live Wonderful APIs. ======================================================================== */ const _now = new Date(); const _tomorrow = new Date(_now); _tomorrow.setDate(_tomorrow.getDate() + 1); const _months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; const _monthsLong = ["January","February","March","April","May","June","July","August","September","October","November","December"]; const _fmtShort = d => `${d.getDate()} ${_months[d.getMonth()]}`; const _fmtLong = d => `${d.getDate()} ${_monthsLong[d.getMonth()]}`; const _fmtTime = d => d.toTimeString().slice(0,5); const TODAY = _fmtShort(_now); const TOMORROW = _fmtShort(_tomorrow); const TODAY_LONG = _fmtLong(_now); const TOMORROW_LONG = _fmtLong(_tomorrow); const NOW_HHMM = _fmtTime(_now); const NOW_TZ = "GST"; window.DEMO_DATES = { TODAY, TOMORROW, TODAY_LONG, TOMORROW_LONG, NOW_HHMM, NOW_TZ }; const DEMO_SCRIPT = { passenger: { name: "Lukas Weber", first: "Lukas", email: "lukas.weber@example.com", initials: "LW", nationality: "German", residence: "Dubai", skywards: "Silver", member_since: 2019, ltv: "AED 47,200", bookings: 23, bookings_recent: 7, languages: "German (native), English (fluent)", photo: "assets/lukas-photo.png", booking_ref: "K7RJ4X", pnr: "K7RJ4X", seat_pref: "23A (window)", meal_pref: "Vegetarian", // Personal context Hala captures during the chat trip_reason: `Sister's wedding, Brooklyn, ${TOMORROW_LONG}, 11:00`, trip_role: "Brother of the bride · giving the speech", }, hala: { name: "Hala", role: "Emirates Virtual Assistant", photo: "assets/hala.svg", }, rep: { name: "Marcus Chen", role: "Senior consultant, Disruption desk", photo: "assets/marcus-photo.png", initials: "MC", }, context: { disruption_event: "Thunderstorm, major operational disruption", flights_affected: "Dozens", contact_centre_wait: "22 min", date: TODAY, }, // Booking, unchanged from prior iterations original_flight: { code: "EK202", from: "DXB", to: "JFK", depart: "08:30", arrive: "13:45", date: TODAY, aircraft: "A380-800", seat: "23A", fare_class: "Economy Flex", value: "AED 4,250", }, auto_rebooked: { code: "EK211", from: "DXB", to: "JFK", depart: "08:30", arrive: "13:45", date: TOMORROW, note: "Lands 13:45, too late for an 11:00 ceremony", }, options: [ { id: "EK203", label: "OPTION 1 · Same-day evening, direct", flight: "EK203 · Direct, no stops", from: "DXB", to: "JFK", depart_date: TODAY, depart_time: "22:40", arrive_date: TOMORROW, arrive_time: "04:55", seats_available: 12, fare_diff: "No fare difference", stops: "Direct", starred: true, // Recommendation reason explicitly references the wedding context reason: "Lands 04:55, gives you ~6 hours to rest before the 11:00 ceremony.", }, { id: "EK205", label: "OPTION 2 · Earliest arrival, via London", flight: "EK205 + EK023 · 1 stop, LHR", from: "DXB", to: "JFK", depart_date: TODAY, depart_time: "14:10", arrive_date: TODAY, arrive_time: "20:30", seats_available: 4, seats_warn: true, fare_diff: "No fare difference", stops: "1 stop · LHR", reason: "Arrives the night before, but it's two flights and a 2h 30m layover.", }, { id: "EK211", label: "OPTION 3 · Auto-rebooked (not recommended)", flight: "EK211 · Direct, tomorrow morning", from: "DXB", to: "JFK", depart_date: TOMORROW, depart_time: "08:30", arrive_date: TOMORROW, arrive_time: "13:45", seats_available: 30, fare_diff: "No fare difference", stops: "Direct", warn: true, reason: "Lands 13:45, after the 11:00 ceremony. Not viable for the wedding.", }, ], // -------------------------------------------------------------------- WEB CHAT // Hala OPENS proactively the moment Lukas hits the manage-booking page. // Lukas vents across THREE escalating messages, angry, specific, fair. // Hala de-escalates with one acknowledgement (no piled apologies, no // defensiveness), captures the wedding context, and produces options // that visibly carry that context forward. // Channel transition is a CHOICE: Emirates calls him, or he calls the // priority disruption line directly. Lukas picks "call me". web_chat: [ // HALA OPENS PROACTIVELY, she initiated, not Lukas { who: "hala-typing", delay: 1200 }, // Intro only, separate turn { who: "hala", t: "09:15", text: "Hi Lukas, I'm Hala, your Emirates virtual assistant.", delay: 1500 }, // Human-path offer immediately after the intro { who: "hala", t: "09:15", text: "I can transfer you to a human rep, but the wait is currently around 22 minutes. Would you like to wait, or shall we continue?", delay: 2600 }, // LUKAS opts to continue { who: "lukas-typing", delay: 1400 }, { who: "lukas", t: "09:15", text: "let's continue. i don't have 22 minutes to wait.", delay: 1800 }, // HALA, booking acknowledgement now its own turn { who: "hala-typing", delay: 1400 }, { who: "hala", t: "09:16", text: "I just saw EK202 was cancelled this morning. I'm sorry. The system has put you on EK211 tomorrow, but before anything's locked in I want to check it actually works for you.", delay: 3000 }, // LUKAS, angry, three escalating beats { who: "lukas-typing", delay: 1400 }, { who: "lukas", t: "09:16", text: "are you serious. EK211 lands at 13:45. my sister is getting married TOMORROW at 11. i'm supposed to give the speech.", delay: 2200, tone: "stressed" }, { who: "lukas", t: "09:16", text: "who at Emirates looked at this booking and thought 'yeah, that flight works'? did anyone actually look?", delay: 1800, tone: "stressed" }, { who: "lukas", t: "09:16", text: "i've been Skywards Silver for seven years. this is the second cancellation this month. what are you actually going to do.", delay: 1800, tone: "stressed" }, // HALA, one specific acknowledgement, no defensiveness, then moves { who: "hala-typing", delay: 2000 }, { who: "hala", t: "09:17", text: "Lukas, you're right, EK211 doesn't work, and the auto-rebook missed that. I'm not going to defend it. I'm going to fix it.", delay: 2600 }, { who: "hala", t: "09:17", text: "Before I pull options, anything else about tomorrow I should know? Ceremony time, where you're staying, anything time-sensitive? It helps me prioritise the right flight.", delay: 2600 }, // LUKAS, calmer, gives the human context { who: "lukas-typing", delay: 1800 }, { who: "lukas", t: "09:18", text: "ceremony 11am, Brooklyn. i'm the brother of the bride, giving the speech. i need to land tonight with time to actually sleep and get ready.", delay: 2400 }, // HALA, warm, brief, then moves { who: "hala-typing", delay: 1400 }, { who: "hala", t: "09:18", text: "Got it. Sister's wedding tomorrow, you're giving the speech, you need to land tonight with time to rest. That's what I'm optimising for.", delay: 2200 }, { who: "system", text: "Hala is checking flight availability…", delay: 1600 }, // OPTIONS, the recommended one explicitly carries the wedding reason { who: "hala", t: "09:19", text: "Three options at no extra cost. I'm recommending the first based on what you told me.", delay: 1800 }, { who: "options", delay: 1200 }, // ONE policy question, the strongest one (safety / what-if EK203 also disrupts) { who: "lukas-typing", delay: 1800 }, { who: "lukas", t: "09:20", text: "EK203 looks right. what if THAT one also gets cancelled? i can't take another disruption.", delay: 2000 }, { who: "hala-typing", delay: 1600 }, { who: "hala", t: "09:20", text: "Fair question. EK203 has operated on time 28 of the last 30 days on the DXB to JFK route. If it does disrupt, you're protected under the same policy, hotel, meals, re-accommodation on the next available flight, no charge.", delay: 2800, highlight: "28 of the last 30 days" }, // LUKAS asks to move to voice { who: "lukas-typing", delay: 1400 }, { who: "lukas", t: "09:21", text: "i have more questions, this will be faster over voice.", delay: 2000 }, // HALA offers a channel choice (mobile call vs in-browser web voice) { who: "hala-typing", delay: 1400 }, { who: "hala", t: "09:21", text: "Of course. Want me to call you on your registered mobile now, or would you rather stay here and switch to web voice?", delay: 2800 }, { who: "channel-choice", delay: 1000 }, // LUKAS picks the mobile call { who: "lukas-typing", delay: 1600 }, { who: "lukas", t: "09:22", text: "call my mobile please, i'd rather take it on the phone.", delay: 2000 }, // HALA confirms and transitions { who: "hala-typing", delay: 1200 }, { who: "hala", t: "09:22", text: "On it. I'll ring you now and bring the whole chat with me.", delay: 2400 }, { who: "call-cta", delay: 800 }, ], // -------------------------------------------------------------------- VOICE // Hala opens by RECALLING multiple facts (proves memory). She handles the // policy questions herself. She places the seat on HOLD, she does NOT // commit the rebook. She tells Lukas the confirmation will arrive across // channels. voice: [ { who: "system", text: "Emirates calling Lukas · verified business caller", delay: 1000 }, { who: "system", text: "Identity & chat context carried from web session · no re-auth needed", delay: 1200 }, // HERO MEMORY LINE, same Hala, full recall, picks up exactly where chat left off { who: "hala", t: "00:03", text: "Hi Lukas, it's Hala, same Hala you were just chatting with. I have everything: EK202 cancelled, EK203 tonight at 22:40 in mind, 23A as your usual seat, vegetarian meal noted, your sister's wedding tomorrow at 11. What do you want to walk through first?", delay: 3800, hero: true }, // Lukas opens the call still wound up, same tone as end of chat { who: "lukas", t: "00:14", text: "look, i'm getting on this flight tonight either way. i just need to know nothing else falls over. if i agree to EK203, am i actually on it or is this still sitting in some queue?", delay: 2600, tone: "stressed" }, { who: "hala", t: "00:22", text: "Fair. Here's where things stand: I'm holding seat 23A for you now, and I'm queuing the rebook to our specialist desk for final sign-off. The moment they approve, the change is committed. It's seconds, not minutes.", delay: 3600 }, // Pressure ratchets, the cost question { who: "lukas", t: "00:36", text: "and i don't get charged anything? no fare difference, no fee, nothing weird shows up on the card later?", delay: 2400, tone: "stressed" }, { who: "hala", t: "00:42", text: "Nothing. EK cancelled the flight, so the rebook is at no charge in the same cabin. Same fare class, miles credit as normal, meal and seat carry across. There is no fare delta on this booking.", delay: 3200 }, // Safety beat, the 28/30 stat. Lukas is still pushing. { who: "lukas", t: "00:58", text: "okay but what if EK203 ALSO cancels. i can't be the brother of the bride who didn't show up. what's the actual backup?", delay: 2600, tone: "stressed" }, { who: "hala", t: "01:05", text: "Honest answer, I can't promise it won't. What I can tell you is EK203 has operated on time 28 of the last 30 days on this route. And if it does disrupt, you're covered under the same policy: hotel, meals, re-accommodation on the next available flight, no charge. I've flagged the booking so that applies automatically.", delay: 4400, hero: true, highlight: "28 of the last 30 days" }, // Lukas softens a fraction, not relieved, just acknowledging. { who: "lukas", t: "01:23", text: "alright. how do i actually know it's locked in? i'm not sitting here refreshing my email for the next hour.", delay: 2400, tone: "stressed" }, // Hala explains the multi-channel confirmation, this is what will finally calm him { who: "hala", t: "01:30", text: "You won't have to. The moment our specialist approves, you'll get it three ways at once, email, SMS, and WhatsApp, with the boarding pass and seat. Usually under a minute from when we hang up. If anything's wrong, I'll call you back myself.", delay: 4400, hero: true }, // The release, Lukas finally lets out the breath { who: "lukas", t: "01:50", text: "okay. okay. that helps. thank you, sorry for being short with you.", delay: 2400 }, { who: "hala", t: "01:55", text: "Don't apologise. It's a stressful morning and a flight you can't miss. You're sorted. Confirmation will land on your phone in three channels the moment our specialist signs off, usually within the minute.", delay: 3400 }, { who: "submit", delay: 1200 }, ], // -------------------------------------------------------------------- CONFIRMATION EMAIL // Payload for the post-commit confirmation that lands on Lukas's phone // once Marcus has approved the rebook. Mirrors the cancellation email // structure but inverted in tone, this is the "you're sorted" beat. confirmation_email: { from_name: "Emirates", from_addr: "do-not-reply@emirates.email", subject: "Your booking is confirmed · EK203 to New York", headline: "Your booking is confirmed", flight_code: "EK203", from_iata: "DXB", from_city: "Dubai International Airport (DXB)", to_iata: "JFK", to_city: "John F. Kennedy Intl Airport (JFK)", depart: "22:40", depart_date: TODAY, arrive: "04:55", arrive_date: TOMORROW, seat: "23A", meal: "Vegetarian", body: "Your rebook is confirmed. Your seat and meal preferences have carried across. Boarding pass attached. Safe travels, Lukas.", }, // -------------------------------------------------------------------- ASSISTANT (rep console) // Marcus is NOT on the call. Hala held the seat and queued the rebook. // Approval is fully human, nothing fires until Marcus clicks Approve. assistant: [ { who: "system", text: "Case received from Hala · queue position 1 of 3", delay: 1200 }, { who: "approval-card-arrive", delay: 800 }, ], }; window.DEMO_SCRIPT = DEMO_SCRIPT;