| | <!DOCTYPE html> |
| | <html lang="fr"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Prompt Manager</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 = { |
| | theme: { |
| | extend: { |
| | colors: { |
| | pastel: { |
| | pink: '#FFD1DC', |
| | purple: '#E0BBE4', |
| | blue: '#B5EAD7', |
| | yellow: '#FFE5B4', |
| | green: '#C7E9C0', |
| | peach: '#FFC8A2', |
| | lavender: '#D4A5C3', |
| | mint: '#A2D7D8', |
| | }, |
| | border: { |
| | active: '#8B5CF6', |
| | } |
| | } |
| | } |
| | } |
| | } |
| | </script> |
| | <style> |
| | .prompt-card:hover { |
| | transform: translateY(-5px); |
| | box-shadow: 0 10px 20px rgba(0,0,0,0.1); |
| | } |
| | .tag:hover { |
| | transform: scale(1.05); |
| | } |
| | .category-btn:hover { |
| | transform: scale(1.03); |
| | } |
| | .fade-in { |
| | animation: fadeIn 0.3s ease-in-out; |
| | } |
| | @keyframes fadeIn { |
| | from { opacity: 0; } |
| | to { opacity: 1; } |
| | } |
| | .scrollbar-hide::-webkit-scrollbar { |
| | display: none; |
| | } |
| | .star-rating .star { |
| | cursor: pointer; |
| | transition: all 0.2s; |
| | } |
| | .star-rating .star:hover { |
| | transform: scale(1.2); |
| | } |
| | .active-filter { |
| | border: 2px solid #8B5CF6; |
| | box-shadow: 0 0 0 1px rgba(139, 92, 246, 0.3); |
| | } |
| | .prompt-modal-content { |
| | max-height: 80vh; |
| | overflow-y: auto; |
| | } |
| | .ai-select-container { |
| | position: relative; |
| | } |
| | .ai-select-dropdown { |
| | display: none; |
| | position: absolute; |
| | background: white; |
| | border: 1px solid #ddd; |
| | border-radius: 0.5rem; |
| | width: 100%; |
| | z-index: 10; |
| | max-height: 200px; |
| | overflow-y: auto; |
| | } |
| | .ai-select-container.open .ai-select-dropdown { |
| | display: block; |
| | } |
| | .filter-dropdown { |
| | position: relative; |
| | } |
| | .filter-dropdown-btn { |
| | width: 100%; |
| | text-align: left; |
| | } |
| | .filter-dropdown-content { |
| | display: none; |
| | position: absolute; |
| | background-color: white; |
| | min-width: 160px; |
| | box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); |
| | z-index: 1; |
| | border-radius: 0.5rem; |
| | max-height: 300px; |
| | overflow-y: auto; |
| | } |
| | .filter-dropdown:hover .filter-dropdown-content { |
| | display: block; |
| | } |
| | .filter-dropdown-item { |
| | padding: 8px 16px; |
| | cursor: pointer; |
| | } |
| | .filter-dropdown-item:hover { |
| | background-color: #f1f1f1; |
| | } |
| | </style> |
| | </head> |
| | <body class="bg-pastel-blue min-h-screen"> |
| | <div class="container mx-auto px-4 py-8"> |
| | |
| | <header class="flex justify-between items-center mb-8"> |
| | <div class="flex items-center"> |
| | <i class="fas fa-database text-4xl text-pastel-purple mr-4"></i> |
| | <h1 class="text-4xl font-bold text-gray-700">Prompt Manager</h1> |
| | </div> |
| | <button id="addPromptBtn" class="bg-pastel-pink hover:bg-pastel-lavender text-white px-6 py-3 rounded-full shadow-md transition-all"> |
| | <i class="fas fa-plus mr-2"></i> Nouveau Prompt |
| | </button> |
| | </header> |
| |
|
| | |
| | <div class="flex flex-col lg:flex-row gap-8"> |
| | |
| | <div class="w-full lg:w-1/4 bg-white rounded-2xl p-6 shadow-lg"> |
| | <div class="mb-8"> |
| | <h2 class="text-xl font-semibold text-gray-700 mb-4">Catégories</h2> |
| | <div class="space-y-2"> |
| | <button class="category-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-green hover:bg-pastel-mint transition-all" data-category="all"> |
| | <i class="fas fa-list mr-2"></i> Tous les prompts |
| | </button> |
| | <button class="category-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-yellow hover:bg-pastel-peach transition-all" data-category="creative"> |
| | <i class="fas fa-paint-brush mr-2"></i> Créatif |
| | </button> |
| | <button class="category-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-purple hover:bg-pastel-lavender transition-all" data-category="technical"> |
| | <i class="fas fa-code mr-2"></i> Technique |
| | </button> |
| | <button class="category-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-peach hover:bg-pastel-pink transition-all" data-category="business"> |
| | <i class="fas fa-briefcase mr-2"></i> Business |
| | </button> |
| | </div> |
| | </div> |
| |
|
| | <div class="mb-8"> |
| | <h2 class="text-xl font-semibold text-gray-700 mb-4">Trier par</h2> |
| | <div class="space-y-2"> |
| | <button class="sort-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-blue hover:bg-pastel-mint transition-all" data-sort="date-desc"> |
| | <i class="fas fa-clock mr-2"></i> Plus récent |
| | </button> |
| | <button class="sort-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-lavender hover:bg-pastel-pink transition-all" data-sort="rating-desc"> |
| | <i class="fas fa-star mr-2"></i> Meilleures notes |
| | </button> |
| | </div> |
| | </div> |
| |
|
| | <div> |
| | <h2 class="text-xl font-semibold text-gray-700 mb-4">Filtres</h2> |
| | <div class="mb-4"> |
| | <h3 class="text-sm font-medium text-gray-700 mb-2">IA Optimisée</h3> |
| | <div class="filter-dropdown"> |
| | <button class="filter-dropdown-btn px-4 py-2 bg-gray-100 rounded-lg w-full text-left flex justify-between items-center"> |
| | <span>Sélectionner une IA</span> |
| | <i class="fas fa-chevron-down"></i> |
| | </button> |
| | <div class="filter-dropdown-content"> |
| | <div class="p-2"> |
| | <h4 class="font-medium text-sm mb-1 text-gray-500">OpenAI</h4> |
| | <div class="space-y-1"> |
| | <div class="filter-dropdown-item" data-ai="gpt-4">GPT-4</div> |
| | <div class="filter-dropdown-item" data-ai="gpt-4-turbo">GPT-4 Turbo</div> |
| | <div class="filter-dropdown-item" data-ai="gpt-4o">GPT-4o</div> |
| | <div class="filter-dropdown-item" data-ai="gpt-3.5">GPT-3.5</div> |
| | </div> |
| | </div> |
| | <div class="p-2"> |
| | <h4 class="font-medium text-sm mb-1 text-gray-500">Google</h4> |
| | <div class="space-y-1"> |
| | <div class="filter-dropdown-item" data-ai="gemini-pro">Gemini Pro</div> |
| | <div class="filter-dropdown-item" data-ai="gemini-ultra">Gemini Ultra</div> |
| | <div class="filter-dropdown-item" data-ai="bard">Bard</div> |
| | </div> |
| | </div> |
| | <div class="p-2"> |
| | <h4 class="font-medium text-sm mb-1 text-gray-500">Anthropic</h4> |
| | <div class="space-y-1"> |
| | <div class="filter-dropdown-item" data-ai="claude-3-opus">Claude 3 Opus</div> |
| | <div class="filter-dropdown-item" data-ai="claude-3-sonnet">Claude 3 Sonnet</div> |
| | <div class="filter-dropdown-item" data-ai="claude-3-haiku">Claude 3 Haiku</div> |
| | <div class="filter-dropdown-item" data-ai="claude-2">Claude 2</div> |
| | </div> |
| | </div> |
| | <div class="p-2"> |
| | <h4 class="font-medium text-sm mb-1 text-gray-500">Meta</h4> |
| | <div class="space-y-1"> |
| | <div class="filter-dropdown-item" data-ai="llama-3">Llama 3</div> |
| | <div class="filter-dropdown-item" data-ai="llama-2">Llama 2</div> |
| | </div> |
| | </div> |
| | <div class="p-2"> |
| | <h4 class="font-medium text-sm mb-1 text-gray-500">Autres</h4> |
| | <div class="space-y-1"> |
| | <div class="filter-dropdown-item" data-ai="mistral">Mistral</div> |
| | <div class="filter-dropdown-item" data-ai="copilot">Copilot</div> |
| | <div class="filter-dropdown-item" data-ai="perplexity">Perplexity</div> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | <div class="flex flex-wrap gap-2 mt-2" id="selectedAIFilters"></div> |
| | </div> |
| | <div> |
| | <h3 class="text-sm font-medium text-gray-700 mb-2">Tags</h3> |
| | <div class="flex flex-wrap gap-2"> |
| | <span class="tag cursor-pointer px-3 py-1 bg-pastel-mint rounded-full text-sm transition-all" data-tag="ia">IA</span> |
| | <span class="tag cursor-pointer px-3 py-1 bg-pastel-yellow rounded-full text-sm transition-all" data-tag="marketing">Marketing</span> |
| | <span class="tag cursor-pointer px-3 py-1 bg-pastel-pink rounded-full text-sm transition-all" data-tag="copywriting">Copywriting</span> |
| | <span class="tag cursor-pointer px-3 py-1 bg-pastel-green rounded-full text-sm transition-all" data-tag="productivite">Productivité</span> |
| | <span class="tag cursor-pointer px-3 py-1 bg-pastel-purple rounded-full text-sm transition-all" data-tag="design">Design</span> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="w-full lg:w-3/4"> |
| | |
| | <div class="flex justify-between items-center mb-6 bg-white p-4 rounded-2xl shadow-lg"> |
| | <h2 class="text-xl font-semibold text-gray-700">Mes Prompts</h2> |
| | <div class="flex space-x-2"> |
| | <button id="listViewBtn" class="p-2 bg-pastel-blue rounded-lg text-gray-700"> |
| | <i class="fas fa-list"></i> |
| | </button> |
| | <button id="gridViewBtn" class="p-2 bg-pastel-green rounded-lg text-gray-700"> |
| | <i class="fas fa-th-large"></i> |
| | </button> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="promptsContainer" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> |
| | |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="promptModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| | <div class="bg-white rounded-2xl p-8 w-full max-w-2xl relative"> |
| | <button id="closeModalBtn" class="absolute top-4 right-4 text-gray-500 hover:text-gray-700"> |
| | <i class="fas fa-times text-2xl"></i> |
| | </button> |
| | <h2 class="text-2xl font-bold text-gray-700 mb-6" id="modalTitle">Ajouter un nouveau prompt</h2> |
| | |
| | <form id="promptForm" class="space-y-6"> |
| | <input type="hidden" id="promptId"> |
| | <div> |
| | <label for="promptTitle" class="block text-sm font-medium text-gray-700 mb-1">Titre</label> |
| | <input type="text" id="promptTitle" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-pastel-purple focus:border-transparent"> |
| | </div> |
| | |
| | <div> |
| | <label for="promptContent" class="block text-sm font-medium text-gray-700 mb-1">Contenu du prompt</label> |
| | <textarea id="promptContent" rows="5" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-pastel-purple focus:border-transparent"></textarea> |
| | </div> |
| | |
| | <div> |
| | <label class="block text-sm font-medium text-gray-700 mb-1">Catégorie</label> |
| | <div class="flex flex-wrap gap-2"> |
| | <label class="inline-flex items-center"> |
| | <input type="radio" name="category" value="creative" class="h-4 w-4 text-pastel-yellow focus:ring-pastel-yellow"> |
| | <span class="ml-2 text-gray-700">Créatif</span> |
| | </label> |
| | <label class="inline-flex items-center"> |
| | <input type="radio" name="category" value="technical" class="h-4 w-4 text-pastel-purple focus:ring-pastel-purple"> |
| | <span class="ml-2 text-gray-700">Technique</span> |
| | </label> |
| | <label class="inline-flex items-center"> |
| | <input type="radio" name="category" value="business" class="h-4 w-4 text-pastel-peach focus:ring-pastel-peach"> |
| | <span class="ml-2 text-gray-700">Business</span> |
| | </label> |
| | </div> |
| | </div> |
| | |
| | <div> |
| | <label class="block text-sm font-medium text-gray-700 mb-1">Optimisé pour</label> |
| | <div class="ai-select-container"> |
| | <div class="flex flex-wrap gap-2" id="selectedAIList"></div> |
| | <div class="relative mt-2"> |
| | <input type="text" id="aiSearch" class="w-full px-4 py-2 border border-gray-300 rounded-lg" placeholder="Rechercher ou ajouter une IA..."> |
| | <div class="ai-select-dropdown" id="aiDropdown"></div> |
| | </div> |
| | <button type="button" id="addNewAI" class="hidden mt-2 text-sm text-pastel-purple hover:underline">Ajouter une nouvelle IA</button> |
| | </div> |
| | </div> |
| | |
| | <div> |
| | <label for="promptTags" class="block text-sm font-medium text-gray-700 mb-1">Tags (séparés par des virgules)</label> |
| | <input type="text" id="promptTags" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-pastel-purple focus:border-transparent" placeholder="IA, marketing, productivité"> |
| | </div> |
| | |
| | <div> |
| | <label class="block text-sm font-medium text-gray-700 mb-1">Note</label> |
| | <div class="star-rating flex" id="editRating"> |
| | <i class="far fa-star star text-2xl text-pastel-yellow" data-rating="1"></i> |
| | <i class="far fa-star star text-2xl text-pastel-yellow" data-rating="2"></i> |
| | <i class="far fa-star star text-2xl text-pastel-yellow" data-rating="3"></i> |
| | <i class="far fa-star star text-2xl text-pastel-yellow" data-rating="4"></i> |
| | <i class="far fa-star star text-2xl text-pastel-yellow" data-rating="5"></i> |
| | </div> |
| | <input type="hidden" id="promptRating" value="0"> |
| | </div> |
| | |
| | <div class="flex justify-end space-x-4"> |
| | <button type="button" id="cancelPromptBtn" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-all"> |
| | Annuler |
| | </button> |
| | <button type="submit" class="px-6 py-2 bg-pastel-pink text-white rounded-lg hover:bg-pastel-lavender transition-all"> |
| | Enregistrer |
| | </button> |
| | </div> |
| | </form> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="viewPromptModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| | <div class="bg-white rounded-2xl p-8 w-full max-w-4xl relative"> |
| | <button id="closeViewModalBtn" class="absolute top-4 right-4 text-gray-500 hover:text-gray-700"> |
| | <i class="fas fa-times text-2xl"></i> |
| | </button> |
| | <div class="flex justify-between items-center mb-6"> |
| | <h2 class="text-2xl font-bold text-gray-700" id="viewPromptTitle"></h2> |
| | <div class="flex space-x-4"> |
| | <button id="editPromptBtn" class="px-4 py-2 bg-pastel-blue text-white rounded-lg hover:bg-pastel-mint transition-all"> |
| | <i class="fas fa-edit mr-2"></i> Modifier |
| | </button> |
| | <button id="copyPromptBtn" class="px-4 py-2 bg-pastel-green text-white rounded-lg hover:bg-pastel-mint transition-all"> |
| | <i class="fas fa-copy mr-2"></i> Copier |
| | </button> |
| | </div> |
| | </div> |
| | |
| | <div class="prompt-modal-content space-y-6"> |
| | <div class="bg-gray-50 p-4 rounded-lg"> |
| | <pre class="whitespace-pre-wrap text-gray-800" id="viewPromptContent"></pre> |
| | </div> |
| | |
| | <div> |
| | <h3 class="text-lg font-semibold text-gray-700 mb-2">Détails</h3> |
| | <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
| | <div> |
| | <h4 class="text-sm font-medium text-gray-500">Catégorie</h4> |
| | <p class="text-gray-800" id="viewPromptCategory"></p> |
| | </div> |
| | <div> |
| | <h4 class="text-sm font-medium text-gray-500">Optimisé pour</h4> |
| | <div class="flex flex-wrap gap-2" id="viewPromptAI"></div> |
| | </div> |
| | <div> |
| | <h4 class="text-sm font-medium text-gray-500">Tags</h4> |
| | <div class="flex flex-wrap gap-2" id="viewPromptTags"></div> |
| | </div> |
| | <div> |
| | <h4 class="text-sm font-medium text-gray-500">Note</h4> |
| | <div class="flex" id="viewPromptRating"></div> |
| | </div> |
| | <div> |
| | <h4 class="text-sm font-medium text-gray-500">Date de création</h4> |
| | <p class="text-gray-800" id="viewPromptDate"></p> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <script> |
| | |
| | |
| | |
| | |
| | class PromptDatabase { |
| | constructor() { |
| | this.key = 'promptManagerDatabase'; |
| | this.availableAIs = [ |
| | |
| | { id: "gpt-4", name: "GPT-4" }, |
| | { id: "gpt-4-turbo", name: "GPT-4 Turbo" }, |
| | { id: "gpt-4o", name: "GPT-4o" }, |
| | { id: "gpt-3.5", name: "GPT-3.5" }, |
| | |
| | |
| | { id: "gemini-pro", name: "Gemini Pro" }, |
| | { id: "gemini-ultra", name: "Gemini Ultra" }, |
| | { id: "bard", name: "Bard" }, |
| | |
| | |
| | { id: "claude-3-opus", name: "Claude 3 Opus" }, |
| | { id: "claude-3-sonnet", name: "Claude 3 Sonnet" }, |
| | { id: "claude-3-haiku", name: "Claude 3 Haiku" }, |
| | { id: "claude-2", name: "Claude 2" }, |
| | |
| | |
| | { id: "llama-3", name: "Llama 3" }, |
| | { id: "llama-2", name: "Llama 2" }, |
| | |
| | |
| | { id: "mistral", name: "Mistral" }, |
| | { id: "copilot", name: "Copilot" }, |
| | { id: "perplexity", name: "Perplexity" } |
| | ]; |
| | |
| | |
| | if (!this.load()) { |
| | this.prompts = [ |
| | { |
| | id: 1, |
| | title: "Générer des idées de contenu", |
| | content: "Génère 10 idées de contenu originales pour un blog sur [thème]. Les idées doivent être engageantes et adaptées à un public de [démographie].", |
| | category: "creative", |
| | tags: ["marketing", "copywriting"], |
| | ai: ["gpt-4", "gemini-pro"], |
| | createdAt: new Date('2023-05-15'), |
| | rating: 4 |
| | }, |
| | { |
| | id: 2, |
| | title: "Optimiser le code Python", |
| | content: "Analyse ce code Python et suggère des optimisations pour améliorer les performances : [coller le code]", |
| | category: "technical", |
| | tags: ["ia", "productivite"], |
| | ai: ["gpt-4-turbo", "copilot"], |
| | createdAt: new Date('2023-06-20'), |
| | rating: 5 |
| | }, |
| | { |
| | id: 3, |
| | title: "Stratégie de croissance", |
| | content: "Développe une stratégie de croissance en 5 étapes pour une startup dans le domaine de [industrie] avec un budget de [montant].", |
| | category: "business", |
| | tags: ["marketing", "business"], |
| | ai: ["claude-3-sonnet", "gemini-pro"], |
| | createdAt: new Date('2023-07-10'), |
| | rating: 3 |
| | } |
| | ]; |
| | this.save(); |
| | } |
| | } |
| | |
| | |
| | load() { |
| | const data = localStorage.getItem(this.key); |
| | if (data) { |
| | try { |
| | const parsed = JSON.parse(data); |
| | this.prompts = parsed.prompts.map(p => ({ |
| | ...p, |
| | createdAt: new Date(p.createdAt) |
| | })); |
| | return true; |
| | } catch (e) { |
| | console.error("Erreur de chargement des données", e); |
| | return false; |
| | } |
| | } |
| | return false; |
| | } |
| | |
| | |
| | save() { |
| | const data = { |
| | prompts: this.prompts, |
| | availableAIs: this.availableAIs |
| | }; |
| | localStorage.setItem(this.key, JSON.stringify(data)); |
| | } |
| | |
| | |
| | addPrompt(prompt) { |
| | prompt.id = this.prompts.length > 0 ? Math.max(...this.prompts.map(p => p.id)) + 1 : 1; |
| | prompt.createdAt = new Date(); |
| | this.prompts.unshift(prompt); |
| | this.save(); |
| | return prompt; |
| | } |
| | |
| | |
| | updatePrompt(id, updatedPrompt) { |
| | const index = this.prompts.findIndex(p => p.id === id); |
| | if (index !== -1) { |
| | this.prompts[index] = { |
| | ...this.prompts[index], |
| | ...updatedPrompt |
| | }; |
| | this.save(); |
| | return this.prompts[index]; |
| | } |
| | return null; |
| | } |
| | |
| | |
| | deletePrompt(id) { |
| | const index = this.prompts.findIndex(p => p.id === id); |
| | if (index !== -1) { |
| | this.prompts.splice(index, 1); |
| | this.save(); |
| | return true; |
| | } |
| | return false; |
| | } |
| | |
| | |
| | getAllPrompts() { |
| | return [...this.prompts]; |
| | } |
| | |
| | |
| | getPromptById(id) { |
| | return this.prompts.find(p => p.id === id); |
| | } |
| | |
| | |
| | filterPrompts(filterFn) { |
| | return this.prompts.filter(filterFn); |
| | } |
| | |
| | |
| | addAI(ai) { |
| | if (!this.availableAIs.some(a => a.id === ai.id)) { |
| | this.availableAIs.push(ai); |
| | this.save(); |
| | } |
| | return ai; |
| | } |
| | |
| | |
| | getAIName(aiId) { |
| | const ai = this.availableAIs.find(a => a.id === aiId); |
| | return ai ? ai.name : aiId; |
| | } |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | const db = new PromptDatabase(); |
| | |
| | |
| | const promptsContainer = document.getElementById('promptsContainer'); |
| | const addPromptBtn = document.getElementById('addPromptBtn'); |
| | const promptModal = document.getElementById('promptModal'); |
| | const viewPromptModal = document.getElementById('viewPromptModal'); |
| | const closeModalBtn = document.getElementById('closeModalBtn'); |
| | const closeViewModalBtn = document.getElementById('closeViewModalBtn'); |
| | const cancelPromptBtn = document.getElementById('cancelPromptBtn'); |
| | const promptForm = document.getElementById('promptForm'); |
| | const listViewBtn = document.getElementById('listViewBtn'); |
| | const gridViewBtn = document.getElementById('gridViewBtn'); |
| | const categoryBtns = document.querySelectorAll('.category-btn'); |
| | const sortBtns = document.querySelectorAll('.sort-btn'); |
| | const tags = document.querySelectorAll('.tag'); |
| | const modalTitle = document.getElementById('modalTitle'); |
| | const promptIdInput = document.getElementById('promptId'); |
| | const promptTitleInput = document.getElementById('promptTitle'); |
| | const promptContentInput = document.getElementById('promptContent'); |
| | const promptTagsInput = document.getElementById('promptTags'); |
| | const promptRatingInput = document.getElementById('promptRating'); |
| | const editRating = document.getElementById('editRating'); |
| | const selectedAIList = document.getElementById('selectedAIList'); |
| | const aiSearch = document.getElementById('aiSearch'); |
| | const aiDropdown = document.getElementById('aiDropdown'); |
| | const addNewAI = document.getElementById('addNewAI'); |
| | const viewPromptTitle = document.getElementById('viewPromptTitle'); |
| | const viewPromptContent = document.getElementById('viewPromptContent'); |
| | const viewPromptCategory = document.getElementById('viewPromptCategory'); |
| | const viewPromptAI = document.getElementById('viewPromptAI'); |
| | const viewPromptTags = document.getElementById('viewPromptTags'); |
| | const viewPromptRating = document.getElementById('viewPromptRating'); |
| | const viewPromptDate = document.getElementById('viewPromptDate'); |
| | const editPromptBtn = document.getElementById('editPromptBtn'); |
| | const copyPromptBtn = document.getElementById('copyPromptBtn'); |
| | const selectedAIFilters = document.getElementById('selectedAIFilters'); |
| | const filterDropdownItems = document.querySelectorAll('.filter-dropdown-item'); |
| | |
| | |
| | let currentView = 'grid'; |
| | let currentCategory = 'all'; |
| | let currentTag = null; |
| | let currentAI = null; |
| | let currentSort = 'date-desc'; |
| | let currentPromptId = null; |
| | let selectedAIs = []; |
| | let isEditing = false; |
| | |
| | |
| | function init() { |
| | renderPrompts(); |
| | setupEventListeners(); |
| | updateViewButtons(); |
| | updateActiveFilters(); |
| | renderAIDropdown(); |
| | } |
| | |
| | |
| | function setupEventListeners() { |
| | |
| | addPromptBtn.addEventListener('click', () => { |
| | isEditing = false; |
| | modalTitle.textContent = "Ajouter un nouveau prompt"; |
| | promptForm.reset(); |
| | selectedAIs = []; |
| | renderSelectedAIs(); |
| | resetRatingStars(editRating); |
| | promptRatingInput.value = "0"; |
| | promptModal.classList.remove('hidden'); |
| | }); |
| | |
| | closeModalBtn.addEventListener('click', () => { |
| | promptModal.classList.add('hidden'); |
| | }); |
| | |
| | closeViewModalBtn.addEventListener('click', () => { |
| | viewPromptModal.classList.add('hidden'); |
| | }); |
| | |
| | cancelPromptBtn.addEventListener('click', () => { |
| | promptModal.classList.add('hidden'); |
| | }); |
| | |
| | |
| | promptForm.addEventListener('submit', (e) => { |
| | e.preventDefault(); |
| | savePrompt(); |
| | }); |
| | |
| | |
| | listViewBtn.addEventListener('click', () => { |
| | currentView = 'list'; |
| | renderPrompts(); |
| | updateViewButtons(); |
| | }); |
| | |
| | gridViewBtn.addEventListener('click', () => { |
| | currentView = 'grid'; |
| | renderPrompts(); |
| | updateViewButtons(); |
| | }); |
| | |
| | |
| | categoryBtns.forEach(btn => { |
| | btn.addEventListener('click', () => { |
| | currentCategory = btn.dataset.category; |
| | currentTag = null; |
| | currentAI = null; |
| | renderPrompts(); |
| | updateActiveFilters(); |
| | }); |
| | }); |
| | |
| | |
| | sortBtns.forEach(btn => { |
| | btn.addEventListener('click', () => { |
| | currentSort = btn.dataset.sort; |
| | renderPrompts(); |
| | updateActiveFilters(); |
| | }); |
| | }); |
| | |
| | |
| | tags.forEach(tag => { |
| | tag.addEventListener('click', () => { |
| | currentTag = tag.dataset.tag; |
| | currentCategory = 'all'; |
| | currentAI = null; |
| | renderPrompts(); |
| | updateActiveFilters(); |
| | }); |
| | }); |
| | |
| | |
| | filterDropdownItems.forEach(item => { |
| | item.addEventListener('click', (e) => { |
| | e.stopPropagation(); |
| | currentAI = item.dataset.ai; |
| | currentCategory = 'all'; |
| | currentTag = null; |
| | renderPrompts(); |
| | updateActiveFilters(); |
| | |
| | |
| | selectedAIFilters.innerHTML = ''; |
| | if (currentAI) { |
| | const ai = db.availableAIs.find(a => a.id === currentAI) || { id: currentAI, name: currentAI }; |
| | const chip = document.createElement('span'); |
| | chip.className = 'px-3 py-1 bg-pastel-blue rounded-full text-sm flex items-center'; |
| | chip.innerHTML = ` |
| | ${ai.name} |
| | <button class="ml-2 text-xs" data-ai-id="${ai.id}"> |
| | <i class="fas fa-times"></i> |
| | </button> |
| | `; |
| | selectedAIFilters.appendChild(chip); |
| | |
| | |
| | const removeBtn = chip.querySelector('button'); |
| | removeBtn.addEventListener('click', (e) => { |
| | e.stopPropagation(); |
| | currentAI = null; |
| | selectedAIFilters.innerHTML = ''; |
| | renderPrompts(); |
| | updateActiveFilters(); |
| | }); |
| | } |
| | }); |
| | }); |
| | |
| | |
| | aiSearch.addEventListener('input', () => { |
| | renderAIDropdown(); |
| | }); |
| | |
| | aiSearch.addEventListener('focus', () => { |
| | document.querySelector('.ai-select-container').classList.add('open'); |
| | renderAIDropdown(); |
| | }); |
| | |
| | document.addEventListener('click', (e) => { |
| | if (!e.target.closest('.ai-select-container')) { |
| | document.querySelector('.ai-select-container').classList.remove('open'); |
| | } |
| | }); |
| | |
| | |
| | editPromptBtn.addEventListener('click', () => { |
| | if (currentPromptId) { |
| | editPrompt(currentPromptId); |
| | } |
| | }); |
| | |
| | |
| | copyPromptBtn.addEventListener('click', () => { |
| | if (currentPromptId) { |
| | const prompt = db.getPromptById(currentPromptId); |
| | if (prompt) { |
| | navigator.clipboard.writeText(prompt.content) |
| | .then(() => { |
| | |
| | const copyBtn = copyPromptBtn; |
| | const originalText = copyBtn.innerHTML; |
| | copyBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Copié!'; |
| | setTimeout(() => { |
| | copyBtn.innerHTML = originalText; |
| | }, 2000); |
| | }) |
| | .catch(err => { |
| | console.error('Failed to copy text: ', err); |
| | }); |
| | } |
| | } |
| | }); |
| | |
| | |
| | setupRatingStars(editRating); |
| | } |
| | |
| | |
| | function updateActiveFilters() { |
| | |
| | categoryBtns.forEach(btn => { |
| | if (btn.dataset.category === currentCategory) { |
| | btn.classList.add('active-filter'); |
| | } else { |
| | btn.classList.remove('active-filter'); |
| | } |
| | }); |
| | |
| | |
| | sortBtns.forEach(btn => { |
| | if (btn.dataset.sort === currentSort) { |
| | btn.classList.add('active-filter'); |
| | } else { |
| | btn.classList.remove('active-filter'); |
| | } |
| | }); |
| | |
| | |
| | tags.forEach(tag => { |
| | if (tag.dataset.tag === currentTag) { |
| | tag.classList.add('active-filter'); |
| | } else { |
| | tag.classList.remove('active-filter'); |
| | } |
| | }); |
| | } |
| | |
| | |
| | function updateViewButtons() { |
| | if (currentView === 'list') { |
| | listViewBtn.classList.add('bg-pastel-purple', 'text-white'); |
| | gridViewBtn.classList.remove('bg-pastel-purple', 'text-white'); |
| | } else { |
| | gridViewBtn.classList.add('bg-pastel-purple', 'text-white'); |
| | listViewBtn.classList.remove('bg-pastel-purple', 'text-white'); |
| | } |
| | } |
| | |
| | |
| | function savePrompt() { |
| | const title = promptTitleInput.value; |
| | const content = promptContentInput.value; |
| | const category = document.querySelector('input[name="category"]:checked').value; |
| | |
| | |
| | const tags = promptTagsInput.value |
| | .split(',') |
| | .map(tag => tag.trim()) |
| | .filter(tag => tag.length > 0); |
| | |
| | |
| | const rating = parseInt(promptRatingInput.value) || 0; |
| | |
| | if (isEditing) { |
| | |
| | const updatedPrompt = { |
| | title, |
| | content, |
| | category, |
| | tags, |
| | ai: selectedAIs, |
| | rating |
| | }; |
| | |
| | db.updatePrompt(currentPromptId, updatedPrompt); |
| | } else { |
| | |
| | const newPrompt = { |
| | title, |
| | content, |
| | category, |
| | tags, |
| | ai: selectedAIs, |
| | rating |
| | }; |
| | |
| | db.addPrompt(newPrompt); |
| | } |
| | |
| | renderPrompts(); |
| | promptModal.classList.add('hidden'); |
| | if (isEditing) { |
| | viewPromptModal.classList.add('hidden'); |
| | } |
| | } |
| | |
| | |
| | function editPrompt(id) { |
| | const prompt = db.getPromptById(id); |
| | if (prompt) { |
| | isEditing = true; |
| | currentPromptId = id; |
| | modalTitle.textContent = "Modifier le prompt"; |
| | |
| | |
| | promptIdInput.value = prompt.id; |
| | promptTitleInput.value = prompt.title; |
| | promptContentInput.value = prompt.content; |
| | document.querySelector(`input[name="category"][value="${prompt.category}"]`).checked = true; |
| | promptTagsInput.value = prompt.tags.join(', '); |
| | promptRatingInput.value = prompt.rating; |
| | |
| | |
| | selectedAIs = [...prompt.ai]; |
| | renderSelectedAIs(); |
| | |
| | |
| | setRatingStars(editRating, prompt.rating); |
| | |
| | promptModal.classList.remove('hidden'); |
| | viewPromptModal.classList.add('hidden'); |
| | } |
| | } |
| | |
| | |
| | function viewPrompt(id) { |
| | const prompt = db.getPromptById(id); |
| | if (prompt) { |
| | currentPromptId = id; |
| | |
| | |
| | viewPromptTitle.textContent = prompt.title; |
| | viewPromptContent.textContent = prompt.content; |
| | viewPromptCategory.textContent = getCategoryName(prompt.category); |
| | |
| | |
| | viewPromptAI.innerHTML = ''; |
| | prompt.ai.forEach(ai => { |
| | const aiElement = document.createElement('span'); |
| | aiElement.className = 'px-3 py-1 bg-pastel-blue rounded-full text-sm'; |
| | aiElement.textContent = db.getAIName(ai); |
| | viewPromptAI.appendChild(aiElement); |
| | }); |
| | |
| | |
| | viewPromptTags.innerHTML = ''; |
| | prompt.tags.forEach(tag => { |
| | const tagElement = document.createElement('span'); |
| | tagElement.className = 'px-3 py-1 bg-pastel-mint rounded-full text-sm'; |
| | tagElement.textContent = tag; |
| | viewPromptTags.appendChild(tagElement); |
| | }); |
| | |
| | |
| | viewPromptRating.innerHTML = ''; |
| | for (let i = 1; i <= 5; i++) { |
| | const star = document.createElement('i'); |
| | star.className = i <= prompt.rating ? 'fas fa-star text-pastel-yellow' : 'far fa-star text-pastel-yellow'; |
| | viewPromptRating.appendChild(star); |
| | } |
| | |
| | |
| | viewPromptDate.textContent = formatDate(prompt.createdAt); |
| | |
| | viewPromptModal.classList.remove('hidden'); |
| | } |
| | } |
| | |
| | |
| | function filterPrompts() { |
| | let filteredPrompts = db.getAllPrompts(); |
| | |
| | |
| | if (currentCategory !== 'all') { |
| | filteredPrompts = filteredPrompts.filter(prompt => prompt.category === currentCategory); |
| | } |
| | |
| | |
| | if (currentTag) { |
| | filteredPrompts = filteredPrompts.filter(prompt => prompt.tags.includes(currentTag)); |
| | } |
| | |
| | |
| | if (currentAI) { |
| | filteredPrompts = filteredPrompts.filter(prompt => prompt.ai.includes(currentAI)); |
| | } |
| | |
| | return filteredPrompts; |
| | } |
| | |
| | |
| | function sortPrompts(promptsToSort) { |
| | const sortedPrompts = [...promptsToSort]; |
| | |
| | switch(currentSort) { |
| | case 'date-desc': |
| | sortedPrompts.sort((a, b) => b.createdAt - a.createdAt); |
| | break; |
| | case 'rating-desc': |
| | sortedPrompts.sort((a, b) => b.rating - a.rating); |
| | break; |
| | default: |
| | sortedPrompts.sort((a, b) => b.createdAt - a.createdAt); |
| | } |
| | |
| | return sortedPrompts; |
| | } |
| | |
| | |
| | function renderPrompts() { |
| | let filteredPrompts = filterPrompts(); |
| | filteredPrompts = sortPrompts(filteredPrompts); |
| | |
| | if (currentView === 'list') { |
| | renderListView(filteredPrompts); |
| | } else { |
| | renderGridView(filteredPrompts); |
| | } |
| | } |
| | |
| | |
| | function renderListView(promptsToRender) { |
| | promptsContainer.innerHTML = ''; |
| | promptsContainer.classList.remove('grid-cols-1', 'md:grid-cols-2', 'lg:grid-cols-3'); |
| | promptsContainer.classList.add('space-y-4'); |
| | |
| | promptsToRender.forEach(prompt => { |
| | const promptElement = document.createElement('div'); |
| | promptElement.className = 'bg-white rounded-2xl p-6 shadow-md transition-all prompt-card hover:shadow-lg fade-in cursor-pointer'; |
| | promptElement.dataset.id = prompt.id; |
| | promptElement.innerHTML = ` |
| | <div class="flex justify-between items-start mb-4"> |
| | <h3 class="text-xl font-semibold text-gray-800">${prompt.title}</h3> |
| | <div class="flex items-center"> |
| | <button class="copy-btn mr-4 p-2 bg-pastel-green text-white rounded-full hover:bg-pastel-mint transition-all" data-prompt-id="${prompt.id}"> |
| | <i class="fas fa-copy"></i> |
| | </button> |
| | <div class="star-rating" data-prompt-id="${prompt.id}"> |
| | ${renderRatingStars(prompt.rating)} |
| | </div> |
| | </div> |
| | </div> |
| | <p class="text-gray-600 mb-4">${prompt.content}</p> |
| | <div class="flex flex-wrap gap-2 mb-4"> |
| | ${prompt.ai.map(ai => ` |
| | <span class="px-3 py-1 bg-pastel-blue rounded-full text-xs">${db.getAIName(ai)}</span> |
| | `).join('')} |
| | </div> |
| | <div class="flex justify-between items-center"> |
| | <div class="flex flex-wrap gap-2"> |
| | ${prompt.tags.map(tag => ` |
| | <span class="px-3 py-1 bg-pastel-mint rounded-full text-xs">${tag}</span> |
| | `).join('')} |
| | </div> |
| | <span class="text-xs text-gray-500">${formatDate(prompt.createdAt)}</span> |
| | </div> |
| | `; |
| | promptsContainer.appendChild(promptElement); |
| | |
| | |
| | promptElement.addEventListener('click', () => { |
| | viewPrompt(prompt.id); |
| | }); |
| | |
| | |
| | const copyBtn = promptElement.querySelector('.copy-btn'); |
| | copyBtn.addEventListener('click', (e) => { |
| | e.stopPropagation(); |
| | navigator.clipboard.writeText(prompt.content) |
| | .then(() => { |
| | |
| | const icon = copyBtn.querySelector('i'); |
| | icon.classList.remove('fa-copy'); |
| | icon.classList.add('fa-check'); |
| | setTimeout(() => { |
| | icon.classList.remove('fa-check'); |
| | icon.classList.add('fa-copy'); |
| | }, 2000); |
| | }) |
| | .catch(err => { |
| | console.error('Failed to copy text: ', err); |
| | }); |
| | }); |
| | }); |
| | |
| | |
| | setupRatingStars(); |
| | } |
| | |
| | |
| | function renderGridView(promptsToRender) { |
| | promptsContainer.innerHTML = ''; |
| | promptsContainer.classList.remove('space-y-4'); |
| | promptsContainer.classList.add('grid-cols-1', 'md:grid-cols-2', 'lg:grid-cols-3'); |
| | |
| | promptsToRender.forEach(prompt => { |
| | const promptElement = document.createElement('div'); |
| | promptElement.className = 'bg-white rounded-2xl p-6 shadow-md transition-all prompt-card hover:shadow-lg fade-in cursor-pointer'; |
| | promptElement.dataset.id = prompt.id; |
| | promptElement.innerHTML = ` |
| | <div class="flex justify-between items-start mb-4"> |
| | <h3 class="text-lg font-semibold text-gray-800">${prompt.title}</h3> |
| | <div class="flex items-center"> |
| | <button class="copy-btn mr-2 p-2 bg-pastel-green text-white rounded-full hover:bg-pastel-mint transition-all" data-prompt-id="${prompt.id}"> |
| | <i class="fas fa-copy text-sm"></i> |
| | </button> |
| | <div class="star-rating" data-prompt-id="${prompt.id}"> |
| | ${renderRatingStars(prompt.rating, true)} |
| | </div> |
| | </div> |
| | </div> |
| | <p class="text-gray-600 mb-4 text-sm line-clamp-3">${prompt.content}</p> |
| | <div class="flex flex-wrap gap-2 mb-4"> |
| | ${prompt.ai.map(ai => ` |
| | <span class="px-2 py-1 bg-pastel-blue rounded-full text-xs">${db.getAIName(ai)}</span> |
| | `).join('')} |
| | </div> |
| | <div class="flex justify-between items-center"> |
| | <div class="flex flex-wrap gap-2"> |
| | ${prompt.tags.map(tag => ` |
| | <span class="px-2 py-1 bg-pastel-mint rounded-full text-xs">${tag}</span> |
| | `).join('')} |
| | </div> |
| | <span class="text-xs text-gray-500">${formatDate(prompt.createdAt)}</span> |
| | </div> |
| | `; |
| | promptsContainer.appendChild(promptElement); |
| | |
| | |
| | promptElement.addEventListener('click', () => { |
| | viewPrompt(prompt.id); |
| | }); |
| | |
| | |
| | const copyBtn = promptElement.querySelector('.copy-btn'); |
| | copyBtn.addEventListener('click', (e) => { |
| | e.stopPropagation(); |
| | navigator.clipboard.writeText(prompt.content) |
| | .then(() => { |
| | |
| | const icon = copyBtn.querySelector('i'); |
| | icon.classList.remove('fa-copy'); |
| | icon.classList.add('fa-check'); |
| | setTimeout(() => { |
| | icon.classList.remove('fa-check'); |
| | icon.classList.add('fa-copy'); |
| | }, 2000); |
| | }) |
| | .catch(err => { |
| | console.error('Failed to copy text: ', err); |
| | }); |
| | }); |
| | }); |
| | |
| | |
| | setupRatingStars(); |
| | } |
| | |
| | |
| | function setupRatingStars(container = null) { |
| | const containers = container ? [container] : document.querySelectorAll('.star-rating'); |
| | |
| | containers.forEach(ratingContainer => { |
| | const promptId = parseInt(ratingContainer.dataset.promptId); |
| | const stars = ratingContainer.querySelectorAll('.star'); |
| | |
| | stars.forEach((star, index) => { |
| | star.addEventListener('click', (e) => { |
| | e.stopPropagation(); |
| | |
| | const prompt = db.getPromptById(promptId); |
| | if (prompt) { |
| | prompt.rating = index + 1; |
| | db.updatePrompt(promptId, { rating: prompt.rating }); |
| | renderPrompts(); |
| | } |
| | }); |
| | }); |
| | }); |
| | } |
| | |
| | |
| | function setRatingStars(container, rating) { |
| | container.innerHTML = ''; |
| | for (let i = 1; i <= 5; i++) { |
| | const star = document.createElement('i'); |
| | star.className = i <= rating ? 'fas fa-star star text-pastel-yellow' : 'far fa-star star text-pastel-yellow'; |
| | star.dataset.rating = i; |
| | container.appendChild(star); |
| | } |
| | setupRatingStars(container); |
| | |
| | |
| | if (container.id === 'editRating') { |
| | promptRatingInput.value = rating; |
| | } |
| | } |
| | |
| | |
| | function resetRatingStars(container) { |
| | container.innerHTML = ''; |
| | for (let i = 1; i <= 5; i++) { |
| | const star = document.createElement('i'); |
| | star.className = 'far fa-star star text-pastel-yellow'; |
| | star.dataset.rating = i; |
| | container.appendChild(star); |
| | } |
| | setupRatingStars(container); |
| | |
| | |
| | if (container.id === 'editRating') { |
| | promptRatingInput.value = "0"; |
| | } |
| | } |
| | |
| | |
| | function renderRatingStars(rating, small = false) { |
| | let stars = ''; |
| | for (let i = 1; i <= 5; i++) { |
| | if (i <= rating) { |
| | stars += `<i class="fas fa-star star ${small ? 'text-sm' : ''} text-pastel-yellow" data-rating="${i}"></i>`; |
| | } else { |
| | stars += `<i class="far fa-star star ${small ? 'text-sm' : ''} text-pastel-yellow" data-rating="${i}"></i>`; |
| | } |
| | } |
| | return stars; |
| | } |
| | |
| | |
| | function renderAIDropdown() { |
| | const searchTerm = aiSearch.value.toLowerCase(); |
| | aiDropdown.innerHTML = ''; |
| | |
| | |
| | const filteredAIs = db.availableAIs.filter(ai => |
| | ai.name.toLowerCase().includes(searchTerm) || |
| | ai.id.toLowerCase().includes(searchTerm) |
| | ); |
| | |
| | |
| | filteredAIs.forEach(ai => { |
| | if (!selectedAIs.includes(ai.id)) { |
| | const aiElement = document.createElement('div'); |
| | aiElement.className = 'px-4 py-2 hover:bg-gray-100 cursor-pointer'; |
| | aiElement.textContent = ai.name; |
| | aiElement.dataset.aiId = ai.id; |
| | aiElement.addEventListener('click', () => { |
| | if (!selectedAIs.includes(ai.id)) { |
| | selectedAIs.push(ai.id); |
| | renderSelectedAIs(); |
| | renderAIDropdown(); |
| | } |
| | }); |
| | aiDropdown.appendChild(aiElement); |
| | } |
| | }); |
| | |
| | |
| | if (searchTerm && !filteredAIs.some(ai => |
| | ai.name.toLowerCase() === searchTerm || |
| | ai.id.toLowerCase() === searchTerm |
| | )) { |
| | addNewAI.classList.remove('hidden'); |
| | addNewAI.textContent = `Ajouter "${searchTerm}" comme nouvelle IA`; |
| | addNewAI.onclick = () => { |
| | const newAI = { |
| | id: searchTerm.toLowerCase().replace(/\s+/g, '-'), |
| | name: searchTerm |
| | }; |
| | db.addAI(newAI); |
| | selectedAIs.push(newAI.id); |
| | renderSelectedAIs(); |
| | aiSearch.value = ''; |
| | renderAIDropdown(); |
| | addNewAI.classList.add('hidden'); |
| | }; |
| | } else { |
| | addNewAI.classList.add('hidden'); |
| | } |
| | } |
| | |
| | |
| | function renderSelectedAIs() { |
| | selectedAIList.innerHTML = ''; |
| | |
| | selectedAIs.forEach(aiId => { |
| | const ai = db.availableAIs.find(a => a.id === aiId) || { id: aiId, name: aiId }; |
| | const aiElement = document.createElement('span'); |
| | aiElement.className = 'inline-flex items-center px-3 py-1 bg-pastel-blue rounded-full text-sm mb-2'; |
| | aiElement.innerHTML = ` |
| | ${ai.name} |
| | <button class="ml-2 text-xs" data-ai-id="${ai.id}"> |
| | <i class="fas fa-times"></i> |
| | </button> |
| | `; |
| | selectedAIList.appendChild(aiElement); |
| | |
| | |
| | const removeBtn = aiElement.querySelector('button'); |
| | removeBtn.addEventListener('click', (e) => { |
| | e.stopPropagation(); |
| | selectedAIs = selectedAIs.filter(id => id !== ai.id); |
| | renderSelectedAIs(); |
| | renderAIDropdown(); |
| | }); |
| | }); |
| | } |
| | |
| | |
| | function getCategoryName(categoryCode) { |
| | const categories = { |
| | 'creative': 'Créatif', |
| | 'technical': 'Technique', |
| | 'business': 'Business' |
| | }; |
| | return categories[categoryCode] || categoryCode; |
| | } |
| | |
| | |
| | function formatDate(date) { |
| | return date.toLocaleDateString('fr-FR', { |
| | day: '2-digit', |
| | month: '2-digit', |
| | year: 'numeric', |
| | hour: '2-digit', |
| | minute: '2-digit' |
| | }); |
| | } |
| | |
| | |
| | document.addEventListener('DOMContentLoaded', init); |
| | </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=Diquels/memory" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| | </html> |