Spaces:
Running
Running
Build a complete, mobile-friendly flight search web app named FlySaver ZA for South Africans to find the cheapest local and international flights. Core Features & Pages Homepage (Landing + Search Form) Inputs: Origin & Destination (dropdown with South African airports: JNB, CPT, DUR, PLZ, BFN) Departure & Return Date (calendar pickers) Trip Type toggle (One-way / Round-trip) Number of Passengers Cabin Class (Economy, Business, First) Search button labeled “Search Flights” Popular Routes section (e.g., Johannesburg–Cape Town) Results Page Display 10+ flight options sorted by price (lowest first) Each flight shows: Airline name and logo Departure and arrival times Flight duration Price in South African Rand (ZAR) “Book Now” button (placeholder or API integration) Optional filters: airline, duration, price Additional Pages About: Explain FlySaver ZA’s mission and focus on saving South Africans money Contact: Form with Name, Email, Message fields Travel Deals (optional): Cards with popular or dummy offers Design & UX Fully responsive, mobile-first layout using TailwindCSS Clean, simple UI inspired by Booking.com or Skyscanner Light/Dark mode toggle Tech Stack Frontend: HTML5, TailwindCSS, JavaScript Backend (optional): Node.js with Express or Python Flask Use static mock data initially, designed for easy API integration (e.g., TravelPayouts, Skyscanner) Currency format: South African Rand (ZAR) Deployment & Future Readiness SEO-friendly structure Public access, no login required Lightweight and fast-loading Easy to extend with monetisation features like affiliate tracking or ads Deliver a deployable app folder (frontend + backend) ready to run locally or host online. The default project name is FlySaver ZA, but it should be easily renameable. - Initial Deployment
5dff425 verified | <html lang="en" class="scroll-smooth"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>FlySaver ZA - Find Cheap Flights in South Africa</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <script> | |
| tailwind.config = { | |
| darkMode: 'class', | |
| theme: { | |
| extend: { | |
| colors: { | |
| primary: '#1E40AF', | |
| secondary: '#1E3A8A', | |
| accent: '#DB2777', | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| .datepicker { | |
| z-index: 1000 ; | |
| } | |
| .flight-card:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); | |
| } | |
| .search-box { | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 dark:bg-gray-900 transition-colors duration-200"> | |
| <!-- Header/Navigation --> | |
| <header class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50"> | |
| <nav class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="flex justify-between h-16"> | |
| <div class="flex items-center"> | |
| <a href="#" class="flex items-center"> | |
| <i class="fas fa-plane-departure text-primary dark:text-accent text-2xl mr-2"></i> | |
| <span class="text-xl font-bold text-gray-900 dark:text-white">FlySaver ZA</span> | |
| </a> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <button id="theme-toggle" class="text-gray-700 dark:text-gray-300 hover:text-primary dark:hover:text-accent"> | |
| <i class="fas fa-moon dark:hidden"></i> | |
| <i class="fas fa-sun hidden dark:block"></i> | |
| </button> | |
| <a href="#home" class="px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-primary dark:hover:text-accent">Home</a> | |
| <a href="#about" class="px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-primary dark:hover:text-accent">About</a> | |
| <a href="#contact" class="px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-primary dark:hover:text-accent">Contact</a> | |
| </div> | |
| </div> | |
| </nav> | |
| </header> | |
| <!-- Hero Section with Search Form --> | |
| <section id="home" class="relative bg-gradient-to-r from-primary to-secondary py-16"> | |
| <div class="absolute inset-0 opacity-20 bg-[url('https://images.unsplash.com/photo-1436491865332-7a61a109cc05?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2074&q=80')]"></div> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative"> | |
| <div class="text-center mb-12"> | |
| <h1 class="text-4xl font-extrabold text-white sm:text-5xl sm:tracking-tight lg:text-6xl"> | |
| Find the Cheapest Flights in South Africa | |
| </h1> | |
| <p class="mt-5 max-w-xl mx-auto text-xl text-gray-100"> | |
| Compare prices across airlines and save up to 40% on your next trip | |
| </p> | |
| </div> | |
| <!-- Search Form --> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg p-6 search-box max-w-4xl mx-auto"> | |
| <div class="flex flex-col md:flex-row mb-4"> | |
| <div class="flex-1 mb-4 md:mb-0 md:mr-4"> | |
| <label for="trip-type" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Trip Type</label> | |
| <div class="flex rounded-md shadow-sm"> | |
| <button type="button" id="one-way" class="trip-type-btn flex-1 py-2 px-4 bg-primary text-white text-sm font-medium rounded-l-md"> | |
| One-way | |
| </button> | |
| <button type="button" id="round-trip" class="trip-type-btn flex-1 py-2 px-4 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-sm font-medium rounded-r-md"> | |
| Round-trip | |
| </button> | |
| </div> | |
| </div> | |
| <div class="flex-1"> | |
| <label for="passengers" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Passengers & Class</label> | |
| <div class="flex"> | |
| <select id="passengers" class="flex-1 mr-2 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm rounded-md"> | |
| <option>1 Passenger</option> | |
| <option>2 Passengers</option> | |
| <option>3 Passengers</option> | |
| <option>4 Passengers</option> | |
| <option>5+ Passengers</option> | |
| </select> | |
| <select id="cabin-class" class="flex-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm rounded-md"> | |
| <option>Economy</option> | |
| <option>Premium Economy</option> | |
| <option>Business</option> | |
| <option>First Class</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex flex-col md:flex-row mb-4"> | |
| <div class="flex-1 mb-4 md:mb-0 md:mr-4"> | |
| <label for="origin" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">From</label> | |
| <select id="origin" class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm rounded-md"> | |
| <option value="">Select origin</option> | |
| <option value="JNB">Johannesburg (JNB)</option> | |
| <option value="CPT">Cape Town (CPT)</option> | |
| <option value="DUR">Durban (DUR)</option> | |
| <option value="PLZ">Port Elizabeth (PLZ)</option> | |
| <option value="BFN">Bloemfontein (BFN)</option> | |
| </select> | |
| </div> | |
| <div class="flex-1 mb-4 md:mb-0 md:mr-4"> | |
| <label for="destination" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">To</label> | |
| <select id="destination" class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm rounded-md"> | |
| <option value="">Select destination</option> | |
| <option value="JNB">Johannesburg (JNB)</option> | |
| <option value="CPT">Cape Town (CPT)</option> | |
| <option value="DUR">Durban (DUR)</option> | |
| <option value="PLZ">Port Elizabeth (PLZ)</option> | |
| <option value="BFN">Bloemfontein (BFN)</option> | |
| </select> | |
| </div> | |
| <div class="flex-1"> | |
| <label for="departure-date" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Departure Date</label> | |
| <input type="date" id="departure-date" class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm rounded-md"> | |
| </div> | |
| <div class="flex-1 ml-4 return-date-container"> | |
| <label for="return-date" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Return Date</label> | |
| <input type="date" id="return-date" class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm rounded-md"> | |
| </div> | |
| </div> | |
| <div class="text-center"> | |
| <button id="search-flights" class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-accent hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-accent"> | |
| <i class="fas fa-search mr-2"></i> Search Flights | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Popular Routes Section --> | |
| <section class="py-12 bg-gray-50 dark:bg-gray-800"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="text-center mb-10"> | |
| <h2 class="text-3xl font-extrabold text-gray-900 dark:text-white sm:text-4xl"> | |
| Popular Routes in South Africa | |
| </h2> | |
| <p class="mt-3 max-w-2xl mx-auto text-xl text-gray-500 dark:text-gray-400 sm:mt-4"> | |
| These routes are frequently searched by our users | |
| </p> | |
| </div> | |
| <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| <!-- Route 1 --> | |
| <div class="bg-white dark:bg-gray-700 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300"> | |
| <div class="p-6"> | |
| <div class="flex items-center"> | |
| <div class="flex-shrink-0 bg-blue-100 dark:bg-blue-900 rounded-md p-3"> | |
| <i class="fas fa-plane text-primary dark:text-blue-300 text-xl"></i> | |
| </div> | |
| <div class="ml-4"> | |
| <h3 class="text-lg font-medium text-gray-900 dark:text-white">Johannesburg to Cape Town</h3> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">From R1,200</p> | |
| </div> | |
| </div> | |
| <div class="mt-4 flex justify-between items-center"> | |
| <div class="text-sm text-gray-500 dark:text-gray-400"> | |
| <span class="font-medium text-gray-900 dark:text-white">1h 55m</span> average flight time | |
| </div> | |
| <button class="text-sm font-medium text-primary dark:text-accent hover:text-secondary dark:hover:text-pink-500"> | |
| Search flights <span aria-hidden="true">→</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Route 2 --> | |
| <div class="bg-white dark:bg-gray-700 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300"> | |
| <div class="p-6"> | |
| <div class="flex items-center"> | |
| <div class="flex-shrink-0 bg-blue-100 dark:bg-blue-900 rounded-md p-3"> | |
| <i class="fas fa-plane text-primary dark:text-blue-300 text-xl"></i> | |
| </div> | |
| <div class="ml-4"> | |
| <h3 class="text-lg font-medium text-gray-900 dark:text-white">Cape Town to Durban</h3> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">From R1,500</p> | |
| </div> | |
| </div> | |
| <div class="mt-4 flex justify-between items-center"> | |
| <div class="text-sm text-gray-500 dark:text-gray-400"> | |
| <span class="font-medium text-gray-900 dark:text-white">2h 10m</span> average flight time | |
| </div> | |
| <button class="text-sm font-medium text-primary dark:text-accent hover:text-secondary dark:hover:text-pink-500"> | |
| Search flights <span aria-hidden="true">→</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Route 3 --> | |
| <div class="bg-white dark:bg-gray-700 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300"> | |
| <div class="p-6"> | |
| <div class="flex items-center"> | |
| <div class="flex-shrink-0 bg-blue-100 dark:bg-blue-900 rounded-md p-3"> | |
| <i class="fas fa-plane text-primary dark:text-blue-300 text-xl"></i> | |
| </div> | |
| <div class="ml-4"> | |
| <h3 class="text-lg font-medium text-gray-900 dark:text-white">Johannesburg to Durban</h3> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">From R900</p> | |
| </div> | |
| </div> | |
| <div class="mt-4 flex justify-between items-center"> | |
| <div class="text-sm text-gray-500 dark:text-gray-400"> | |
| <span class="font-medium text-gray-900 dark:text-white">1h 5m</span> average flight time | |
| </div> | |
| <button class="text-sm font-medium text-primary dark:text-accent hover:text-secondary dark:hover:text-pink-500"> | |
| Search flights <span aria-hidden="true">→</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Flight Results Section (Initially Hidden) --> | |
| <section id="flight-results" class="py-12 bg-white dark:bg-gray-900 hidden"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="flex justify-between items-center mb-8"> | |
| <h2 class="text-2xl font-bold text-gray-900 dark:text-white">Flight Results</h2> | |
| <div class="flex items-center space-x-4"> | |
| <div class="relative"> | |
| <select id="sort-by" class="appearance-none bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 py-2 px-4 pr-8 rounded leading-tight focus:outline-none focus:ring-primary focus:border-primary"> | |
| <option>Sort by: Price (Lowest)</option> | |
| <option>Sort by: Duration (Shortest)</option> | |
| <option>Sort by: Departure Time</option> | |
| </select> | |
| <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700 dark:text-gray-300"> | |
| <i class="fas fa-chevron-down"></i> | |
| </div> | |
| </div> | |
| <button id="modify-search" class="text-sm font-medium text-primary dark:text-accent hover:text-secondary dark:hover:text-pink-500"> | |
| <i class="fas fa-edit mr-1"></i> Modify Search | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Filters --> | |
| <div class="bg-gray-50 dark:bg-gray-800 rounded-lg p-4 mb-8"> | |
| <div class="flex flex-wrap items-center gap-4"> | |
| <div> | |
| <label for="airline-filter" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Airline</label> | |
| <select id="airline-filter" class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm rounded-md"> | |
| <option>All Airlines</option> | |
| <option>South African Airways</option> | |
| <option>Mango</option> | |
| <option>Kulula</option> | |
| <option>FlySafair</option> | |
| <option>British Airways</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="price-range" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Price Range</label> | |
| <select id="price-range" class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm rounded-md"> | |
| <option>Any Price</option> | |
| <option>Under R1,000</option> | |
| <option>R1,000 - R2,000</option> | |
| <option>R2,000 - R3,000</option> | |
| <option>Over R3,000</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="departure-time" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Departure Time</label> | |
| <select id="departure-time" class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm rounded-md"> | |
| <option>Any Time</option> | |
| <option>Early Morning (12am - 6am)</option> | |
| <option>Morning (6am - 12pm)</option> | |
| <option>Afternoon (12pm - 6pm)</option> | |
| <option>Evening (6pm - 12am)</option> | |
| </select> | |
| </div> | |
| <button class="mt-6 text-sm font-medium text-primary dark:text-accent hover:text-secondary dark:hover:text-pink-500"> | |
| Clear All Filters | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Flight Cards --> | |
| <div class="space-y-4" id="flights-container"> | |
| <!-- Flight cards will be inserted here by JavaScript --> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- About Section --> | |
| <section id="about" class="py-12 bg-gray-50 dark:bg-gray-800"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="lg:text-center"> | |
| <h2 class="text-base text-primary dark:text-accent font-semibold tracking-wide uppercase">About FlySaver ZA</h2> | |
| <p class="mt-2 text-3xl leading-8 font-extrabold tracking-tight text-gray-900 dark:text-white sm:text-4xl"> | |
| Helping South Africans Travel for Less | |
| </p> | |
| <p class="mt-4 max-w-2xl text-xl text-gray-500 dark:text-gray-400 lg:mx-auto"> | |
| Our mission is to make air travel more affordable and accessible for everyone in South Africa. | |
| </p> | |
| </div> | |
| <div class="mt-10"> | |
| <div class="space-y-10 md:space-y-0 md:grid md:grid-cols-2 md:gap-x-8 md:gap-y-10"> | |
| <div class="flex"> | |
| <div class="flex-shrink-0"> | |
| <div class="flex items-center justify-center h-12 w-12 rounded-md bg-primary dark:bg-accent text-white"> | |
| <i class="fas fa-wallet text-xl"></i> | |
| </div> | |
| </div> | |
| <div class="ml-4"> | |
| <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Save Money</h3> | |
| <p class="mt-2 text-base text-gray-500 dark:text-gray-400"> | |
| We compare prices across all major South African airlines to ensure you get the best deal possible. | |
| </p> | |
| </div> | |
| </div> | |
| <div class="flex"> | |
| <div class="flex-shrink-0"> | |
| <div class="flex items-center justify-center h-12 w-12 rounded-md bg-primary dark:bg-accent text-white"> | |
| <i class="fas fa-bolt text-xl"></i> | |
| </div> | |
| </div> | |
| <div class="ml-4"> | |
| <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Quick & Easy</h3> | |
| <p class="mt-2 text-base text-gray-500 dark:text-gray-400"> | |
| Our simple interface lets you find and book flights in minutes, with no hidden fees. | |
| </p> | |
| </div> | |
| </div> | |
| <div class="flex"> | |
| <div class="flex-shrink-0"> | |
| <div class="flex items-center justify-center h-12 w-12 rounded-md bg-primary dark:bg-accent text-white"> | |
| <i class="fas fa-map-marked-alt text-xl"></i> | |
| </div> | |
| </div> | |
| <div class="ml-4"> | |
| <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Local Focus</h3> | |
| <p class="mt-2 text-base text-gray-500 dark:text-gray-400"> | |
| We specialize in South African routes and understand the unique needs of local travelers. | |
| </p> | |
| </div> | |
| </div> | |
| <div class="flex"> | |
| <div class="flex-shrink-0"> | |
| <div class="flex items-center justify-center h-12 w-12 rounded-md bg-primary dark:bg-accent text-white"> | |
| <i class="fas fa-shield-alt text-xl"></i> | |
| </div> | |
| </div> | |
| <div class="ml-4"> | |
| <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Trusted Partners</h3> | |
| <p class="mt-2 text-base text-gray-500 dark:text-gray-400"> | |
| We work directly with airlines to bring you the most accurate pricing and availability. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Contact Section --> | |
| <section id="contact" class="py-12 bg-white dark:bg-gray-900"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="lg:text-center mb-12"> | |
| <h2 class="text-base text-primary dark:text-accent font-semibold tracking-wide uppercase">Contact Us</h2> | |
| <p class="mt-2 text-3xl leading-8 font-extrabold tracking-tight text-gray-900 dark:text-white sm:text-4xl"> | |
| We're Here to Help | |
| </p> | |
| <p class="mt-4 max-w-2xl text-xl text-gray-500 dark:text-gray-400 lg:mx-auto"> | |
| Have questions or feedback? Reach out to our team. | |
| </p> | |
| </div> | |
| <div class="mt-10 sm:mt-20"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-8"> | |
| <div class="bg-gray-50 dark:bg-gray-800 rounded-lg p-6"> | |
| <h3 class="text-lg font-medium text-gray-900 dark:text-white mb-4">Get in Touch</h3> | |
| <div class="space-y-4"> | |
| <div class="flex items-start"> | |
| <div class="flex-shrink-0"> | |
| <i class="fas fa-envelope text-primary dark:text-accent"></i> | |
| </div> | |
| <div class="ml-3 text-base text-gray-500 dark:text-gray-400"> | |
| <p>support@flysaver.co.za</p> | |
| </div> | |
| </div> | |
| <div class="flex items-start"> | |
| <div class="flex-shrink-0"> | |
| <i class="fas fa-phone-alt text-primary dark:text-accent"></i> | |
| </div> | |
| <div class="ml-3 text-base text-gray-500 dark:text-gray-400"> | |
| <p>+27 21 123 4567</p> | |
| </div> | |
| </div> | |
| <div class="flex items-start"> | |
| <div class="flex-shrink-0"> | |
| <i class="fas fa-map-marker-alt text-primary dark:text-accent"></i> | |
| </div> | |
| <div class="ml-3 text-base text-gray-500 dark:text-gray-400"> | |
| <p>123 Aviation Way<br>Cape Town, 8001<br>South Africa</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-50 dark:bg-gray-800 rounded-lg p-6"> | |
| <h3 class="text-lg font-medium text-gray-900 dark:text-white mb-4">Send Us a Message</h3> | |
| <form class="space-y-4"> | |
| <div> | |
| <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Name</label> | |
| <input type="text" id="name" name="name" class="mt-1 block w-full border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary"> | |
| </div> | |
| <div> | |
| <label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Email</label> | |
| <input type="email" id="email" name="email" class="mt-1 block w-full border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary"> | |
| </div> | |
| <div> | |
| <label for="message" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Message</label> | |
| <textarea id="message" name="message" rows="4" class="mt-1 block w-full border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary"></textarea> | |
| </div> | |
| <div> | |
| <button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-primary dark:bg-accent hover:bg-secondary dark:hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"> | |
| Send Message | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Footer --> | |
| <footer class="bg-gray-800 dark:bg-gray-900"> | |
| <div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8"> | |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-8"> | |
| <div> | |
| <h3 class="text-sm font-semibold text-gray-400 tracking-wider uppercase">Company</h3> | |
| <ul class="mt-4 space-y-4"> | |
| <li><a href="#" class="text-base text-gray-300 hover:text-white">About</a></li> | |
| <li><a href="#" class="text-base text-gray-300 hover:text-white">Careers</a></li> | |
| <li><a href="#" class="text-base text-gray-300 hover:text-white">Press</a></li> | |
| <li><a href="#" class="text-base text-gray-300 hover:text-white">Blog</a></li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h3 class="text-sm font-semibold text-gray-400 tracking-wider uppercase">Support</h3> | |
| <ul class="mt-4 space-y-4"> | |
| <li><a href="#" class="text-base text-gray-300 hover:text-white">Help Center</a></li> | |
| <li><a href="#" class="text-base text-gray-300 hover:text-white">Safety</a></li> | |
| <li><a href="#" class="text-base text-gray-300 hover:text-white">Cancellation</a></li> | |
| <li><a href="#" class="text-base text-gray-300 hover:text-white">FAQ</a></li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h3 class="text-sm font-semibold text-gray-400 tracking-wider uppercase">Legal</h3> | |
| <ul class="mt-4 space-y-4"> | |
| <li><a href="#" class="text-base text-gray-300 hover:text-white">Terms</a></li> | |
| <li><a href="#" class="text-base text-gray-300 hover:text-white">Privacy</a></li> | |
| <li><a href="#" class="text-base text-gray-300 hover:text-white">Cookie Policy</a></li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h3 class="text-sm font-semibold text-gray-400 tracking-wider uppercase">Follow Us</h3> | |
| <div class="mt-4 flex space-x-6"> | |
| <a href="#" class="text-gray-300 hover:text-white"> | |
| <i class="fab fa-facebook-f"></i> | |
| </a> | |
| <a href="#" class="text-gray-300 hover:text-white"> | |
| <i class="fab fa-twitter"></i> | |
| </a> | |
| <a href="#" class="text-gray-300 hover:text-white"> | |
| <i class="fab fa-instagram"></i> | |
| </a> | |
| <a href="#" class="text-gray-300 hover:text-white"> | |
| <i class="fab fa-linkedin-in"></i> | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-8 border-t border-gray-700 pt-8 md:flex md:items-center md:justify-between"> | |
| <div class="flex space-x-6 md:order-2"> | |
| <p class="text-base text-gray-400">© 2023 FlySaver ZA. All rights reserved.</p> | |
| </div> | |
| <p class="mt-8 text-base text-gray-400 md:mt-0 md:order-1"> | |
| Proudly South African | |
| </p> | |
| </div> | |
| </div> | |
| </footer> | |
| <!-- JavaScript --> | |
| <script> | |
| // Dark mode toggle | |
| const themeToggle = document.getElementById('theme-toggle'); | |
| themeToggle.addEventListener('click', () => { | |
| document.documentElement.classList.toggle('dark'); | |
| localStorage.setItem('color-theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light'); | |
| }); | |
| // Check for saved theme preference | |
| if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { | |
| document.documentElement.classList.add('dark'); | |
| } else { | |
| document.documentElement.classList.remove('dark'); | |
| } | |
| // Trip type toggle | |
| const oneWayBtn = document.getElementById('one-way'); | |
| const roundTripBtn = document.getElementById('round-trip'); | |
| const returnDateContainer = document.querySelector('.return-date-container'); | |
| const returnDateInput = document.getElementById('return-date'); | |
| oneWayBtn.addEventListener('click', () => { | |
| oneWayBtn.classList.remove('bg-gray-200', 'dark:bg-gray-700', 'text-gray-700', 'dark:text-gray-300'); | |
| oneWayBtn.classList.add('bg-primary', 'text-white'); | |
| roundTripBtn.classList.remove('bg-primary', 'text-white'); | |
| roundTripBtn.classList.add('bg-gray-200', 'dark:bg-gray-700', 'text-gray-700', 'dark:text-gray-300'); | |
| returnDateContainer.classList.add('hidden'); | |
| returnDateInput.value = ''; | |
| }); | |
| roundTripBtn.addEventListener('click', () => { | |
| roundTripBtn.classList.remove('bg-gray-200', 'dark:bg-gray-700', 'text-gray-700', 'dark:text-gray-300'); | |
| roundTripBtn.classList.add('bg-primary', 'text-white'); | |
| oneWayBtn.classList.remove('bg-primary', 'text-white'); | |
| oneWayBtn.classList.add('bg-gray-200', 'dark:bg-gray-700', 'text-gray-700', 'dark:text-gray-300'); | |
| returnDateContainer.classList.remove('hidden'); | |
| }); | |
| // Mock flight data | |
| const mockFlights = [ | |
| { | |
| id: 1, | |
| airline: 'FlySafair', | |
| logo: 'fas fa-plane', | |
| departureTime: '06:15', | |
| arrivalTime: '08:10', | |
| duration: '1h 55m', | |
| price: 1250, | |
| origin: 'JNB', | |
| destination: 'CPT', | |
| stops: 0, | |
| departureDate: '2023-11-15', | |
| returnDate: '2023-11-22' | |
| }, | |
| { | |
| id: 2, | |
| airline: 'Mango', | |
| logo: 'fas fa-plane', | |
| departureTime: '08:45', | |
| arrivalTime: '10:40', | |
| duration: '1h 55m', | |
| price: 1350, | |
| origin: 'JNB', | |
| destination: 'CPT', | |
| stops: 0, | |
| departureDate: '2023-11-15', | |
| returnDate: '2023-11-22' | |
| }, | |
| { | |
| id: 3, | |
| airline: 'Kulula', | |
| logo: 'fas fa-plane', | |
| departureTime: '12:30', | |
| arrivalTime: '14:25', | |
| duration: '1h 55m', | |
| price: 1400, | |
| origin: 'JNB', | |
| destination: 'CPT', | |
| stops: 0, | |
| departureDate: '2023-11-15', | |
| returnDate: '2023-11-22' | |
| }, | |
| { | |
| id: 4, | |
| airline: 'South African Airways', | |
| logo: 'fas fa-plane', | |
| departureTime: '15:15', | |
| arrivalTime: '17:10', | |
| duration: '1h 55m', | |
| price: 1800, | |
| origin: 'JNB', | |
| destination: 'CPT', | |
| stops: 0, | |
| departureDate: '2023-11-15', | |
| returnDate: '2023-11-22' | |
| }, | |
| { | |
| id: 5, | |
| airline: 'British Airways', | |
| logo: 'fas fa-plane', | |
| departureTime: '18:45', | |
| arrivalTime: '20:40', | |
| duration: '1h 55m', | |
| price: 1950, | |
| origin: 'JNB', | |
| destination: 'CPT', | |
| stops: 0, | |
| departureDate: '2023-11-15', | |
| returnDate: '2023-11-22' | |
| }, | |
| { | |
| id: 6, | |
| airline: 'FlySafair', | |
| logo: 'fas fa-plane', | |
| departureTime: '07:30', | |
| arrivalTime: '08:35', | |
| duration: '1h 05m', | |
| price: 950, | |
| origin: 'JNB', | |
| destination: 'DUR', | |
| stops: 0, | |
| departureDate: '2023-11-15', | |
| returnDate: '2023-11-22' | |
| }, | |
| { | |
| id: 7, | |
| airline: 'Mango', | |
| logo: 'fas fa-plane', | |
| departureTime: '10:15', | |
| arrivalTime: '11:20', | |
| duration: '1h 05m', | |
| price: 1050, | |
| origin: 'JNB', | |
| destination: 'DUR', | |
| stops: 0, | |
| departureDate: '2023-11-15', | |
| returnDate: '2023-11-22' | |
| }, | |
| { | |
| id: 8, | |
| airline: 'Kulula', | |
| logo: 'fas fa-plane', | |
| departureTime: '13:45', | |
| arrivalTime: '14:50', | |
| duration: '1h 05m', | |
| price: 1100, | |
| origin: 'JNB', | |
| destination: 'DUR', | |
| stops: 0, | |
| departureDate: '2023-11-15', | |
| returnDate: '2023-11-22' | |
| }, | |
| { | |
| id: 9, | |
| airline: 'South African Airways', | |
| logo: 'fas fa-plane', | |
| departureTime: '16:30', | |
| arrivalTime: '17:35', | |
| duration: '1h 05m', | |
| price: 1500, | |
| origin: 'JNB', | |
| destination: 'DUR', | |
| stops: 0, | |
| departureDate: '2023-11-15', | |
| returnDate: '2023-11-22' | |
| }, | |
| { | |
| id: 10, | |
| airline: 'British Airways', | |
| logo: 'fas fa-plane', | |
| departureTime: '19:15', | |
| arrivalTime: '20:20', | |
| duration: '1h 05m', | |
| price: 1650, | |
| origin: 'JNB', | |
| destination: 'DUR', | |
| stops: 0, | |
| departureDate: '2023-11-15', | |
| returnDate: '2023-11-22' | |
| } | |
| ]; | |
| // Search flights button click handler | |
| document.getElementById('search-flights').addEventListener('click', function() { | |
| const origin = document.getElementById('origin').value; | |
| const destination = document.getElementById('destination').value; | |
| const departureDate = document.getElementById('departure-date').value; | |
| const returnDate = document.getElementById('return-date').value; | |
| const passengers = document.getElementById('passengers').value; | |
| const cabinClass = document.getElementById('cabin-class').value; | |
| if (!origin || !destination || !departureDate) { | |
| alert('Please fill in all required fields (Origin, Destination, Departure Date)'); | |
| return; | |
| } | |
| // Filter mock flights based on search criteria | |
| const filteredFlights = mockFlights.filter(flight => | |
| flight.origin === origin && | |
| flight.destination === destination && | |
| flight.departureDate === departureDate | |
| ); | |
| // Sort by price (lowest first) | |
| filteredFlights.sort((a, b) => a.price - b.price); | |
| // Display results | |
| displayFlightResults(filteredFlights, origin, destination); | |
| // Scroll to results | |
| document.getElementById('flight-results').scrollIntoView({ behavior: 'smooth' }); | |
| }); | |
| // Modify search button click handler | |
| document.getElementById('modify-search').addEventListener('click', function() { | |
| document.getElementById('flight-results').classList.add('hidden'); | |
| document.getElementById('home').scrollIntoView({ behavior: 'smooth' }); | |
| }); | |
| // Display flight results | |
| function displayFlightResults(flights, origin, destination) { | |
| const flightsContainer = document.getElementById('flights-container'); | |
| const flightResultsSection = document.getElementById('flight-results'); | |
| // Clear previous results | |
| flightsContainer.innerHTML = ''; | |
| if (flights.length === 0) { | |
| flightsContainer.innerHTML = ` | |
| <div class="text-center py-12"> | |
| <i class="fas fa-plane-slash text-4xl text-gray-400 mb-4"></i> | |
| <h3 class="text-lg font-medium text-gray-900 dark:text-white">No flights found</h3> | |
| <p class="mt-2 text-sm text-gray-500 dark:text-gray-400">We couldn't find any flights matching your criteria. Try adjusting your search.</p> | |
| </div> | |
| `; | |
| flightResultsSection.classList.remove('hidden'); | |
| return; | |
| } | |
| // Add results count | |
| const resultsCount = document.createElement('div'); | |
| resultsCount.className = 'text-sm text-gray-500 dark:text-gray-400 mb-4'; | |
| resultsCount.textContent = `Showing ${flights.length} flights from ${getAirportName(origin)} to ${getAirportName(destination)}`; | |
| flightsContainer.appendChild(resultsCount); | |
| // Add flight cards | |
| flights.forEach(flight => { | |
| const flightCard = document.createElement('div'); | |
| flightCard.className = 'bg-white dark:bg-gray-700 rounded-lg shadow-md overflow-hidden flight-card transition-all duration-200'; | |
| flightCard.innerHTML = ` | |
| <div class="p-6"> | |
| <div class="flex flex-col sm:flex-row sm:items-center justify-between"> | |
| <div class="flex items-center mb-4 sm:mb-0"> | |
| <div class="flex-shrink-0 bg-blue-100 dark:bg-blue-900 rounded-md p-3 mr-4"> | |
| <i class="${flight.logo} text-primary dark:text-blue-300 text-xl"></i> | |
| </div> | |
| <div> | |
| <h3 class="text-lg font-medium text-gray-900 dark:text-white">${flight.airline}</h3> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">${flight.stops === 0 ? 'Non-stop' : flight.stops + ' stop' + (flight.stops > 1 ? 's' : '')}</p> | |
| </div> | |
| </div> | |
| <div class="flex items-center space-x-6"> | |
| <div class="text-center"> | |
| <p class="text-lg font-medium text-gray-900 dark:text-white">${flight.departureTime}</p> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">${getAirportCode(flight.origin)}</p> | |
| </div> | |
| <div class="text-center"> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">${flight.duration}</p> | |
| <div class="relative"> | |
| <div class="border-t-2 border-gray-300 dark:border-gray-600 w-16"></div> | |
| <i class="fas fa-plane absolute -top-2 -left-2 text-gray-400 dark:text-gray-500 text-xs transform rotate-45"></i> | |
| </div> | |
| </div> | |
| <div class="text-center"> | |
| <p class="text-lg font-medium text-gray-900 dark:text-white">${flight.arrivalTime}</p> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">${getAirportCode(flight.destination)}</p> | |
| </div> | |
| </div> | |
| <div class="mt-4 sm:mt-0 flex flex-col items-end"> | |
| <p class="text-2xl font-bold text-primary dark:text-accent">R${flight.price.toLocaleString()}</p> | |
| <button class="mt-2 inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-primary dark:bg-accent hover:bg-secondary dark:hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary book-flight" data-flight-id="${flight.id}"> | |
| Book Now | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| flightsContainer.appendChild(flightCard); | |
| }); | |
| // Show results section | |
| flightResultsSection.classList.remove('hidden'); | |
| // Add event listeners to book buttons | |
| document.querySelectorAll('.book-flight').forEach(button => { | |
| button.addEventListener('click', function() { | |
| const flightId = this.getAttribute('data-flight-id'); | |
| const flight = mockFlights.find(f => f.id === parseInt(flightId)); | |
| alert(`Booking flight ${flight.airline} from ${flight.origin} to ${flight.destination} for R${flight.price}`); | |
| }); | |
| }); | |
| } | |
| // Helper functions | |
| function getAirportName(code) { | |
| const airports = { | |
| 'JNB': 'Johannesburg', | |
| 'CPT': 'Cape Town', | |
| 'DUR': 'Durban', | |
| 'PLZ': 'Port Elizabeth', | |
| 'BFN': 'Bloemfontein' | |
| }; | |
| return airports[code] || code; | |
| } | |
| function getAirportCode(code) { | |
| return code; // In a real app, you might format this differently | |
| } | |
| // Set default dates | |
| const today = new Date(); | |
| const tomorrow = new Date(today); | |
| tomorrow.setDate(tomorrow.getDate() + 1); | |
| document.getElementById('departure-date').valueAsDate = today; | |
| document.getElementById('return-date').valueAsDate = tomorrow; | |
| // Set min date for date inputs to today | |
| const dateInputs = document.querySelectorAll('input[type="date"]'); | |
| dateInputs.forEach(input => { | |
| input.min = today.toISOString().split('T')[0]; | |
| }); | |
| // Popular route buttons | |
| document.querySelectorAll('.popular-route').forEach(button => { | |
| button.addEventListener('click', function() { | |
| const route = this.getAttribute('data-route').split('-'); | |
| document.getElementById('origin').value = route[0]; | |
| document.getElementById('destination').value = route[1]; | |
| }); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=pheello/flysaver-za" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |