PageSourceSearch

https://inno-solutions.ro/script.js

js inno-solutions.ro collected 2026-10-07 09:40:06 UTC 12,275 bytes, 298 lines download raw bytes

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.