import { GoogleGenAI, Chat, GenerateContentResponse } from "@google/genai"; declare global { interface Window { AMP: any; } } class Chatbot { private ai: GoogleGenAI; private chat: Chat | null = null; private elements: { container: HTMLElement | null; messagesContainer: HTMLElement | null; input: HTMLTextAreaElement | null; sendBtn: HTMLButtonElement | null; typingIndicator: HTMLElement | null; }; private systemInstruction: string = ` You are an expert virtual sales assistant named AbidjanBot, integrated into a high-traffic website. Your primary role is to proactively engage with visitors, understand their specific needs regarding our products/services, and qualify them. Your ultimate goal is to convert their interest into a concrete action: scheduling a phone call or requesting a quote/invoice. Conversation Flow: 1. Greeting & Proactive Engagement: Start with a warm, professional, and slightly proactive greeting. Example: "Bonjour et bienvenue sur Abidjan Solution ! Je suis AbidjanBot, votre assistant. Cherchez-vous quelque chose de spécifique aujourd'hui ou puis-je vous présenter nos produits phares ?" 2. Needs Discovery: Use open-ended questions to understand the user's requirements. Examples: "Pourriez-vous m'en dire plus sur le type de machine que vous recherchez ?", "Quel est le secteur d'activité de votre entreprise ?", "Avez-vous un projet spécifique en tête ?" 3. Product/Service Matching: Based on their needs, recommend specific products or services from our catalog. Be precise and helpful using the categories below. 4. Qualification & Call to Action: Once interest is confirmed, pivot towards the conversion goal. Be direct but courteous. - For a Call: "Cela semble être un projet intéressant. Un de nos experts pourrait vous appeler pour discuter des détails techniques et vous fournir un conseil personnalisé. Seriez-vous disponible pour un bref appel ?" If yes, ask for their phone number. - For a Quote: "Je peux vous préparer un devis détaillé pour cet équipement. Pour cela, j'aurai besoin de votre nom complet, du nom de votre entreprise et de votre adresse e-mail. Souhaitez-vous que je lance la procédure ?" 5. Handling Objections/Questions: Answer any questions concisely and accurately. If you don't know the answer, state: "C'est une excellente question. Pour vous donner une information précise, il serait préférable qu'un de nos spécialistes vous contacte directement." Our Product Categories: Quand un utilisateur vous interroge sur nos produits, utilisez cette liste pour le guider. Soyez prêt à discuter des articles dans ces catégories. - Fabrication Locale : Ceci inclut des machines fabriquées localement comme la 'Broyeuse de Manioc' et la 'Presse à Huile'. Elles sont destinées à la transformation alimentaire. - Appareils Électroménagers (Neuf) : Des appareils ménagers et industriels neufs. Cela comprend des machines à coudre industrielles et des moteurs diesel. - Matériel Lourd (Location/Vente Occasion) : Nous proposons du matériel lourd d'occasion à la location ou à la vente. Par exemple, des tracteurs agricoles (John Deere), des pelleteuses (CAT), des groupes électrogènes diesel et des camions-bennes (Renault Kerax). - Packs Industriels : Des solutions industrielles complètes pour la mise en place d'unités de production, comme pour l'Attiéké ou l'Huile de Palme. - Services Numériques & Virtuelles : Nous fournissons des services numériques, y compris l'échange de cryptomonnaies (Bitcoin, USDT) et la création de sites e-commerce. Essayez toujours de faire correspondre les besoins de l'utilisateur à l'une de ces catégories et donnez des exemples précis si possible. Key Instructions: - Language: Always communicate in clear, professional, and friendly French (fr-FR). - Conciseness: Keep your responses brief and to the point. - Goal-Oriented: Never lose sight of the primary goal: getting a phone number for a call or details for a quote. - Persona: You are helpful, expert, and efficient. - Data Collection: Only ask for contact information (phone or email) AFTER the user has agreed to be contacted or receive a quote. `; constructor(apiKey: string) { if (!apiKey) { throw new Error("API key is missing. Please ensure the API_KEY environment variable is set."); } this.ai = new GoogleGenAI({ apiKey }); this.elements = { container: null, messagesContainer: null, input: null, sendBtn: null, typingIndicator: null, }; this.initializeChatbot(); } private async initializeChatbot() { try { this.chat = this.ai.chats.create({ model: 'gemini-2.5-flash', config: { systemInstruction: this.systemInstruction, }, }); this.buildUI(); this.addEventListeners(); this.addMessage("Bonjour et bienvenue ! Je suis AbidjanBot. Comment puis-je vous aider à trouver le produit ou service parfait pour votre projet aujourd'hui ?", 'bot'); } catch (error) { console.error("Chat initialization failed:", error); this.displayError("L'assistant IA n'a pas pu démarrer. Veuillez vérifier la configuration."); } } private buildUI() { const root = document.getElementById('chatbot-root'); if (!root) return; root.innerHTML = `
💬
AbidjanBot En ligne
`; this.elements.container = root.querySelector('.chatbot-container'); this.elements.messagesContainer = root.querySelector('.chatbot-messages'); this.elements.input = root.querySelector('textarea'); this.elements.sendBtn = root.querySelector('button'); // Add typing indicator element const typingIndicator = document.createElement('div'); typingIndicator.className = 'message bot typing-indicator'; typingIndicator.innerHTML = ''; this.elements.typingIndicator = typingIndicator; } private addEventListeners() { this.elements.sendBtn?.addEventListener('click', () => this.sendMessage()); this.elements.input?.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.sendMessage(); } }); this.elements.input?.addEventListener('input', this.autoResizeInput); } private autoResizeInput = () => { if (this.elements.input) { this.elements.input.style.height = 'auto'; this.elements.input.style.height = `${this.elements.input.scrollHeight}px`; } } private addMessage(text: string, sender: 'user' | 'bot') { const messageDiv = document.createElement('div'); messageDiv.className = `message ${sender}`; messageDiv.textContent = text; this.elements.messagesContainer?.appendChild(messageDiv); this.scrollToBottom(); } private displayError(message: string) { const root = document.getElementById('chatbot-root'); if (root) { root.innerHTML = `
${message}
`; } } private showTyping(show: boolean) { if (show && this.elements.typingIndicator) { this.elements.messagesContainer?.appendChild(this.elements.typingIndicator); this.scrollToBottom(); } else { this.elements.typingIndicator?.remove(); } } private scrollToBottom() { this.elements.messagesContainer?.scrollTo({ top: this.elements.messagesContainer.scrollHeight, behavior: 'smooth' }); } private async sendMessage() { const userInput = this.elements.input?.value.trim(); if (!userInput || !this.chat) return; this.addMessage(userInput, 'user'); this.elements.input!.value = ''; this.autoResizeInput(); this.showTyping(true); try { const response: GenerateContentResponse = await this.chat.sendMessage({ message: userInput }); this.showTyping(false); this.addMessage(response.text, 'bot'); } catch (error) { console.error("Gemini API call failed:", error); this.showTyping(false); this.addMessage("Désolé, une erreur s'est produite. Veuillez réessayer.", 'bot'); } } } // --- Main Execution --- // This code runs only inside the chatbot.html iframe. if (window.location.pathname.includes('chatbot.html')) { try { // The API key MUST be provided as an environment variable `process.env.API_KEY`. // This is a secure way to manage credentials and prevents exposing them in the source code. new Chatbot(process.env.API_KEY!); } catch (e: any) { console.error(e.message); const root = document.getElementById('chatbot-root'); if (root) { root.innerHTML = `
Could not initialize chatbot: ${e.message}
`; } } } // --- Payment Page Logic --- if (window.location.pathname.includes('payment.html')) { document.addEventListener('DOMContentLoaded', () => { const urlParams = new URLSearchParams(window.location.search); const productName = urlParams.get('name'); const productPrice = urlParams.get('price'); const imageUrl = urlParams.get('imageUrl'); const imageEl = document.getElementById('product-image'); const nameEl = document.getElementById('product-name'); const priceEl = document.getElementById('product-price'); const totalEl = document.getElementById('total-price'); if (nameEl) nameEl.textContent = productName || 'Produit non spécifié'; if (priceEl) priceEl.textContent = productPrice || 'Prix non disponible'; if (totalEl) totalEl.textContent = productPrice || 'N/A'; if (imageEl && imageUrl) { const ampImg = document.createElement('amp-img'); ampImg.setAttribute('src', imageUrl); ampImg.setAttribute('width', '80'); ampImg.setAttribute('height', '80'); ampImg.setAttribute('layout', 'responsive'); ampImg.setAttribute('alt', productName || 'Image du produit'); imageEl.appendChild(ampImg); } else if (imageEl) { const placeholder = document.createElement('div'); placeholder.textContent = 'Image non disponible'; placeholder.style.cssText = 'width: 80px; height: 80px; background: #eee; text-align: center; line-height: 80px; font-size: 12px; color: #888; border-radius: 5px;'; imageEl.appendChild(placeholder); } const form = document.getElementById('checkout-form'); const formContainer = document.getElementById('payment-form-container'); const successMessage = document.getElementById('order-success-message'); form?.addEventListener('submit', (e) => { e.preventDefault(); // Simple validation const nameInput = document.getElementById('fullname') as HTMLInputElement; const phoneInput = document.getElementById('phone') as HTMLInputElement; if (!nameInput || !phoneInput) return; if (nameInput.value.trim() === '' || phoneInput.value.trim() === '') { alert('Veuillez remplir votre nom complet et votre numéro de téléphone.'); return; } if(formContainer && successMessage) { formContainer.style.display = 'none'; successMessage.removeAttribute('hidden'); window.scrollTo(0, 0); } }); }); }