Spaces:
Running
Running
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Envato Search - Recherche de produits</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"> | |
| <style> | |
| .dropzone { | |
| border: 2px dashed #3b82f6; | |
| border-radius: 0.5rem; | |
| padding: 2rem; | |
| text-align: center; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| } | |
| .dropzone:hover { | |
| border-color: #10b981; | |
| background-color: rgba(16, 185, 129, 0.05); | |
| } | |
| .dropzone.active { | |
| border-color: #8b5cf6; | |
| background-color: rgba(139, 92, 246, 0.05); | |
| } | |
| .search-highlight { | |
| background-color: rgba(16, 185, 129, 0.3); | |
| font-weight: bold; | |
| padding: 0.1rem 0.2rem; | |
| border-radius: 0.2rem; | |
| } | |
| .envato-badge { | |
| background-color: #82b440; | |
| } | |
| .codecanyon-badge { | |
| background-color: #7e57c2; | |
| } | |
| .skeleton { | |
| animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; | |
| background-color: #e5e7eb; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| .image-search-preview { | |
| transition: all 0.3s ease; | |
| } | |
| .image-search-preview:hover { | |
| transform: scale(1.02); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8 max-w-6xl"> | |
| <!-- Header --> | |
| <header class="mb-8"> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center"> | |
| <img src="https://assets.envato.com/front/assets/envato-market-6d2a3fe6f4f1c4a16bea9eafb0e2b4b5d3c9188111f805d41a61d3d3b21e5ef1.svg" alt="Envato Market" class="h-10 mr-4"> | |
| <h1 class="text-3xl font-bold text-gray-800">Recherche de produits</h1> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <button class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition flex items-center"> | |
| <i class="fas fa-sign-in-alt mr-2"></i> Connexion | |
| </button> | |
| </div> | |
| </div> | |
| <p class="text-gray-600 mt-2">Recherchez parmi des milliers de produits Envato Market et CodeCanyon</p> | |
| </header> | |
| <!-- Search Section --> | |
| <div class="bg-white rounded-lg shadow-md p-6 mb-8"> | |
| <div class="flex items-center justify-between mb-6"> | |
| <h2 class="text-xl font-semibold text-gray-800">Options de recherche</h2> | |
| <div class="flex space-x-2"> | |
| <button id="textSearchTab" class="px-4 py-2 bg-green-600 text-white rounded-md transition">Recherche par texte</button> | |
| <button id="imageSearchTab" class="px-4 py-2 bg-blue-600 text-white rounded-md transition">Recherche par image</button> | |
| </div> | |
| </div> | |
| <!-- Text Search --> | |
| <div id="textSearchSection" class=""> | |
| <div class="relative"> | |
| <input type="text" id="searchInput" placeholder="Rechercher par nom de produit, catégorie, auteur..." | |
| class="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500"> | |
| <button id="searchBtn" class="absolute right-2 top-1/2 transform -translate-y-1/2 bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 transition"> | |
| <i class="fas fa-search mr-2"></i> Rechercher | |
| </button> | |
| </div> | |
| <div class="flex flex-wrap gap-4 mt-4"> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="filterEnvato" checked class="mr-2"> | |
| <label for="filterEnvato" class="text-gray-700">Envato Market</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="filterCodeCanyon" checked class="mr-2"> | |
| <label for="filterCodeCanyon" class="text-gray-700">CodeCanyon</label> | |
| </div> | |
| <div class="flex items-center ml-auto"> | |
| <select id="sortBy" class="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <option value="relevance">Pertinence</option> | |
| <option value="popular">Plus populaires</option> | |
| <option value="newest">Plus récents</option> | |
| <option value="price_asc">Prix croissant</option> | |
| <option value="price_desc">Prix décroissant</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Image Search --> | |
| <div id="imageSearchSection" class="hidden"> | |
| <div class="mb-6"> | |
| <div id="dropzone" class="dropzone mb-4"> | |
| <i class="fas fa-cloud-upload-alt text-4xl text-blue-500 mb-3"></i> | |
| <p class="text-lg font-medium text-gray-700">Glissez-déposez une image de produit ici</p> | |
| <p class="text-gray-500 mt-1">ou cliquez pour sélectionner un fichier</p> | |
| <input type="file" id="fileInput" accept="image/*" class="hidden"> | |
| </div> | |
| <div id="imagePreviewContainer" class="hidden"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="text-lg font-medium text-gray-800">Image sélectionnée</h3> | |
| <button id="clearImageBtn" class="text-red-600 hover:text-red-800"> | |
| <i class="fas fa-times mr-1"></i> Supprimer | |
| </button> | |
| </div> | |
| <div class="flex flex-col md:flex-row gap-6"> | |
| <div class="w-full md:w-1/3"> | |
| <div class="image-search-preview bg-white p-2 rounded-md border border-gray-200"> | |
| <img id="preview" src="#" alt="Aperçu de l'image" class="w-full h-auto rounded-md"> | |
| </div> | |
| </div> | |
| <div class="w-full md:w-2/3"> | |
| <div class="space-y-4"> | |
| <div> | |
| <label for="imageSearchType" class="block text-sm font-medium text-gray-700 mb-1">Type de recherche</label> | |
| <select id="imageSearchType" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <option value="exact">Recherche exacte</option> | |
| <option value="similar">Produits similaires</option> | |
| <option value="color">Par palette de couleurs</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="imageSearchConfidence" class="block text-sm font-medium text-gray-700 mb-1">Niveau de correspondance</label> | |
| <input type="range" id="imageSearchConfidence" min="50" max="100" value="80" class="w-full"> | |
| <div class="flex justify-between text-xs text-gray-500 mt-1"> | |
| <span>Large</span> | |
| <span>Précis</span> | |
| </div> | |
| </div> | |
| <button id="searchImageBtn" class="w-full md:w-auto px-6 py-3 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition flex items-center justify-center"> | |
| <i class="fas fa-camera mr-2"></i> Lancer la recherche | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Results Section --> | |
| <div id="resultsSection" class="hidden"> | |
| <div class="flex items-center justify-between mb-6"> | |
| <h2 class="text-xl font-semibold text-gray-800"> | |
| Résultats de recherche: <span id="resultsCount" class="text-blue-600">0</span> | |
| </h2> | |
| <div class="flex items-center space-x-2"> | |
| <span class="text-sm text-gray-600">Trier par:</span> | |
| <select id="resultsSortBy" class="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <option value="relevance">Pertinence</option> | |
| <option value="popular">Popularité</option> | |
| <option value="newest">Plus récents</option> | |
| <option value="price_asc">Prix croissant</option> | |
| <option value="price_desc">Prix décroissant</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div id="loadingIndicator" class="hidden"> | |
| <div class="space-y-4"> | |
| <div class="skeleton h-32 rounded-md"></div> | |
| <div class="skeleton h-32 rounded-md"></div> | |
| <div class="skeleton h-32 rounded-md"></div> | |
| </div> | |
| </div> | |
| <div id="resultsContainer" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| <!-- Results will be loaded here --> | |
| </div> | |
| <div id="pagination" class="mt-8 flex justify-center hidden"> | |
| <nav class="inline-flex rounded-md shadow"> | |
| <button class="px-3 py-1 rounded-l-md border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"> | |
| Précédent | |
| </button> | |
| <button class="px-3 py-1 border-t border-b border-gray-300 bg-blue-50 text-blue-600 font-medium"> | |
| 1 | |
| </button> | |
| <button class="px-3 py-1 border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"> | |
| 2 | |
| </button> | |
| <button class="px-3 py-1 border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"> | |
| 3 | |
| </button> | |
| <button class="px-3 py-1 rounded-r-md border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"> | |
| Suivant | |
| </button> | |
| </nav> | |
| </div> | |
| <div id="noResults" class="hidden text-center py-12"> | |
| <i class="fas fa-search fa-3x text-gray-300 mb-4"></i> | |
| <h3 class="text-xl font-medium text-gray-700">Aucun résultat trouvé</h3> | |
| <p class="text-gray-500 mt-2">Essayez de modifier vos critères de recherche</p> | |
| </div> | |
| </div> | |
| <!-- Footer --> | |
| <footer class="mt-12 pt-8 border-t border-gray-200"> | |
| <div class="flex flex-col md:flex-row justify-between items-center"> | |
| <div class="mb-4 md:mb-0"> | |
| <img src="https://assets.envato.com/front/assets/envato-market-6d2a3fe6f4f1c4a16bea9eafb0e2b4b5d3c9188111f805d41a61d3d3b21e5ef1.svg" alt="Envato Market" class="h-8"> | |
| <p class="text-gray-500 text-sm mt-2">© 2023 Envato Pty Ltd. Tous droits réservés.</p> | |
| </div> | |
| <div class="flex space-x-6"> | |
| <a href="#" class="text-gray-600 hover:text-gray-900">Conditions</a> | |
| <a href="#" class="text-gray-600 hover:text-gray-900">Politique de confidentialité</a> | |
| <a href="#" class="text-gray-600 hover:text-gray-900">API</a> | |
| <a href="#" class="text-gray-600 hover:text-gray-900">Support</a> | |
| </div> | |
| </div> | |
| </footer> | |
| </div> | |
| <script> | |
| // Sample product database (in a real app, this would come from an API) | |
| const products = [ | |
| { | |
| id: "avada", | |
| name: "Avada | Website Builder For WordPress & WooCommerce", | |
| type: "envato", | |
| category: "Thème WordPress", | |
| author: "ThemeFusion", | |
| price: 69, | |
| sales: 842000, | |
| rating: 4.82, | |
| updated: "2023-10-15", | |
| description: "Avada est le meilleur vendeur #1 de tous les temps sur ThemeForest avec 840 000 ventes. Il s'agit d'un thème WordPress ultra-personnalisable avec un constructeur de page avancé.", | |
| image: "https://themeforest.img.customer.envatousercontent.com/files/459385881/avada-preview.jpg?auto=compress%2Cformat&fit=crop&crop=top&w=590&h=300&s=7b1b0c0b1b1b1b1b1b1b1b1b1b1b1b1", | |
| tags: ["wordpress", "builder", "woocommerce", "responsive"] | |
| }, | |
| { | |
| id: "revolution", | |
| name: "Slider Revolution - More Than Just a WordPress Slider", | |
| type: "codecanyon", | |
| category: "Plugin WordPress", | |
| author: "ThemePunch", | |
| price: 39, | |
| sales: 320000, | |
| rating: 4.78, | |
| updated: "2023-09-28", | |
| description: "Slider Revolution est un plugin de création de sliders et animations avancées pour WordPress. Créez des présentations époustouflantes avec des effets visuels impressionnants.", | |
| image: "https://s3.envato.com/files/328384320/slider-revolution-preview.jpg", | |
| tags: ["slider", "animation", "wordpress", "visual"] | |
| }, | |
| { | |
| id: "betheme", | |
| name: "BeTheme - Responsive Multi-Purpose WordPress Theme", | |
| type: "envato", | |
| category: "Thème WordPress", | |
| author: "Muffin group", | |
| price: 59, | |
| sales: 235000, | |
| rating: 4.75, | |
| updated: "2023-08-10", | |
| description: "BeTheme est un thème WordPress multi-usage avec plus de 600 démos pré-construites. Idéal pour créer tout type de site web rapidement et facilement.", | |
| image: "https://themeforest.img.customer.envatousercontent.com/files/459385881/betheme-preview.jpg?auto=compress%2Cformat&fit=crop&crop=top&w=590&h=300&s=7b1b0c0b1b1b1b1b1b1b1b1b1b1b1b1", | |
| tags: ["wordpress", "multipurpose", "responsive", "demos"] | |
| }, | |
| { | |
| id: "wpbakery", | |
| name: "WPBakery Page Builder (formerly Visual Composer)", | |
| type: "codecanyon", | |
| category: "Plugin WordPress", | |
| author: "WPBakery", | |
| price: 45, | |
| sales: 410000, | |
| rating: 4.72, | |
| updated: "2023-07-22", | |
| description: "WPBakery Page Builder est un constructeur de page visuel pour WordPress. Créez des mises en page complexes sans coder avec une interface glisser-déposer intuitive.", | |
| image: "https://s3.envato.com/files/328384320/wpbakery-preview.jpg", | |
| tags: ["page builder", "wordpress", "drag and drop", "visual"] | |
| }, | |
| { | |
| id: "the7", | |
| name: "The7 — Website and eCommerce Builder for WordPress", | |
| type: "envato", | |
| category: "Thème WordPress", | |
| author: "Dream-Theme", | |
| price: 39, | |
| sales: 125000, | |
| rating: 4.81, | |
| updated: "2023-10-05", | |
| description: "The7 est un thème WordPress ultra-personnalisable avec options avancées. Compatible avec WooCommerce pour créer des boutiques en ligne performantes.", | |
| image: "https://themeforest.img.customer.envatousercontent.com/files/459385881/the7-preview.jpg?auto=compress%2Cformat&fit=crop&crop=top&w=590&h=300&s=7b1b0c0b1b1b1b1b1b1b1b1b1b1b1b1", | |
| tags: ["wordpress", "woocommerce", "responsive", "customizable"] | |
| }, | |
| { | |
| id: "elementor", | |
| name: "Elementor Pro — Premium Drag & Drop Page Builder", | |
| type: "codecanyon", | |
| category: "Plugin WordPress", | |
| author: "Elementor.com", | |
| price: 49, | |
| sales: 380000, | |
| rating: 4.85, | |
| updated: "2023-09-15", | |
| description: "Elementor Pro est un constructeur de page avancé avec fonctionnalités pro. Créez des sites web professionnels avec une interface glisser-déposer intuitive et des widgets avancés.", | |
| image: "https://s3.envato.com/files/328384320/elementor-preview.jpg", | |
| tags: ["page builder", "wordpress", "drag and drop", "pro"] | |
| }, | |
| { | |
| id: "flatsome", | |
| name: "Flatsome | Multi-Purpose Responsive WooCommerce Theme", | |
| type: "envato", | |
| category: "Thème WordPress", | |
| author: "UX-Themes", | |
| price: 59, | |
| sales: 185000, | |
| rating: 4.83, | |
| updated: "2023-08-28", | |
| description: "Flatsome est un thème WordPress spécialement conçu pour WooCommerce. Il offre des fonctionnalités avancées pour créer des boutiques en ligne performantes et esthétiques.", | |
| image: "https://themeforest.img.customer.envatousercontent.com/files/459385881/flatsome-preview.jpg?auto=compress%2Cformat&fit=crop&crop=top&w=590&h=300&s=7b1b0c0b1b1b1b1b1b1b1b1b1b1b1b1", | |
| tags: ["wordpress", "woocommerce", "ecommerce", "responsive"] | |
| }, | |
| { | |
| id: "ultimate", | |
| name: "Ultimate Addons for Elementor - Power-up Your Elementor Page Builder", | |
| type: "codecanyon", | |
| category: "Plugin WordPress", | |
| author: "Brainstorm Force", | |
| price: 25, | |
| sales: 95000, | |
| rating: 4.79, | |
| updated: "2023-07-30", | |
| description: "Ultimate Addons étend les fonctionnalités d'Elementor avec des widgets et modules supplémentaires. Créez des mises en page encore plus impressionnantes avec ces outils avancés.", | |
| image: "https://s3.envato.com/files/328384320/ultimate-addons-preview.jpg", | |
| tags: ["elementor", "wordpress", "addons", "widgets"] | |
| } | |
| ]; | |
| // DOM Elements | |
| const textSearchTab = document.getElementById('textSearchTab'); | |
| const imageSearchTab = document.getElementById('imageSearchTab'); | |
| const textSearchSection = document.getElementById('textSearchSection'); | |
| const imageSearchSection = document.getElementById('imageSearchSection'); | |
| const searchInput = document.getElementById('searchInput'); | |
| const searchBtn = document.getElementById('searchBtn'); | |
| const dropzone = document.getElementById('dropzone'); | |
| const fileInput = document.getElementById('fileInput'); | |
| const imagePreviewContainer = document.getElementById('imagePreviewContainer'); | |
| const preview = document.getElementById('preview'); | |
| const clearImageBtn = document.getElementById('clearImageBtn'); | |
| const searchImageBtn = document.getElementById('searchImageBtn'); | |
| const resultsSection = document.getElementById('resultsSection'); | |
| const resultsContainer = document.getElementById('resultsContainer'); | |
| const resultsCount = document.getElementById('resultsCount'); | |
| const loadingIndicator = document.getElementById('loadingIndicator'); | |
| const noResults = document.getElementById('noResults'); | |
| const pagination = document.getElementById('pagination'); | |
| const filterEnvato = document.getElementById('filterEnvato'); | |
| const filterCodeCanyon = document.getElementById('filterCodeCanyon'); | |
| const sortBy = document.getElementById('sortBy'); | |
| const resultsSortBy = document.getElementById('resultsSortBy'); | |
| // Tab switching | |
| textSearchTab.addEventListener('click', () => { | |
| textSearchTab.classList.add('bg-green-600'); | |
| imageSearchTab.classList.remove('bg-blue-600'); | |
| textSearchSection.classList.remove('hidden'); | |
| imageSearchSection.classList.add('hidden'); | |
| }); | |
| imageSearchTab.addEventListener('click', () => { | |
| imageSearchTab.classList.add('bg-blue-600'); | |
| textSearchTab.classList.remove('bg-green-600'); | |
| imageSearchSection.classList.remove('hidden'); | |
| textSearchSection.classList.add('hidden'); | |
| }); | |
| // Image upload handling | |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { | |
| dropzone.addEventListener(eventName, preventDefaults, false); | |
| }); | |
| function preventDefaults(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| } | |
| ['dragenter', 'dragover'].forEach(eventName => { | |
| dropzone.addEventListener(eventName, highlight, false); | |
| }); | |
| ['dragleave', 'drop'].forEach(eventName => { | |
| dropzone.addEventListener(eventName, unhighlight, false); | |
| }); | |
| function highlight() { | |
| dropzone.classList.add('border-blue-500', 'bg-blue-50'); | |
| } | |
| function unhighlight() { | |
| dropzone.classList.remove('border-blue-500', 'bg-blue-50'); | |
| } | |
| dropzone.addEventListener('drop', handleDrop, false); | |
| dropzone.addEventListener('click', () => fileInput.click(), false); | |
| fileInput.addEventListener('change', function() { | |
| handleFiles(this.files); | |
| }); | |
| function handleDrop(e) { | |
| const dt = e.dataTransfer; | |
| const files = dt.files; | |
| handleFiles(files); | |
| } | |
| function handleFiles(files) { | |
| if (files.length) { | |
| const file = files[0]; | |
| if (file.type.startsWith('image/')) { | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| preview.src = e.target.result; | |
| imagePreviewContainer.classList.remove('hidden'); | |
| dropzone.classList.add('hidden'); | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| } | |
| } | |
| clearImageBtn.addEventListener('click', function() { | |
| preview.src = '#'; | |
| fileInput.value = ''; | |
| imagePreviewContainer.classList.add('hidden'); | |
| dropzone.classList.remove('hidden'); | |
| }); | |
| // Search functions | |
| searchBtn.addEventListener('click', performTextSearch); | |
| searchInput.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') { | |
| performTextSearch(); | |
| } | |
| }); | |
| searchImageBtn.addEventListener('click', performImageSearch); | |
| function performTextSearch() { | |
| const searchTerm = searchInput.value.trim().toLowerCase(); | |
| if (searchTerm) { | |
| showLoading(); | |
| // Simulate API delay | |
| setTimeout(() => { | |
| const filteredProducts = filterProducts(searchTerm); | |
| displayResults(filteredProducts, searchTerm); | |
| }, 800); | |
| } | |
| } | |
| function performImageSearch() { | |
| if (preview.src && !preview.src.endsWith('#')) { | |
| showLoading(); | |
| // Simulate image search with random results | |
| setTimeout(() => { | |
| const randomProducts = [...products].sort(() => 0.5 - Math.random()).slice(0, 6); | |
| displayResults(randomProducts, '', true); | |
| }, 1200); | |
| } else { | |
| alert("Veuillez d'abord sélectionner une image."); | |
| } | |
| } | |
| function filterProducts(searchTerm) { | |
| const showEnvato = filterEnvato.checked; | |
| const showCodeCanyon = filterCodeCanyon.checked; | |
| const sortMethod = sortBy.value; | |
| let filtered = products.filter(product => { | |
| // Filter by type | |
| if ((product.type === 'envato' && !showEnvato) || | |
| (product.type === 'codecanyon' && !showCodeCanyon)) { | |
| return false; | |
| } | |
| // Filter by search term | |
| return product.name.toLowerCase().includes(searchTerm) || | |
| product.description.toLowerCase().includes(searchTerm) || | |
| product.category.toLowerCase().includes(searchTerm) || | |
| product.author.toLowerCase().includes(searchTerm) || | |
| product.tags.some(tag => tag.toLowerCase().includes(searchTerm)); | |
| }); | |
| // Sort results | |
| switch(sortMethod) { | |
| case 'popular': | |
| filtered.sort((a, b) => b.sales - a.sales); | |
| break; | |
| case 'newest': | |
| filtered.sort((a, b) => new Date(b.updated) - new Date(a.updated)); | |
| break; | |
| case 'price_asc': | |
| filtered.sort((a, b) => a.price - b.price); | |
| break; | |
| case 'price_desc': | |
| filtered.sort((a, b) => b.price - a.price); | |
| break; | |
| default: // relevance | |
| // Simple relevance scoring - more matches = higher relevance | |
| filtered.forEach(product => { | |
| let score = 0; | |
| if (product.name.toLowerCase().includes(searchTerm)) score += 3; | |
| if (product.description.toLowerCase().includes(searchTerm)) score += 2; | |
| if (product.category.toLowerCase().includes(searchTerm)) score += 1; | |
| if (product.author.toLowerCase().includes(searchTerm)) score += 1; | |
| product.tags.forEach(tag => { | |
| if (tag.toLowerCase().includes(searchTerm)) score += 1; | |
| }); | |
| product.relevance = score; | |
| }); | |
| filtered.sort((a, b) => b.relevance - a.relevance); | |
| } | |
| return filtered; | |
| } | |
| function showLoading() { | |
| resultsSection.classList.remove('hidden'); | |
| loadingIndicator.classList.remove('hidden'); | |
| resultsContainer.innerHTML = ''; | |
| noResults.classList.add('hidden'); | |
| pagination.classList.add('hidden'); | |
| } | |
| function displayResults(results, searchTerm = '', isImageSearch = false) { | |
| loadingIndicator.classList.add('hidden'); | |
| if (results.length === 0) { | |
| noResults.classList.remove('hidden'); | |
| resultsCount.textContent = '0'; | |
| pagination.classList.add('hidden'); | |
| return; | |
| } | |
| resultsCount.textContent = results.length; | |
| resultsContainer.innerHTML = ''; | |
| results.forEach(product => { | |
| const productElement = document.createElement('div'); | |
| productElement.className = 'bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition'; | |
| // Highlight search term in results (for text search only) | |
| let highlightedName = product.name; | |
| let highlightedDesc = product.description; | |
| if (searchTerm && !isImageSearch) { | |
| const regex = new RegExp(searchTerm, 'gi'); | |
| highlightedName = product.name.replace(regex, match => | |
| `<span class="search-highlight">${match}</span>` | |
| ); | |
| highlightedDesc = product.description.replace(regex, match => | |
| `<span class="search-highlight">${match}</span>` | |
| ); | |
| } | |
| // Format price | |
| const formattedPrice = new Intl.NumberFormat('fr-FR', { | |
| style: 'currency', | |
| currency: 'USD' | |
| }).format(product.price); | |
| productElement.innerHTML = ` | |
| <div class="relative"> | |
| <img src="${product.image}" alt="${product.name}" class="w-full h-48 object-cover"> | |
| <span class="absolute top-2 left-2 px-2 py-1 rounded-md text-xs font-semibold text-white ${product.type === 'envato' ? 'envato-badge' : 'codecanyon-badge'}"> | |
| ${product.type === 'envato' ? 'Envato' : 'CodeCanyon'} | |
| </span> | |
| ${isImageSearch ? ` | |
| <div class="absolute top-2 right-2 bg-white bg-opacity-90 px-2 py-1 rounded-md text-xs font-semibold"> | |
| <i class="fas fa-percentage mr-1"></i> ${Math.floor(Math.random() * 20) + 80}% match | |
| </div> | |
| ` : ''} | |
| </div> | |
| <div class="p-4"> | |
| <h3 class="font-bold text-lg text-gray-800 mb-1">${highlightedName}</h3> | |
| <p class="text-sm text-gray-600 mb-2">Par ${product.author}</p> | |
| <p class="text-gray-700 text-sm mb-3 line-clamp-2">${highlightedDesc}</p> | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <span class="font-bold text-gray-900">${formattedPrice}</span> | |
| <span class="text-xs text-gray-500 ml-1">+ TVA si applicable</span> | |
| </div> | |
| <div class="flex items-center"> | |
| <i class="fas fa-star text-yellow-400 mr-1"></i> | |
| <span class="text-sm font-medium">${product.rating}</span> | |
| <span class="text-xs text-gray-500 ml-1">(${Math.floor(product.sales / 1000)}k)</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="px-4 py-3 bg-gray-50 border-t border-gray-100 flex justify-between"> | |
| <a href="#" class="text-blue-600 hover:text-blue-800 text-sm font-medium"> | |
| <i class="far fa-eye mr-1"></i> Voir détails | |
| </a> | |
| <button class="text-green-600 hover:text-green-800 text-sm font-medium add-to-cart" data-id="${product.id}"> | |
| <i class="fas fa-cart-plus mr-1"></i> Ajouter | |
| </button> | |
| </div> | |
| `; | |
| resultsContainer.appendChild(productElement); | |
| }); | |
| // Show pagination if enough results | |
| if (results.length > 6) { | |
| pagination.classList.remove('hidden'); | |
| } else { | |
| pagination.classList.add('hidden'); | |
| } | |
| } | |
| // Add to cart functionality | |
| document.addEventListener('click', function(e) { | |
| if (e.target.classList.contains('add-to-cart') || e.target.closest('.add-to-cart')) { | |
| const button = e.target.classList.contains('add-to-cart') ? e.target : e.target.closest('.add-to-cart'); | |
| const productId = button.getAttribute('data-id'); | |
| const product = products.find(p => p.id === productId); | |
| // Show added notification | |
| const notification = document.createElement('div'); | |
| notification.className = 'fixed top-4 right-4 bg-green-600 text-white px-4 py-2 rounded-md shadow-lg flex items-center animate-fade-in'; | |
| notification.innerHTML = ` | |
| <i class="fas fa-check-circle mr-2"></i> | |
| "${product.name}" ajouté au panier | |
| `; | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| notification.classList.add('animate-fade-out'); | |
| setTimeout(() => notification.remove(), 300); | |
| }, 3000); | |
| } | |
| }); | |
| // Results sorting | |
| resultsSortBy.addEventListener('change', function() { | |
| const currentResults = Array.from(resultsContainer.children).map(el => { | |
| const id = el.querySelector('.add-to-cart').getAttribute('data-id'); | |
| return products.find(p => p.id === id); | |
| }); | |
| const sortMethod = this.value; | |
| switch(sortMethod) { | |
| case 'popular': | |
| currentResults.sort((a, b) => b.sales - a.sales); | |
| break; | |
| case 'newest': | |
| currentResults.sort((a, b) => new Date(b.updated) - new Date(a.updated)); | |
| break; | |
| case 'price_asc': | |
| currentResults.sort((a, b) => a.price - b.price); | |
| break; | |
| case 'price_desc': | |
| currentResults.sort((a, b) => b.price - a.price); | |
| break; | |
| default: // relevance - keep original order | |
| break; | |
| } | |
| displayResults(currentResults); | |
| }); | |
| </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=docto41/envato-code" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |