import { useState } from 'react'; function Section({ title, children }: { title: string; children: React.ReactNode }) { return (

{title}

{children}
); } interface JourneyStep { icon: React.ReactNode; iconBg: string; title: string; subtitle: string; details: { heading: string; body: string; bullets?: string[]; }; } const ArrowIcon = () => ( ); function StepCard({ step, onClick, }: { step: JourneyStep; onClick: () => void; }) { return ( ); } function DetailModal({ step, onClose, }: { step: JourneyStep; onClose: () => void; }) { return (
e.stopPropagation()} >
{step.icon}

{step.title}

{step.subtitle}

{step.details.heading}

{step.details.body}

{step.details.bullets && (
    {step.details.bullets.map((b, i) => (
  • {b}
  • ))}
)}
); } const HomeIcon = () => ( ); const CaptureIcon = () => ( ); const ConvertIcon = () => ( ); const ConfigureIcon = () => ( ); const operatorSteps: JourneyStep[] = [ { icon: , iconBg: '#056F82', title: 'Quest Home', subtitle: 'Lightning App', details: { heading: 'Entry point', body: 'The Quest Home page is the operator\'s starting point inside Salesforce. It provides a dashboard overview of active leads, recent searches, and quick-access shortcuts.', bullets: [ 'Salesforce Lightning App', 'Dashboard with active pipeline', 'Quick search and recent activity', ], }, }, { icon: , iconBg: '#4285FF', title: 'Lead Capture', subtitle: 'Salesforce Lead', details: { heading: 'Capture enquiry', body: 'Incoming travel enquiries are captured as Salesforce Leads. This step gathers traveller details, preferences, and initial requirements.', bullets: [ 'Standard Salesforce Lead object', 'Traveller preferences and requirements', 'Source tracking and attribution', ], }, }, { icon: , iconBg: '#FFBC42', title: 'Convert', subtitle: 'Quest Canvas + API', details: { heading: 'Build the itinerary', body: 'The lead is converted into a bookable trip using Quest Canvas. Operators search availability, configure packages, and build a complete itinerary with real-time pricing.', bullets: [ 'Quest Canvas visual builder', 'Real-time availability and pricing via API', 'Multi-day itinerary configuration', 'Dynamic package assembly', ], }, }, { icon: , iconBg: '#DE37A4', title: 'Configure', subtitle: 'Quest App', details: { heading: 'Finalise and book', body: 'Final configuration, pricing adjustments, and booking confirmation happen in the Quest App. The operator reviews the complete package before submitting.', bullets: [ 'Pricing review and adjustments', 'Add-ons and optional services', 'Booking confirmation and payment', 'Automated confirmation emails', ], }, }, ]; const GuestIcon = () => ( ); const SearchIcon = () => ( ); const CartIcon = () => ( ); const ConfirmIcon = () => ( ); const guestSteps: JourneyStep[] = [ { icon: , iconBg: '#034955', title: 'Browse', subtitle: 'Web / App', details: { heading: 'Discovery', body: 'The guest browses available travel packages through the operator\'s website or app, powered by Quest search.', bullets: ['Curated package listings', 'Filter by destination, date, and budget', 'Responsive web and mobile experience'], }, }, { icon: , iconBg: '#056F82', title: 'Search', subtitle: 'Quest API', details: { heading: 'Find availability', body: 'Real-time search powered by Quest API returns available packages with pricing, availability, and options.', bullets: ['Real-time availability', 'Dynamic pricing', 'Package comparison'], }, }, { icon: , iconBg: '#FFBC42', title: 'Customise', subtitle: 'Package Builder', details: { heading: 'Build your trip', body: 'The guest selects options, adds extras, and customises their trip to their preferences before checkout.', bullets: ['Room and cabin selection', 'Optional add-ons and upgrades', 'Traveller details and preferences'], }, }, { icon: , iconBg: '#2BA711', title: 'Confirm', subtitle: 'Checkout', details: { heading: 'Book and pay', body: 'Secure checkout with payment processing, booking confirmation, and automated communications.', bullets: ['Secure payment processing', 'Instant booking confirmation', 'Email and SMS notifications'], }, }, ]; function JourneyFlow({ steps, label, labelColor, }: { steps: JourneyStep[]; label: string; labelColor: string; }) { const [activeStep, setActiveStep] = useState(null); return (

{label}

{steps.map((step, i) => (
setActiveStep(i)} /> {i < steps.length - 1 && }
))}
{activeStep !== null && ( setActiveStep(null)} /> )}
); } interface CardStep { icon: React.ReactNode; iconBg: string; title: string; subtitle: string; image: string; description: string; features: string[]; tag?: string; } const cardSteps: CardStep[] = [ { icon: , iconBg: '#056F82', title: 'Quest Home', subtitle: 'Lightning App', image: '/images/hero-quest.png', tag: 'Entry Point', description: 'The operator\'s command centre inside Salesforce. Quest Home surfaces active leads, recent searches, and pipeline health in a single dashboard — removing the need to navigate between tabs.', features: [ 'Dashboard with pipeline overview and KPIs', 'Quick-access shortcuts to active itineraries', 'Recent search history and saved filters', 'Role-based views for agents and managers', ], }, { icon: , iconBg: '#4285FF', title: 'Lead Capture', subtitle: 'Salesforce Lead', image: '/images/hero-circle.png', tag: 'Capture', description: 'Every travel enquiry begins as a Salesforce Lead. This step captures traveller details, preferences, and source attribution — ensuring nothing falls through the cracks.', features: [ 'Standard Salesforce Lead object integration', 'Traveller preferences and special requirements', 'Source tracking, UTM attribution, and referral data', 'Automated lead assignment and routing rules', ], }, { icon: , iconBg: '#FFBC42', title: 'Convert', subtitle: 'Quest Canvas + API', image: '/images/hero-travel.png', tag: 'Build', description: 'Quest Canvas transforms a lead into a bookable itinerary. Operators search live availability, drag-and-drop components, and see real-time pricing — all within a visual builder.', features: [ 'Visual drag-and-drop itinerary builder', 'Real-time availability and pricing via Quest API', 'Multi-day, multi-destination configuration', 'Dynamic package assembly with optional add-ons', ], }, { icon: , iconBg: '#034955', title: 'Configure', subtitle: 'Quest App', image: '/images/card-example.png', tag: 'Finalise', description: 'The final step before booking. Operators review the complete package, apply pricing adjustments, add optional services, and confirm the reservation with a single action.', features: [ 'Full pricing review with margin controls', 'Add-on services and experience upgrades', 'Payment processing and deposit management', 'Automated confirmation emails and documentation', ], }, { icon: , iconBg: '#2BA711', title: 'Confirm', subtitle: 'Booking Engine', image: '/images/hero-travel.png', tag: 'Book', description: 'The booking is confirmed and payment is processed. The system generates all required documents and triggers downstream fulfilment workflows.', features: [ 'Secure payment capture and receipts', 'Booking reference generation', 'Supplier notifications and allocations', 'Traveller confirmation and documents', ], }, { icon: , iconBg: '#056F82', title: 'Monitor', subtitle: 'Operations Dashboard', image: '/images/hero-quest.png', tag: 'Track', description: 'Active bookings are monitored through the operations dashboard. Operators track supplier confirmations, amendment requests, and upcoming departures.', features: [ 'Real-time booking status tracking', 'Supplier confirmation management', 'Amendment and cancellation workflows', 'Departure countdown and readiness checks', ], }, ]; function JourneyCard({ step, index, onClick }: { step: CardStep; index: number; onClick: () => void }) { return ( ); } function CardDetailModal({ step, onClose }: { step: CardStep; onClose: () => void }) { return (
e.stopPropagation()} >
{step.title}
{step.icon}

{step.title}

{step.subtitle}

{step.description}

    {step.features.map((f, i) => (
  • {f}
  • ))}
); } const LeftArrowIcon = () => ( ); function ReturnArrow({ side }: { side: 'right' | 'left' }) { return (
); } function CardFlow({ steps, label }: { steps: CardStep[]; label: string }) { const [activeStep, setActiveStep] = useState(null); const perRow = 4; const rows: CardStep[][] = []; for (let i = 0; i < steps.length; i += perRow) { rows.push(steps.slice(i, i + perRow)); } return (

{label}

{rows.map((row, rowIdx) => { const reversed = rowIdx % 2 === 1; const displayRow = reversed ? [...row].reverse() : row; const startIdx = rowIdx * perRow; const hasNextRow = rowIdx < rows.length - 1; return (
{displayRow.map((step, colIdx) => { const actualIdx = reversed ? startIdx + (row.length - 1 - colIdx) : startIdx + colIdx; const isLastInRow = colIdx === displayRow.length - 1; return (
setActiveStep(actualIdx)} />
{!isLastInRow && (
{reversed ? : }
)}
); })}
{hasNextRow && ( )}
); })} {activeStep !== null && ( setActiveStep(null)} /> )}
); } const BiArrowIcon = () => ( ); function BidirectionalFlow({ steps, label }: { steps: CardStep[]; label: string }) { const [activeStep, setActiveStep] = useState(null); const display = steps.slice(0, 4); return (

{label}

{display.map((step, i) => (
setActiveStep(i)} />
{i < display.length - 1 && (
{i === 0 || i === display.length - 2 ? : }
)}
))}
{activeStep !== null && ( setActiveStep(null)} /> )}
); } export default function JourneyDemo() { return (

Steps connected both ways — showing how the first and last steps feed back into each other.

); }