// Hotel data const hotelData = [ { "name": "Hotel Britannique", "address": "20 Av. Victoria, 75001 Paris", "rating": 4.6, "price_range": "€200-320", "arrondissement": "1st", "coordinates": [48.8567, 2.3471], "description": "Classic 19th-century hotel near Châtelet with theatrical red decor and excellent Metro access", "website": "https://hotel-britannique.fr", "amenities": ["Free WiFi", "Minibar", "Air Conditioning", "Elevator"] }, { "name": "Hôtel Signature Saint Germain des Prés", "address": "5 Rue Chomel, 75007 Paris", "rating": 4.8, "price_range": "€220-290", "arrondissement": "7th", "coordinates": [48.8485, 2.3244], "description": "Chic boutique property in Saint-Germain with elegant, colorful rooms and vintage-chic lobby", "website": "http://www.signature-saintgermain.com", "amenities": ["Free WiFi", "Air Conditioning", "Buffet Breakfast", "24-hour Reception"] }, { "name": "Paris France Hotel", "address": "72 R. de Turbigo, 75003 Paris", "rating": 4.7, "price_range": "€180-280", "arrondissement": "3rd", "coordinates": [48.8644, 2.3614], "description": "Traditional hotel with a cafe/bar and lounge in the heart of the Marais district", "website": "https://www.paris-france-hotel.com", "amenities": ["Free WiFi", "Café/Bar", "Lounge", "Optional Breakfast"] }, { "name": "Hôtel Lenox Montparnasse", "address": "15 Rue Delambre, 75014 Paris", "rating": 4.5, "price_range": "€170-260", "arrondissement": "14th", "coordinates": [48.8427, 2.3269], "description": "Classic rooms & suites with warmly decorated honesty bar near Montparnasse", "website": "http://www.paris-hotel-lenox.com", "amenities": ["Free WiFi", "Honesty Bar", "Classic Decor", "Cozy Atmosphere"] }, { "name": "Hôtel Caron de Beaumarchais", "address": "12 Rue Vieille du Temple, 75004 Paris", "rating": 4.6, "price_range": "€200-280", "arrondissement": "4th", "coordinates": [48.8572, 2.3598], "description": "18th-century lodging with antiques-filled rooms and private balconies in Le Marais", "website": "http://www.carondebeaumarchais.com", "amenities": ["Free WiFi", "Private Balconies", "Antique Furnishings", "18th Century Charm"] }, { "name": "Hôtel Bourg Tibourg", "address": "19 Rue du Bourg Tibourg, 75004 Paris", "rating": 4.7, "price_range": "€250-300", "arrondissement": "4th", "coordinates": [48.8547, 2.3581], "description": "Lavish boutique hotel with opulent rooms and sophisticated dramatic atmosphere", "website": "https://www.bourgtibourg.com", "amenities": ["Free WiFi", "Opulent Decor", "Sophisticated Atmosphere", "Dramatic Design"] }, { "name": "Hôtel de l'Abbaye Saint-Germain", "address": "10 Rue Cassette, 75006 Paris", "rating": 4.6, "price_range": "€280-300", "arrondissement": "6th", "coordinates": [48.8495, 2.3309], "description": "Refined hotel with plush lounges and garden, plus free WiFi & breakfast", "website": "https://www.hotelabbayeparis.com", "amenities": ["Free WiFi", "Garden", "Plush Lounges", "Breakfast Included"] }, { "name": "Hotel Quartier Latin", "address": "9 Rue des Écoles, 75005 Paris", "rating": 4.3, "price_range": "€160-250", "arrondissement": "5th", "coordinates": [48.8490, 2.3468], "description": "Traditional hotel with simple rooms, minibars and breakfast buffet option", "website": "https://hotelquartierlatin.com", "amenities": ["Free WiFi", "Minibar", "Breakfast Buffet", "Traditional Decor"] }, { "name": "CitizenM Paris Gare de Lyon", "address": "8 Rue Van Gogh, 75012 Paris", "rating": 4.4, "price_range": "€150-220", "arrondissement": "12th", "coordinates": [48.8448, 2.3732], "description": "Trendy budget hotel with compact rooms, ambient lighting and 24-hour cafe/bar", "website": "https://www.citizenm.com/hotels/europe/paris/gare-de-lyon-hotel", "amenities": ["24-hour Café/Bar", "Ambient Lighting", "Compact Design", "Modern Amenities"] }, { "name": "Hôtel Migny Opéra-Montmartre", "address": "13 Rue Victor Massé, 75009 Paris", "rating": 4.1, "price_range": "€140-200", "arrondissement": "9th", "coordinates": [48.8810, 2.3375], "description": "Colorful rooms with satellite TV, casual breakfast area and lounge near Opéra", "website": "https://www.hotelmigny.com", "amenities": ["Free WiFi", "Satellite TV", "Breakfast Area", "LGBTQ+ Friendly"] }, { "name": "Hôtel Saint-André des Arts", "address": "66 Rue Saint-André des Arts, 75006 Paris", "rating": 4.8, "price_range": "€180-280", "arrondissement": "6th", "coordinates": [48.8540, 2.3401], "description": "Boutique hotel in Saint-Germain with unique rooms and bar", "website": "https://www.saintandredesarts.com", "amenities": ["Bar", "Unique Rooms", "Saint-Germain Location", "Boutique Style"] }, { "name": "Grand Hôtel des Gobelins", "address": "57 Bd Saint-Marcel, 75013 Paris", "rating": 4.1, "price_range": "€130-180", "arrondissement": "13th", "coordinates": [48.8375, 2.3556], "description": "Casual hotel with warmly furnished rooms and lobby bar", "website": "http://www.grandhotelgobelins.com", "amenities": ["Free WiFi", "Lobby Bar", "Warm Furnishings", "Relaxed Atmosphere"] }, { "name": "Hotel Marignan", "address": "13 Rue du Sommerard, 75005 Paris", "rating": 4.1, "price_range": "€120-180", "arrondissement": "5th", "coordinates": [48.8504, 2.3445], "description": "Laid-back hotel with simple rooms, guest kitchen and library, plus free breakfast", "website": "http://www.hotel-marignan.com", "amenities": ["Free Breakfast", "Free WiFi", "Guest Kitchen", "Library"] }, { "name": "Hôtel La Comtesse Tour Eiffel", "address": "29 Av. de Tourville, 75007 Paris", "rating": 4.6, "price_range": "€250-300", "arrondissement": "7th", "coordinates": [48.8548, 2.3078], "description": "Traditional hotel with classic rooms and Eiffel Tower views", "website": "http://www.comtesse-hotel.com", "amenities": ["Free WiFi", "Eiffel Tower Views", "Bar", "Traditional Decor"] }, { "name": "Boutique Hôtel Mareuil Paris", "address": "51 Rue de Malte, 75011 Paris", "rating": 4.7, "price_range": "€200-280", "arrondissement": "11th", "coordinates": [48.8640, 2.3677], "description": "Hip property with modern rooms & suites, plus bar, hammam & exercise room", "website": "https://www.hotelmareuil.com/fr/", "amenities": ["Bar", "Hammam", "Exercise Room", "Modern Design"] }, { "name": "Generator Paris", "address": "9-11 Pl. du Colonel Fabien, 75010 Paris", "rating": 4.1, "price_range": "€90-150", "arrondissement": "10th", "coordinates": [48.8772, 2.3711], "description": "Trendy hostel with stylish rooms & streamlined dorms, rooftop terrace & vibrant eatery", "website": "https://staygenerator.com/hostels/paris?lang=fr-FR", "amenities": ["Rooftop Terrace", "Restaurant", "Bar", "Budget-Friendly"] }, { "name": "Hôtel Henriette", "address": "9 rue des Gobelins, 75013 Paris", "rating": 4.5, "price_range": "€130-180", "arrondissement": "13th", "coordinates": [48.8363, 2.3518], "description": "Stylish boutique hotel with subtly decorated rooms and courtyard breakfast area", "website": "https://www.hotelhenriette.com", "amenities": ["Free WiFi", "Courtyard", "Breakfast", "Design Interiors"] }, { "name": "Hotel Paradis", "address": "41 Rue Des Petities Ecuries, 75010 Paris", "rating": 4.3, "price_range": "€120-180", "arrondissement": "10th", "coordinates": [48.8718, 2.3502], "description": "Tastefully decorated rooms in a top-notch location near trendy hotspots", "website": "https://hotelparadisparis.com", "amenities": ["Free WiFi", "Designer Decor", "Trendy Area", "Modern Amenities"] }, { "name": "COQ Hotel Paris", "address": "15 Rue Edouard Manet, 75013 Paris", "rating": 4.4, "price_range": "€140-190", "arrondissement": "13th", "coordinates": [48.8261, 2.3542], "description": "Stylish design hotel with cozy bar, extensive breakfast and warm atmosphere", "website": "https://coq-hotel-paris.com", "amenities": ["Bar", "Breakfast", "Design Interiors", "Cozy Atmosphere"] }, { "name": "Hotel Diana", "address": "73 Rue Saint-Jacques, 75005 Paris", "rating": 4.2, "price_range": "€150-200", "arrondissement": "5th", "coordinates": [48.8485, 2.3456], "description": "Simple but comfortable 2-star hotel with bright rooms and modern bathrooms", "website": "http://www.hoteldiana.fr", "amenities": ["Free WiFi", "Modern Bathrooms", "Central Location", "Budget-Friendly"] } ]; // Global variables let map; let markers = []; let filteredHotels = hotelData; let selectedHotel = null; // Initialize the application document.addEventListener('DOMContentLoaded', function() { initializeApp(); }); function initializeApp() { initializeMap(); populateArrondissementFilter(); bindEventListeners(); applyFilters(); updateHotelCount(); } // Initialize Leaflet map function initializeMap() { // Initialize map centered on Paris map = L.map('map').setView([48.8566, 2.3522], 12); // Add OpenStreetMap tiles L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors', maxZoom: 18 }).addTo(map); // Add hotels to map addHotelsToMap(hotelData); } // Add hotel markers to map function addHotelsToMap(hotels) { // Clear existing markers markers.forEach(marker => map.removeLayer(marker)); markers = []; hotels.forEach(hotel => { const marker = L.marker([hotel.coordinates[0], hotel.coordinates[1]], { icon: L.divIcon({ className: 'custom-marker', html: `
`, iconSize: [24, 24], iconAnchor: [12, 12] }) }); // Create popup content const popupContent = ` `; marker.bindPopup(popupContent); marker.addTo(map); markers.push(marker); // Store hotel data with marker for easy access marker.hotelData = hotel; }); } // Populate arrondissement dropdown function populateArrondissementFilter() { const select = document.getElementById('arrondissement'); const arrondissements = [...new Set(hotelData.map(hotel => hotel.arrondissement))].sort(); arrondissements.forEach(arr => { const option = document.createElement('option'); option.value = arr; option.textContent = `${arr} Arrondissement`; select.appendChild(option); }); } // Bind event listeners function bindEventListeners() { // View toggle buttons document.getElementById('map-view-btn').addEventListener('click', () => switchView('map')); document.getElementById('list-view-btn').addEventListener('click', () => switchView('list')); // Search input document.getElementById('search').addEventListener('input', applyFilters); // Price filter buttons document.querySelectorAll('.price-filter').forEach(button => { button.addEventListener('click', function() { document.querySelectorAll('.price-filter').forEach(btn => btn.classList.remove('active')); this.classList.add('active'); applyFilters(); }); }); // Arrondissement filter document.getElementById('arrondissement').addEventListener('change', applyFilters); // Rating slider const ratingSlider = document.getElementById('rating'); const ratingValue = document.getElementById('rating-value'); ratingSlider.addEventListener('input', function() { ratingValue.textContent = this.value; applyFilters(); }); // Reset filters button document.getElementById('reset-filters').addEventListener('click', resetFilters); } // Switch between map and list view function switchView(view) { const mapViewBtn = document.getElementById('map-view-btn'); const listViewBtn = document.getElementById('list-view-btn'); const mapView = document.getElementById('map-view'); const listView = document.getElementById('list-view'); if (view === 'map') { mapViewBtn.classList.add('active'); mapViewBtn.classList.remove('btn--secondary'); mapViewBtn.classList.add('btn--primary'); listViewBtn.classList.remove('active'); listViewBtn.classList.remove('btn--primary'); listViewBtn.classList.add('btn--secondary'); mapView.classList.add('active'); listView.classList.remove('active'); // Refresh map after view switch setTimeout(() => map.invalidateSize(), 100); } else { listViewBtn.classList.add('active'); listViewBtn.classList.remove('btn--secondary'); listViewBtn.classList.add('btn--primary'); mapViewBtn.classList.remove('active'); mapViewBtn.classList.remove('btn--primary'); mapViewBtn.classList.add('btn--secondary'); listView.classList.add('active'); mapView.classList.remove('active'); renderHotelList(); } } // Apply all filters function applyFilters() { const searchTerm = document.getElementById('search').value.toLowerCase(); const selectedArrondissement = document.getElementById('arrondissement').value; const minRating = parseFloat(document.getElementById('rating').value); const activePriceFilter = document.querySelector('.price-filter.active'); const minPrice = parseInt(activePriceFilter.dataset.min); const maxPrice = parseInt(activePriceFilter.dataset.max); filteredHotels = hotelData.filter(hotel => { // Search filter if (searchTerm && !hotel.name.toLowerCase().includes(searchTerm)) { return false; } // Arrondissement filter if (selectedArrondissement !== 'all' && hotel.arrondissement !== selectedArrondissement) { return false; } // Rating filter if (hotel.rating < minRating) { return false; } // Price filter const hotelMinPrice = parseInt(hotel.price_range.match(/€(\d+)/)[1]); const hotelMaxPrice = parseInt(hotel.price_range.match(/€\d+-(\d+)/)[1]); // Check if hotel price range overlaps with selected price range if (hotelMaxPrice < minPrice || hotelMinPrice > maxPrice) { return false; } return true; }); // Update map markers addHotelsToMap(filteredHotels); // Update list view if active if (document.getElementById('list-view').classList.contains('active')) { renderHotelList(); } updateHotelCount(); } // Update hotel count display function updateHotelCount() { document.getElementById('hotel-count').textContent = filteredHotels.length; } // Reset all filters function resetFilters() { document.getElementById('search').value = ''; document.getElementById('arrondissement').value = 'all'; document.getElementById('rating').value = '4.0'; document.getElementById('rating-value').textContent = '4.0'; // Reset price filter to "Under €150" to focus on budget options document.querySelectorAll('.price-filter').forEach(btn => btn.classList.remove('active')); document.querySelector('.price-filter[data-max="150"]').classList.add('active'); applyFilters(); } // Render hotel list function renderHotelList() { const listContainer = document.getElementById('hotels-list'); if (filteredHotels.length === 0) { listContainer.innerHTML = `

