1// ===== CONSTANTS & STATE ===== 2const chatInput = document.getElementById('chatInput'); 3const sendButton = document.getElementById('sendButton'); 4const chatMessages = document.getElementById('chatMessages'); 5const typingIndicator = document.getElementById('typingIndicator'); 6const STORAGE_KEY = 'inno_chat_history'; 7let isTyping = false; 8 9// ===== GOOGLE SHEETS INTEGRATION ===== 10// Replace this URL with your Google Apps Script Web App URL 11const GOOGLE_SCRIPT_URL = 'https://script.google.com/macros/s/AKfycbyHLfUJDcTvCIEhtoDQ5CKaccfmrFIbnhkmUS36zKe9JF342UhrC7i79URszooolG4F/exec'; 12 13// Generate a unique session ID for this browser session 14let sessionId = sessionStorage.getItem('inno_session_id'); 15if (!sessionId) { 16 sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); 17 sessionStorage.setItem('inno_session_id', sessionId); 18} 19 20// Function to send data to Google Sheets 21async function sendToGoogleSheets(userMessage, aiResponse, category) { 22 // Skip if URL not configured 23 if (GOOGLE_SCRIPT_URL === 'YOUR_GOOGLE_APPS_SCRIPT_URL_HERE') { 24 console.log('Google Sheets integration not configured'); 25 return; 26 } 27 28 try { 29 const data = { 30 sessionId: sessionId, 31 userMessage: userMessage, 32 aiResponse: aiResponse, 33 category: category, 34 userAgent: navigator.userAgent, 35 timestamp: new Date().toISOString() 36 }; 37 38 await fetch(GOOGLE_SCRIPT_URL, { 39 method: 'POST', 40 mode: 'no-cors', // Required for Google Apps Script 41 headers: { 42 'Content-Type': 'application/json', 43 }, 44 body: JSON.stringify(data) 45 }); 46 47 console.log('Data sent to Google Sheets successfully'); 48 } catch (error) { 49 console.error('Error sending to Google Sheets:', error); 50 } 51} 52 53// Function to determine message category 54function getCategoryFromMessage(message) { 55 const msg = message.toLowerCase(); 56 if (msg.includes('ai') || msg.includes('chatbot') || msg.includes('machine learning') || msg.includes('ml')) { 57 return 'ai'; 58 } 59 if (msg.includes('cloud') || msg.includes('aws') || msg.includes('azure') || msg.includes('hosting')) { 60 return 'cloud'; 61 } 62 if (msg.includes('web') || msg.includes('website') || msg.includes('app') || msg.includes('develop')) { 63 return 'web'; 64 } 65 if (msg.includes('price') || msg.includes('cost') || msg.includes('quote') || msg.includes('budget')) { 66 return 'pricing'; 67 } 68 if (msg.includes('time') || msg.includes('timeline') || msg.includes('how long') || msg.includes('when')) { 69 return 'timeline'; 70 } 71 if (msg.includes('contact') || msg.includes('human') || msg.includes('talk') || msg.includes('call') || msg.includes('email')) { 72 return 'contact'; 73 } 74 return 'general'; 75} 76 77// ===== AI RESPONSE DATABASE (CLEAN VERSION) ===== 78const responses = { 79 ai: [ 80 "We help companies design and implement practical AI solutions â from chatbots and workflow automation to custom ML integrations. Most projects start with a short discovery phase so we can scope the right approach. What problem are you trying to solve with AI?", 81 "Our AI work focuses on real business impact, not hype. We build GPT-powered assistants, integrate AI APIs, and automate internal processes. Would you like to explore what makes sense for your use case?", 82 "AI works best when it's aligned with your existing systems. We can help you evaluate feasibility, costs, and ROI before building anything. What systems are you currently using?" 83 ], 84 85 cloud: [ 86 "Cloud architecture and DevOps are core to what we do. We help teams migrate, optimize, and scale on AWS, Azure, or Google Cloud. Are you planning a migration or improving an existing setup?", 87 "We design secure, scalable cloud infrastructure using containers, CI/CD, and automation. Let me know what challenges you're facing â performance, cost, reliability, or something else?",
88 "If you're looking to modernize your infrastructure, we usually start with a technical assessment and clear roadmap. Want to discuss your current environment?" 89 ], 90 91 web: [ 92 "We build modern, high-performance websites and web applications with a strong focus on UX and scalability. Is this a marketing site, internal tool, or customer-facing app?", 93 "Our web projects typically use modern stacks like React, Next.js, and APIs tailored to your business needs. What key features or integrations are important for you?", 94 "A successful web project starts with clear goals and user flows. If you want, I can help you outline the scope before involving our team." 95 ], 96 97 pricing: [ 98 "Pricing depends on scope, complexity, and timeline. As a reference:\n\n⢠AI & automation: typically starting from a few thousand EUR\n⢠Cloud & DevOps projects: mid to large engagements\n⢠Web projects: depends on features and integrations\n\nWe're happy to provide a clear estimate after a short discussion.", 99 "We offer fixed-price projects, retainers, and consulting â depending on what fits your needs best. Would you like to talk through options?" 100 ], 101 102 timeline: [ 103 "Timelines vary by project, but most engagements follow this pattern:\n\n⢠Discovery & planning: 1â2 weeks\n⢠Implementation: 4â10 weeks\n⢠Iteration & launch: ongoing\n\nDo you have a target launch date in mind?", 104 "We can usually start within 1â2 weeks, depending on availability and scope. Let me know how urgent your project is." 105 ], 106 107 contact: [ 108 "Happy to connect you with our team:\n\nð§ <a href='mailto:[email protected]' style='color:#FF6B35;text-decoration:underline;'>[email protected]</a>\nð <a href='tel:+40768144426' style='color:#FF6B35;text-decoration:underline;'>+40 (768) 144 426</a>\nð <a href='https://calendly.com/octavian-inno-solutions' target='_blank' style='color:#FF6B35;text-decoration:underline;'>Book a call via Calendly</a>\n\nWhat's the easiest way to reach you?", 109 ], 110 111 default: [ 112 "That sounds interesting. Could you share a bit more context so I can point you in the right direction?", 113 "I can help clarify options, timelines, or next steps â what's your main goal right now?", 114 "Thanks for reaching out! What outcome would make this project a success for you?", 115 "Would you prefer a quick written answer here, or a short call with our team?" 116 ] 117}; 118 119 120// ===== UTILITY FUNCTIONS ===== 121function scrollToBottom() { 122 chatMessages.scrollTop = chatMessages.scrollHeight; 123} 124 125function scrollToChat() { 126 document.getElementById('chatbot').scrollIntoView({ behavior: 'smooth' }); 127 setTimeout(() => chatInput.focus(), 500); 128} 129 130function escapeHtml(text) { 131 const div = document.createElement('div'); 132 div.textContent = text; 133 return div.innerHTML; 134} 135 136function selectPrompt(element) { 137 chatInput.value = element.textContent; 138 chatInput.focus(); 139 sendMessage(); 140} 141 142// ===== MESSAGE HANDLING ===== 143function addMessage(content, isUser = false) { 144 const messageDiv = document.createElement('div'); 145 messageDiv.className = `message ${isUser ? 'user' : 'ai'}`; 146 147 if (isUser) { 148 messageDiv.innerHTML = ` 149 <div class="message-content">${escapeHtml(content)}</div> 150 <div class="message-avatar">U</div> 151 `; 152 } else { 153 messageDiv.innerHTML = ` 154 <div class="message-avatar">AI</div> 155 <div class="message-content">${content}</div> 156 `; 157 } 158 159 // Insert before typing indicator 160 const typingParent = typingIndicator.closest('.message'); 161 chatMessages.insertBefore(messageDiv, typingParent); 162 scrollToBottom(); 163 164 saveChatHistory(); 165} 166 167function showTyping() { 168 isTyping = true; 169 typingIndicator.classList.add('active'); 170 scrollToBottom(); 171} 172 173function hideTyping() { 174 isTyping = false; 175 typingIndicator.classList.remove('active'); 176} 177 178// ===== AI RESPONSE LOGIC ===== 179function getAIResponse(userMessage) {
180 const msg = userMessage.toLowerCase(); 181 182 // Keyword matching 183 if (msg.includes('ai') || msg.includes('chatbot') || msg.includes('machine learning') || msg.includes('ml')) { 184 return responses.ai[Math.floor(Math.random() * responses.ai.length)]; 185 } 186 if (msg.includes('cloud') || msg.includes('aws') || msg.includes('azure') || msg.includes('hosting')) { 187 return responses.cloud[Math.floor(Math.random() * responses.cloud.length)]; 188 } 189 if (msg.includes('web') || msg.includes('website') || msg.includes('app') || msg.includes('develop')) { 190 return responses.web[Math.floor(Math.random() * responses.web.length)]; 191 } 192 if (msg.includes('price') || msg.includes('cost') || msg.includes('quote') || msg.includes('budget')) { 193 return responses.pricing[Math.floor(Math.random() * responses.pricing.length)]; 194 } 195 if (msg.includes('time') || msg.includes('timeline') || msg.includes('how long') || msg.includes('when')) { 196 return responses.timeline[Math.floor(Math.random() * responses.timeline.length)]; 197 } 198 if (msg.includes('contact') || msg.includes('human') || msg.includes('talk') || msg.includes('call') || msg.includes('email')) { 199 return responses.contact[Math.floor(Math.random() * responses.contact.length)]; 200 } 201 202 return responses.default[Math.floor(Math.random() * responses.default.length)]; 203} 204 205function sendMessage() { 206 const message = chatInput.value.trim(); 207 if (!message || isTyping) return; 208 209 // Add user message 210 addMessage(message, true); 211 chatInput.value = ''; 212 213 // Determine category 214 const category = getCategoryFromMessage(message); 215 216 // Show typing indicator 217 showTyping(); 218 219 // Simulate AI response delay 220 const delay = 1000 + Math.random() * 1000; // 1-2 seconds 221 setTimeout(() => { 222 hideTyping(); 223 const response = getAIResponse(message); 224 addMessage(response); 225 226 // Send to Google Sheets 227 sendToGoogleSheets(message, response, category); 228 }, delay); 229} 230 231// ===== LOCAL STORAGE ===== 232function saveChatHistory() { 233 const messages = []; 234 document.querySelectorAll('.message:not(:last-child)').forEach(msg => { 235 const isUser = msg.classList.contains('user'); 236 const content = msg.querySelector('.message-content').innerHTML; 237 messages.push({ isUser, content }); 238 }); 239 localStorage.setItem(STORAGE_KEY, JSON.stringify(messages)); 240} 241 242function loadChatHistory() { 243 const history = localStorage.getItem(STORAGE_KEY); 244 if (history) { 245 const messages = JSON.parse(history); 246 // Keep only the initial greeting, clear others 247 const initialMessages = document.querySelectorAll('.message.ai'); 248 // Load saved messages (uncomment to enable persistence) 249 // messages.forEach(msg => addMessage(msg.content, msg.isUser)); 250 } 251} 252 253function clearChatHistory() { 254 localStorage.removeItem(STORAGE_KEY); 255} 256 257// ===== EVENT LISTENERS ===== 258sendButton.addEventListener('click', sendMessage); 259 260chatInput.addEventListener('keypress', (e) => { 261 if (e.key === 'Enter' && !e.shiftKey) { 262 e.preventDefault(); 263 sendMessage(); 264 } 265}); 266 267chatInput.addEventListener('input', () => { 268 sendButton.disabled = !chatInput.value.trim(); 269}); 270 271// ===== INITIALIZATION ===== 272document.addEventListener('DOMContentLoaded', () => { 273 loadChatHistory(); 274 chatInput.focus(); 275 sendButton.disabled = true; 276 console.log('Session ID:', sessionId); 277}); 278 279// ===== API INTEGRATION PLACEHOLDER ===== 280// Uncomment and configure when backend is ready: 281/* 282async function sendToBackend(message) { 283 try { 284 const response = await fetch('/api/chat', { 285 method: 'POST', 286 headers: { 287 'Content-Type': 'application/json', 288 }, 289 body: JSON.stringify({ message }) 290 }); 291 const data = await response.json(); 292 return data.response; 293 } catch (error) { 294 console.error('API Error:', error); 295 return 'Sorry, I encountered an error. Please try again or contact us directly.'; 296 } 297} 298*/
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.