No hotels found

Try adjusting your filters to see more results.

`; return; } listContainer.innerHTML = filteredHotels.map(hotel => `

${hotel.name}

${hotel.price_range}
${generateStars(hotel.rating)} ${hotel.rating}
${hotel.address}
${hotel.description}
${hotel.amenities.map(amenity => `${amenity}`).join('')}
${hotel.arrondissement} Arrondissement
Visit Website
`).join(''); } // Center map on selected hotel function centerMapOnHotel(hotelName) { const hotel = hotelData.find(h => h.name === hotelName); if (hotel) { // Switch to map view switchView('map'); // Center map on hotel map.setView([hotel.coordinates[0], hotel.coordinates[1]], 15); // Find and open the marker popup const marker = markers.find(m => m.hotelData && m.hotelData.name === hotelName); if (marker) { marker.openPopup(); // Highlight the marker temporarily const markerElement = marker.getElement(); if (markerElement) { const pin = markerElement.querySelector('.marker-pin'); if (pin) { pin.style.backgroundColor = '#A84B2F'; // Warning color setTimeout(() => { pin.style.backgroundColor = '#21808D'; // Back to primary }, 2000); } } } } } // Generate star rating HTML function generateStars(rating) { const fullStars = Math.floor(rating); const hasHalfStar = rating % 1 >= 0.5; let stars = ''; for (let i = 0; i < fullStars; i++) { stars += ''; } if (hasHalfStar) { stars += ''; } const emptyStars = 5 - fullStars - (hasHalfStar ? 1 : 0); for (let i = 0; i < emptyStars; i++) { stars += ''; } return stars; } // Initialize with focus on budget hotels (under €150) document.addEventListener('DOMContentLoaded', function() { // Small delay to ensure everything is loaded setTimeout(() => { // Set default filter to show budget hotels under €150 document.querySelectorAll('.price-filter').forEach(btn => btn.classList.remove('active')); document.querySelector('.price-filter[data-max="150"]').classList.add('active'); applyFilters(); }, 500); });