jaysx / index.html
jjmandog's picture
You might need to recode . Settings button doesn’t work and neither do some of the other buttons. Improve the test ai calling by having it call a number I give to it and have it learn. Also improve the training code and add reasoning. It’s too simple. Separate training into two buttons one for text tracing and one for voice answering training. - Follow Up Deployment
fe3fc65 verified
raw
history blame
80.1 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Phone Assistant</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>
// Business settings data
let businessSettings = {
maxRingTime: 30,
smsResponseTime: 15,
responseLanguage: "en",
personalGreeting: "",
businessHours: {
open: "08:00",
close: "18:00"
},
responses: {
missedCall: {
en: "Hello, this is Jay's Mobile Wash. We missed your call. Please leave a message and we'll return your call as soon as possible.",
es: "Hola, es el lavado móvil de Jay. Perdimos su llamada. Por favor deje un mensaje y le devolveremos la llamada lo antes posible."
},
afterHours: {
en: "Thank you for contacting Jay's Mobile Wash. Our business hours are [hours]. We will respond when we reopen.",
es: "Gracias por contactar el lavado móvil de Jay. Nuestro horario es de [hours]. Responderemos cuando volvamos a abrir."
}
}
};
function loadSettings() {
const saved = localStorage.getItem('JMW_Settings');
if (saved) {
businessSettings = JSON.parse(saved);
// Set default values if they don't exist
if (!businessSettings.maxRingTime) businessSettings.maxRingTime = 30;
if (!businessSettings.smsResponseTime) businessSettings.smsResponseTime = 15;
document.getElementById('maxRingTime').value = businessSettings.maxRingTime;
document.getElementById('smsResponseTime').value = businessSettings.smsResponseTime;
document.getElementById('openTime').value = businessSettings.businessHours.open;
document.getElementById('closeTime').value = businessSettings.businessHours.close;
document.getElementById('responseLanguage').value = businessSettings.responseLanguage;
document.getElementById('personalGreeting').value = businessSettings.personalGreeting;
document.getElementById('missedCallResponse').value = businessSettings.responses.missedCall.en;
document.getElementById('missedCallResponseEs').value = businessSettings.responses.missedCall.es;
document.getElementById('afterHoursResponse').value = businessSettings.responses.afterHours.en;
document.getElementById('afterHoursResponseEs').value = businessSettings.responses.afterHours.es;
}
}
function saveSettings() {
businessSettings = {
maxRingTime: parseInt(document.getElementById('maxRingTime').value),
smsResponseTime: parseInt(document.getElementById('smsResponseTime').value),
responseLanguage: document.getElementById('responseLanguage').value,
personalGreeting: document.getElementById('personalGreeting').value,
businessHours: {
open: document.getElementById('openTime').value,
close: document.getElementById('closeTime').value
},
responses: {
missedCall: {
en: document.getElementById('missedCallResponse').value,
es: document.getElementById('missedCallResponseEs').value
},
afterHours: {
en: document.getElementById('afterHoursResponse').value,
es: document.getElementById('afterHoursResponseEs').value
}
}
};
localStorage.setItem('JMW_Settings', JSON.stringify(businessSettings));
// Show success message
const notification = document.createElement('div');
notification.className = 'fixed top-40 left-1/2 transform -translate-x-1/2 bg-green-500 text-white px-6 py-3 rounded-xl animate-fadeIn z-50 shadow-lg';
notification.innerHTML = 'Settings saved successfully!';
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 3000);
}
// Enhanced answerCall with time-based response
function answerCall() {
const currentTime = new Date();
const openTime = new Date();
const closeTime = new Date();
const [openHours, openMins] = businessSettings.businessHours.open.split(':');
const [closeHours, closeMins] = businessSettings.businessHours.close.split(':');
openTime.setHours(openHours, openMins);
closeTime.setHours(closeHours, closeMins);
if (currentTime < openTime || currentTime > closeTime) {
const fab = document.querySelector('.fab');
const icon = fab.querySelector('i');
icon.classList.remove('fa-phone');
icon.classList.add('fa-clock');
showResponseMessage(businessSettings.responses.afterHours.replace('[hours]',
`${businessSettings.businessHours.open} - ${businessSettings.businessHours.close}`));
setTimeout(() => {
icon.classList.remove('fa-clock');
icon.classList.add('fa-phone');
}, 1000);
return;
}
// Original answerCall functionality...
}
function showResponseMessage(message) {
const responseContainer = document.querySelector('#callResponseContainer');
if (!responseContainer) {
const container = document.createElement('div');
container.id = 'callResponseContainer';
container.className = 'fixed top-16 left-1/2 transform -translate-x-1/2 bg-black bg-opacity-80 text-white px-4 py-2 rounded-lg z-50';
container.textContent = message;
document.body.appendChild(container);
setTimeout(() => container.remove(), 3000);
}
}
function exportConfiguration() {
const data = {
trainingData: trainingData,
timestamp: new Date().toISOString(),
version: "1.0",
aiProfile: "Grok-Style Learning Assistant"
};
const blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `AI-Call-Assistant-${new Date().toISOString().split('T')[0]}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
alert("Configuration exported successfully!");
}
// Tailwind config
tailwind.config = {
theme: {
extend: {
colors: {
iosbg: '#f2f2f7',
iosdark: '#1c1c1e',
accent: '#0a84ff',
accent2: '#5e5ce6',
}
}
}
}
// SMS/iMessage Handling
function handleSMS(message, sender, isiMessage) {
const responseLanguage = businessSettings.responseLanguage;
const greeting = businessSettings.personalGreeting;
let response;
// Check for Spanish keywords
const spanishKeywords = ['hola', 'gracias', 'servicio', 'lavado'];
const isSpanish = spanishKeywords.some(word =>
message.toLowerCase().includes(word));
// Determine best language to respond in
const useSpanish = responseLanguage === 'es' || isSpanish;
if (message.toLowerCase().includes('appointment') ||
message.toLowerCase().includes('cita')) {
response = useSpanish ?
`Gracias por su mensaje. Nuestros horarios para citas son ${businessSettings.businessHours.open} a ${businessSettings.businessHours.close}.` :
`Thanks for your message. Our appointment hours are ${businessSettings.businessHours.open} to ${businessSettings.businessHours.close}.`;
} else if (greeting && !isiMessage) {
// Use personal greeting for SMS (not iMessage)
response = greeting;
} else {
response = useSpanish ?
"Gracias por su mensaje. ¿En qué puedo ayudarle hoy?" :
"Thanks for your message. How can I help you today?";
}
return response;
}
// Call Answering with Speech Synthesis
function answerCallWithSpeech(callerNumber) {
const synth = window.speechSynthesis;
const utterance = new SpeechSynthesisUtterance();
const greeting = businessSettings.personalGreeting ||
(businessSettings.responseLanguage === 'es' ?
"Buenos días, habla el asistente de Jay's Mobile Wash. ¿En qué puedo ayudarle?" :
"Hello, this is Jay's Mobile Wash assistant. How can I help you?");
utterance.text = greeting;
utterance.lang = businessSettings.responseLanguage === 'es' ? 'es-ES' : 'en-US';
// Speak the greeting
synth.speak(utterance);
// Set up voice recognition for caller response
if ('webkitSpeechRecognition' in window) {
const recognition = new webkitSpeechRecognition();
recognition.lang = businessSettings.responseLanguage === 'es' ? 'es-ES' : 'en-US';
recognition.interimResults = false;
recognition.onresult = function(event) {
const transcript = event.results[0][0].transcript;
// Process caller's speech and respond
processCallerSpeech(transcript, recognition);
};
recognition.start();
}
}
function processCallerSpeech(transcript, recognition) {
const synth = window.speechSynthesis;
const utterance = new SpeechSynthesisUtterance();
utterance.lang = businessSettings.responseLanguage === 'es' ? 'es-ES' : 'en-US';
// Simple response logic - would be enhanced with AI in production
if (transcript.toLowerCase().includes('appointment') ||
transcript.toLowerCase().includes('cita')) {
utterance.text = businessSettings.responseLanguage === 'es' ?
`Las citas están disponibles de ${businessSettings.businessHours.open} a ${businessSettings.businessHours.close}.` :
`Appointments are available from ${businessSettings.businessHours.open} to ${businessSettings.businessHours.close}.`;
} else {
utterance.text = businessSettings.responseLanguage === 'es' ?
"Por favor visite nuestro sitio web jaysmobilewash.com para más información. ¿Algo más en lo que pueda ayudarle?" :
"Please visit our website jaysmobilewash.com for more information. Is there anything else I can help with?";
}
synth.speak(utterance);
// Continue listening for next input
setTimeout(() => recognition.start(), 2000);
}
</script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.screen {
display: none;
}
.screen.active {
display: block;
}
/* Simple switch */
.toggle-switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #2196F3;
}
input:checked + .slider:before {
transform: translateX(26px);
}
/* Chat bubbles */
.ai-bubble {
background: #f1f1f1;
border-radius: 5px 15px 15px 5px;
padding: 10px 15px;
max-width: 85%;
margin: 5px 0;
}
.user-bubble {
background: #2196F3;
color: white;
border-radius: 15px 5px 5px 15px;
padding: 10px 15px;
max-width: 85%;
margin: 5px 0 5px auto;
}
/* Simple buttons */
.simple-btn {
background: #2196F3;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
}
/* Function buttons */
.action-btn {
background: #4CAF50;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
}
</style>
</head>
<body class="bg-iosbg dark:bg-iosdark text-gray-900 dark:text-gray-200 min-h-screen">
<!-- Status Bar -->
<div class="fixed top-0 left-0 right-0 h-12 flex items-center px-4 z-50 bg-iosbg dark:bg-iosdark">
<div class="text-left text-sm w-20">9:41</div>
<div class="flex-1 flex justify-center">
<i class="fas fa-signal mr-2"></i>
<i class="fas fa-wifi mr-2"></i>
<i class="fas fa-battery-three-quarters"></i>
</div>
<div class="w-20 text-right text-xs">100%</div>
</div>
<!-- Main App Container -->
<div class="relative pt-12 max-w-md mx-auto h-screen overflow-hidden">
<!-- Home Screen -->
<div id="homeScreen" class="screen active px-4 pt-4 h-full flex flex-col">
<div class="mt-2">
<h1 class="text-3xl font-bold">Jay's Mobile Wash</h1>
<p class="text-gray-500 dark:text-gray-400 mt-1">AI Call Assistant Dashboard</p>
</div>
<!-- Quick Action Buttons -->
<div class="mt-8 grid grid-cols-2 gap-4">
<button onclick="showScreen('repliesScreen')" class="bg-white dark:bg-gray-800 rounded-2xl p-5 flex flex-col items-center shadow-sm">
<div class="w-12 h-12 rounded-full bg-blue-100 flex items-center justify-center mb-3">
<i class="fas fa-comments text-blue-500 text-xl"></i>
</div>
<h3 class="font-medium">Response Templates</h3>
<p class="text-xs text-gray-500 mt-1 text-center">Pre-set replies for common calls</p>
</button>
<button onclick="showScreen('trainingScreen')" class="bg-white dark:bg-gray-800 rounded-2xl p-5 flex flex-col items-center shadow-sm">
<div class="w-12 h-12 rounded-full bg-purple-100 flex items-center justify-center mb-3">
<i class="fas fa-graduation-cap text-purple-500 text-xl"></i>
</div>
<h3 class="font-medium">Train AI</h3>
<p class="text-xs text-gray-500 mt-1 text-center">Improve call responses</p>
</button>
<button onclick="showScreen('settingsScreen')" class="bg-white dark:bg-gray-800 rounded-2xl p-5 flex flex-col items-center shadow-sm">
<div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center mb-3">
<i class="fas fa-cog text-green-500 text-xl"></i>
</div>
<h3 class="font-medium">Settings</h3>
<p class="text-xs text-gray-500 mt-1 text-center">Configure call handling</p>
</button>
<button onclick="answerCall(true)" class="bg-white dark:bg-gray-800 rounded-2xl p-5 flex flex-col items-center shadow-sm">
<div class="w-12 h-12 rounded-full bg-red-100 flex items-center justify-center mb-3">
<i class="fas fa-phone text-red-500 text-xl"></i>
</div>
<h3 class="font-medium">Test Call</h3>
<p class="text-xs text-gray-500 mt-1 text-center">Practice AI responses</p>
</button>
</div>
<!-- Today's Summary -->
<div class="mt-8 bg-white dark:bg-gray-800 rounded-2xl p-5">
<h2 class="font-bold text-lg mb-3">Today's Summary</h2>
<div class="grid grid-cols-3 gap-4 text-center">
<div class="bg-blue-50 dark:bg-blue-900/30 p-3 rounded-xl">
<div class="text-2xl font-bold">12</div>
<div class="text-xs">Calls</div>
</div>
<div class="bg-green-50 dark:bg-green-900/30 p-3 rounded-xl">
<div class="text-2xl font-bold">8</div>
<div class="text-xs">Answered</div>
</div>
<div class="bg-yellow-50 dark:bg-yellow-900/30 p-3 rounded-xl">
<div class="text-2xl font-bold">4</div>
<div class="text-xs">Missed</div>
</div>
</div>
</div>
<!-- Recent Activity -->
<div class="mt-4">
<div class="flex justify-between items-center mb-2">
<h3 class="font-medium">Recent Calls</h3>
<button class="text-sm text-blue-500">See All</button>
</div>
<div class="space-y-2">
<div class="flex items-center justify-between p-3 bg-white dark:bg-gray-800 rounded-xl">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center mr-3">
<i class="fas fa-check text-green-500 text-xs"></i>
</div>
<div>
<p class="text-sm font-medium">(562) 228-9429</p>
<p class="text-xs text-gray-500">AI Answered - 2:15 PM</p>
</div>
</div>
<i class="fas fa-chevron-right text-gray-400"></i>
</div>
<div class="flex items-center justify-between p-3 bg-white dark:bg-gray-800 rounded-xl">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full bg-red-100 flex items-center justify-center mr-3">
<i class="fas fa-times text-red-500 text-xs"></i>
</div>
<div>
<p class="text-sm font-medium">Unknown Caller</p>
<p class="text-xs text-gray-500">Missed - 10:45 AM</p>
</div>
</div>
<i class="fas fa-chevron-right text-gray-400"></i>
</div>
</div>
</div>
</div>
<!-- Configure Replies Screen -->
<div id="repliesScreen" class="screen h-full flex flex-col">
<div class="px-4 pt-4">
<div class="flex items-center">
<button class="p-2 rounded-full" onclick="showScreen('homeScreen')">
<i class="fas fa-arrow-left"></i>
</button>
<h2 class="text-xl font-bold ml-2">Smart Replies</h2>
</div>
<p class="text-gray-500 dark:text-gray-400 mt-1 ml-12">Customize how AI answers calls</p>
</div>
<div class="mt-4 px-4 flex-1 overflow-y-auto">
<div class="bg-white dark:bg-gray-800 rounded-2xl p-5 mb-4">
<div class="flex items-center">
<div class="w-12 h-12 rounded-full bg-gradient-to-br from-accent to-accent2 flex items-center justify-center">
<i class="fas fa-brain text-white text-xl"></i>
</div>
<div class="ml-3">
<h3 class="font-medium">AI Learning Mode</h3>
<p class="text-gray-500 dark:text-gray-400 text-sm">Improves responses over time</p>
</div>
</div>
<div class="mt-4 flex items-center justify-between">
<span>Learning from interactions</span>
<label class="ios-switch">
<input type="checkbox" checked>
<span class="ios-slider"></span>
</label>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-2xl overflow-hidden">
<div class="px-5 pt-4">
<h3 class="font-medium">Custom Response Templates</h3>
<p class="text-gray-500 dark:text-gray-400 text-sm mt-1">Set predefined responses</p>
</div>
<div class="mt-4 space-y-2">
<div class="flex items-center justify-between p-4 hover:bg-gray-100 dark:hover:bg-gray-750 cursor-pointer">
<div>
<h4 class="font-medium">Business Calls</h4>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">"Hello, this is [Your Name]'s assistant..."</p>
</div>
<i class="fas fa-chevron-right text-gray-400"></i>
</div>
<div class="flex items-center justify-between p-4 hover:bg-gray-100 dark:hover:bg-gray-750 cursor-pointer">
<div>
<h4 class="font-medium">Personal Calls</h4>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">"Hi, this is [Name]'s phone..."</p>
</div>
<i class="fas fa-chevron-right text-gray-400"></i>
</div>
<div class="flex items-center justify-between p-4 hover:bg-gray-100 dark:hover:bg-gray-750 cursor-pointer">
<div>
<h4 class="font-medium">Spam Protection</h4>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">"Sorry, this number is not accepting calls..."</p>
</div>
<i class="fas fa-chevron-right text-gray-400"></i>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-2xl mt-4 p-5">
<h3 class="font-medium">Response Style</h3>
<div class="mt-4 space-y-4">
<div class="flex items-center justify-between">
<div>
<h4 class="font-medium">Formal Tone</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">Professional business language</p>
</div>
<label class="ios-switch">
<input type="checkbox" checked>
<span class="ios-slider"></span>
</label>
</div>
<div class="flex items-center justify-between">
<div>
<h4 class="font-medium">Friendly Tone</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">Casual conversation style</p>
</div>
<label class="ios-switch">
<input type="checkbox">
<span class="ios-slider"></span>
</label>
</div>
<div class="flex items-center justify-between">
<div>
<h4 class="font-medium">Use My Name</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">"This is [Your Name]'s phone"</p>
</div>
<label class="ios-switch">
<input type="checkbox" checked>
<span class="ios-slider"></span>
</label>
</div>
</div>
</div>
</div>
</div>
<!-- Text Training Screen -->
<div id="trainingScreen" class="screen h-full flex flex-col">
<div class="px-4 pt-4">
<div class="flex items-center justify-between">
<div class="flex items-center">
<button class="p-2 rounded-full" onclick="showScreen('homeScreen')">
<i class="fas fa-arrow-left"></i>
</button>
<div class="ml-2">
<h2 class="text-xl font-bold">Train AI</h2>
<p class="text-gray-500 dark:text-gray-400 text-sm -mt-1">Linked to (562) 228-9429</p>
</div>
</div>
<button class="text-accent" id="saveTraining">Save</button>
</div>
</div>
<div class="mt-4 px-4 flex-1 overflow-hidden flex flex-col">
<div class="bg-white dark:bg-gray-800 rounded-2xl p-5 flex-1 overflow-hidden flex flex-col">
<div class="flex-1 overflow-y-auto pb-4">
<div class="ai-bubble">
<p>How would you like me to respond to calls from your family?</p>
</div>
<div class="user-bubble mt-4">
<p>Always transfer calls from Mom and Dad to me</p>
</div>
<div class="ai-bubble mt-4">
<p>Noted! I'll transfer calls from Mom and Dad immediately.</p>
<p class="mt-2">For other family members, how should I respond?</p>
</div>
<div class="user-bubble mt-4">
<p>Ask them for the reason of calling and text me if it's important</p>
</div>
<div class="ai-bubble mt-4">
<p>Got it. Here's the response I created based on your feedback:</p>
<div class="mt-2 bg-blue-50 dark:bg-blue-900 rounded-lg p-3">
<p>"Hello, this is Alex's assistant. Could you let me know what you're calling about? I'll make sure they get your message."</p>
</div>
<p class="mt-2">Does this work?</p>
</div>
</div>
<div class="mt-auto pt-4 border-t border-gray-200 dark:border-gray-700">
<div class="flex gap-2">
<input id="trainingInput" type="text" class="ios-input flex-1" placeholder="Teach your assistant..." onkeypress="handleTrainingKeyPress(event)">
<div class="flex gap-2">
<button class="w-12 h-12 rounded-xl bg-blue-500 flex items-center justify-center text-white" onclick="submitTextTraining()">
<i class="fas fa-keyboard"></i>
</button>
<button class="w-12 h-12 rounded-xl bg-purple-500 flex items-center justify-center text-white" onclick="showScreen('voiceTrainingScreen')">
<i class="fas fa-microphone"></i>
</button>
</div>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-2 text-center">The AI learns from every interaction</p>
</div>
</div>
</div>
</div>
<!-- Enhanced Navigation Tab Bar -->
<div class="tab-bar fixed inset-x-0 bottom-0 bg-white dark:bg-gray-800 shadow-lg p-2 flex justify-around items-center border-t border-gray-200 dark:border-gray-700">
<button class="flex flex-col items-center text-xs" onclick="showScreen('homeScreen')">
<i class="fas fa-home mb-1"></i>
<span>Home</span>
</button>
<button class="flex flex-col items-center text-xs" onclick="showScreen('repliesScreen')">
<i class="fas fa-comments mb-1"></i>
<span>Replies</span>
</button>
<button class="flex flex-col items-center text-xs" onclick="showScreen('trainingScreen')">
<i class="fas fa-graduation-cap mb-1"></i>
<span>Train</span>
</button>
<button class="flex flex-col items-center text-xs" onclick="showScreen('settingsScreen')">
<i class="fas fa-cog mb-1"></i>
<span>Settings</span>
</button>
</div>
<!-- Voice Training Screen -->
<div id="voiceTrainingScreen" class="screen h-full flex flex-col">
<div class="px-4 pt-4">
<div class="flex items-center justify-between">
<div class="flex items-center">
<button class="p-2 rounded-full" onclick="showScreen('trainingScreen')">
<i class="fas fa-arrow-left"></i>
</button>
<div class="ml-2">
<h2 class="text-xl font-bold">Voice Training</h2>
<p class="text-gray-500 dark:text-gray-400 text-sm -mt-1">Train vocal responses</p>
</div>
</div>
</div>
</div>
<div class="mt-4 px-4 flex-1 overflow-hidden flex flex-col">
<div class="bg-white dark:bg-gray-800 rounded-2xl p-5 flex-1 overflow-hidden flex flex-col">
<div id="voiceFeedback" class="flex-1 overflow-y-auto pb-4">
<div class="ai-bubble">
<p>Let's train your AI's verbal responses. Start by saying sample phrases you'd like it to use.</p>
</div>
</div>
<div class="mt-auto pt-4 border-t border-gray-200 dark:border-gray-700">
<div class="flex flex-col gap-3">
<div class="flex gap-2">
<button id="startRecording" class="flex-1 h-12 rounded-xl bg-red-500 text-white" onclick="startVoiceTraining()">
<i class="fas fa-microphone mr-2"></i> Record Response
</button>
<button class="w-12 h-12 rounded-xl bg-gray-200 flex items-center justify-center" onclick="playLastResponse()">
<i class="fas fa-play"></i>
</button>
</div>
<div class="text-center">
<p class="text-xs text-gray-500 dark:text-gray-400">Analyzing: Tone, Clarity, Pace</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- AI Chat Test Screen -->
<div id="chatScreen" class="screen h-full flex flex-col">
<div class="px-4 pt-4">
<div class="flex items-center">
<button class="p-2 rounded-full" onclick="showScreen('homeScreen')">
<i class="fas fa-arrow-left"></i>
</button>
<h2 class="text-xl font-bold ml-2">AI Chat Test</h2>
</div>
<p class="text-gray-500 dark:text-gray-400 mt-1 ml-12">Test and train the AI assistant</p>
</div>
<div class="mt-4 px-4 flex-1 overflow-hidden flex flex-col">
<div class="bg-white dark:bg-gray-800 rounded-2xl p-5 flex-1 overflow-hidden flex flex-col">
<div id="chatMessages" class="flex-1 overflow-y-auto pb-4 space-y-4">
<div class="ai-bubble animate-fadeIn">
<p>Hi! I'm your AI assistant for Jay's Mobile Wash. I can answer questions about our detailing services and learn from our conversations.</p>
<p class="mt-2">Would you like to test my knowledge or teach me something new?</p>
</div>
</div>
<div class="mt-auto pt-4 border-t border-gray-200 dark:border-gray-700">
<div class="flex gap-2">
<input id="chatInput" type="text" class="ios-input flex-1" placeholder="Ask or teach me..." onkeypress="handleChatKeyPress(event)">
<button class="w-12 h-12 rounded-xl bg-accent flex items-center justify-center text-white" onclick="submitChatMessage()">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-2 text-center">I'll learn from every interaction</p>
</div>
</div>
</div>
</div>
<!-- Settings Screen -->
<div id="settingsScreen" class="screen h-full flex flex-col">
<div class="px-4 pt-4">
<div class="flex items-center">
<button class="p-2 rounded-full" onclick="showScreen('homeScreen')">
<i class="fas fa-arrow-left"></i>
</button>
<h2 class="text-xl font-bold ml-2">Jay's Mobile Wash Settings</h2>
</div>
<p class="text-gray-500 dark:text-gray-400 mt-1 ml-12">Configure AI response behavior</p>
</div>
<div class="mt-4 px-4 flex-1 overflow-y-auto">
<div class="bg-white dark:bg-gray-800 rounded-2xl p-5 mb-4">
<h3 class="font-medium mb-4">Call Response Timing</h3>
<div class="space-y-4">
<div class="flex items-center justify-between">
<div>
<h4>Max Ring Time (seconds)</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">If you don't answer by this time, AI will answer</p>
</div>
<input type="number" id="maxRingTime" value="30" min="5" max="60" class="w-20 px-2 py-1 rounded-lg border border-gray-300">
</div>
<div class="flex items-center justify-between">
<div>
<h4>SMS Auto-Reply Time (minutes)</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">If no reply to SMS by this time</p>
</div>
<input type="number" id="smsResponseTime" value="15" min="1" max="120" class="w-20 px-2 py-1 rounded-lg border border-gray-300">
</div>
<div class="border-t border-gray-200 dark:border-gray-700 pt-4">
<h3 class="font-medium mb-2">Business Hours</h3>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-sm text-gray-500 dark:text-gray-400 mb-1">Open Time</label>
<input type="time" id="openTime" value="08:00" class="w-full px-2 py-1 rounded-lg border border-gray-300">
</div>
<div>
<label class="block text-sm text-gray-500 dark:text-gray-400 mb-1">Close Time</label>
<input type="time" id="closeTime" value="18:00" class="w-full px-2 py-1 rounded-lg border border-gray-300">
</div>
</div>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-2xl p-5">
<h3 class="font-medium mb-4">Call Handling Configuration</h3>
<div class="flex items-center justify-between mb-4">
<div>
<h4>Answer Incoming Calls</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">Enable AI to answer phone calls</p>
</div>
<label class="ios-switch">
<input type="checkbox" id="answerCallsToggle" checked>
<span class="ios-slider"></span>
</label>
</div>
<div class="flex items-center justify-between mb-4">
<div>
<h4>Live Call Training Mode</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">Ask me questions during calls to learn</p>
</div>
<label class="ios-switch">
<input type="checkbox" id="callTrainingToggle">
<span class="ios-slider"></span>
</label>
</div>
<h3 class="font-medium mb-4 mt-6">Response Messages</h3>
<div class="space-y-4">
<div class="flex items-center justify-between">
<div>
<h4>Language</h4>
<p class="text-gray-500 dark:text-gray-400 text-sm">Default response language</p>
</div>
<select id="responseLanguage" class="px-2 py-1 rounded-lg border border-gray-300">
<option value="en">English</option>
<option value="es">Español</option>
</select>
</div>
<div>
<label class="block text-sm mb-1">Personal Greeting</label>
<textarea id="personalGreeting" rows="2" class="w-full px-3 py-2 rounded-lg border border-gray-300" placeholder="Custom greeting when answering calls..."></textarea>
</div>
<div>
<label class="block text-sm mb-1">Missed Call Response (English)</label>
<textarea id="missedCallResponse" rows="2" class="w-full px-3 py-2 rounded-lg border border-gray-300">Hello, this is Jay's Mobile Wash. We missed your call. Please leave a message and we'll return your call as soon as possible.</textarea>
</div>
<div>
<label class="block text-sm mb-1">Respuesta Llamada Perdida (Español)</label>
<textarea id="missedCallResponseEs" rows="2" class="w-full px-3 py-2 rounded-lg border border-gray-300">Hola, es el lavado móvil de Jay. Perdimos su llamada. Por favor deje un mensaje y le devolveremos la llamada lo antes posible.</textarea>
</div>
<div>
<label class="block text-sm mb-1">After Hours Response (English)</label>
<textarea id="afterHoursResponse" rows="2" class="w-full px-3 py-2 rounded-lg border border-gray-300">Thank you for contacting Jay's Mobile Wash. Our business hours are [hours]. We will respond when we reopen.</textarea>
</div>
<div>
<label class="block text-sm mb-1">Respuesta Fuera Horario (Español)</label>
<textarea id="afterHoursResponseEs" rows="2" class="w-full px-3 py-2 rounded-lg border border-gray-300">Gracias por contactar el lavado móvil de Jay. Nuestro horario es de [hours]. Responderemos cuando volvamos a abrir.</textarea>
</div>
</div>
<button onclick="saveCallTimings()" class="mt-6 w-full bg-green-500 text-white py-2 rounded-lg font-medium">Save Call Timing Settings</button>
<button onclick="document.getElementById('directPhoneLink').click()" class="mt-4 w-full bg-blue-500 text-white py-2 rounded-lg font-medium flex items-center justify-center">
<i class="fas fa-phone mr-2"></i> Test Phone Connection
</button>
</div>
</div>
</div>
<!-- Floating Action Button -->
<div class="fab" onclick="answerCall()">
<i class="fas fa-phone"></i>
</div>
</div>
<script>
// Enhanced Training Data Structure
let trainingData = {
// Text-based training
textTraining: {
responseTemplates: {},
patternMatching: {},
contextRules: {},
learningMetrics: {
responseAccuracy: 0.85,
confidenceScores: {}
}
},
// Voice-based training
voiceTraining: {
speechProfiles: {},
toneAnalysis: {},
voiceResponses: [],
pronunciation: {}
},
customResponses: {
business: '"Hello, this is [Your Name]\'s assistant. How can I help you today?"',
personal: '"Hi, this is [Name] calling. How can I help you?"',
spam: '"Sorry, this number is not accepting unsolicited calls at this time."',
urgent: '"This is urgent, please connect me immediately"'
},
// Grok-specific additions
humorProfiles: {},
sarcasmDetection: {},
emotionalContext: {},
savedTemplates: []
};
// Enhanced training function storage
function storeTrainingPattern(pattern, response) {
trainingData.trainedPatterns[pattern.toLowerCase()] = response;
localStorage.setItem('AI_Training', JSON.stringify(trainingData));
}
// Enhanced Navigation System
function showScreen(screenId) {
// Hide all screens
document.querySelectorAll('.screen').forEach(screen => {
screen.classList.remove('active');
});
// Show selected screen with animation
const targetScreen = document.getElementById(screenId);
targetScreen.classList.add('active');
// Apply active state to tab bar
const tabs = document.querySelectorAll('.tab-bar button');
tabs.forEach(tab => {
tab.classList.remove('text-accent', 'font-medium');
tab.classList.add('text-gray-500');
});
// Find and activate matching tab
const activeIndex = Array.from(tabs).findIndex(tab =>
tab.getAttribute('onclick').includes(screenId)
);
if (activeIndex >= 0) {
tabs[activeIndex].classList.remove('text-gray-500');
tabs[activeIndex].classList.add('text-accent', 'font-medium');
}
// Screen-specific initialization
switch(screenId) {
case 'settingsScreen':
loadSettings();
break;
case 'trainingScreen':
initTraining();
break;
case 'voiceTrainingScreen':
initVoiceTraining();
break;
}
// Save last viewed screen
localStorage.setItem('lastScreen', screenId);
}
// Save call timing settings to localStorage
function saveCallTimings() {
businessSettings.maxRingTime = parseInt(document.getElementById('maxRingTime').value);
businessSettings.smsResponseTime = parseInt(document.getElementById('smsResponseTime').value);
businessSettings.businessHours = {
open: document.getElementById('openTime').value,
close: document.getElementById('closeTime').value
};
localStorage.setItem('JMW_Settings', JSON.stringify(businessSettings));
// Show success message
const notification = document.createElement('div');
notification.className = 'fixed top-40 left-1/2 transform -translate-x-1/2 bg-green-500 text-white px-6 py-3 rounded-xl animate-fadeIn z-50 shadow-lg';
notification.innerHTML = 'Call timings saved successfully!';
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 3000);
}
// Simulate AI learning progress
function simulateAIProgress() {
const progressBars = document.querySelectorAll('.progress');
progressBars.forEach(bar => {
const width = 70 + Math.floor(Math.random() * 30);
bar.style.width = `${width}%`;
});
}
// Toggle call recording function
function toggleRecording() {
const fab = document.querySelector('.fab');
const icon = fab.querySelector('i');
if (icon.classList.contains('fa-microphone')) {
icon.classList.remove('fa-microphone');
icon.classList.add('fa-stop');
fab.style.background = 'linear-gradient(135deg, #ff375f, #ff2d55)';
// Show a notification
const notification = document.createElement('div');
notification.innerHTML = `
<div class="fixed top-16 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white px-4 py-2 rounded-full animate-fadeIn">
Recording started
</div>
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 2000);
} else {
icon.classList.remove('fa-stop');
icon.classList.add('fa-microphone');
fab.style.background = 'linear-gradient(135deg, #0a84ff, #5e5ce6)';
// Simulate AI learning after recording
simulateAIProgress();
}
}
// Answer call function
function answerCall(options = {}) {
const {
isTest = false,
customNumber = '',
scenario = 'standard',
retryCount = 0
} = options;
// Log this interaction for learning
const callLog = {
timestamp: new Date().toISOString(),
number: customNumber || (isTest ? 'Test Caller' : 'Unknown'),
scenario,
duration: businessSettings.maxRingTime,
response: businessSettings.personalGreeting,
outcome: 'answered'
};
trainingData.callHistory = trainingData.callHistory || [];
trainingData.callHistory.push(callLog);
const fab = document.querySelector('.fab');
const icon = fab.querySelector('i');
if (icon.classList.contains('fa-phone')) {
icon.classList.remove('fa-phone');
icon.classList.add('fa-stop');
fab.style.background = 'linear-gradient(135deg, #32d74b, #30d158)';
// Show call is ringing
showResponseMessage(`Call ringing (will answer in ${businessSettings.maxRingTime} seconds if no reply)...`);
// Set timeout to answer call after maxRingTime
setTimeout(() => {
if (icon.classList.contains('fa-stop')) { // If still not answered
// Create a new call card
const homeScreen = document.getElementById('homeScreen');
const callCard = document.createElement('div');
callCard.className = 'bg-white dark:bg-gray-800 rounded-2xl p-4 mt-4 animate-fadeIn';
const caller = isTest ? 'Test Caller' : 'Unknown (New number)';
callCard.innerHTML = `
<div class="flex items-center justify-between">
<div>
<h3 class="font-medium">AI answered call</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">From: ${caller}</p>
</div>
<div class="text-green-500">
<i class="fas fa-check-circle text-xl"></i>
</div>
</div>
<div class="mt-3 text-sm">
<p>AI answered after ${businessSettings.maxRingTime} seconds</p>
</div>
`;
homeScreen.insertBefore(callCard, homeScreen.children[3]);
showResponseMessage(businessSettings.personalGreeting ||
(businessSettings.responseLanguage === 'es' ?
"Hola, habla el asistente de Jay's Mobile Wash" :
"Hello, this is Jay's Mobile Wash assistant"));
}
}, businessSettings.maxRingTime * 1000);
} else {
// End call
icon.classList.remove('fa-stop');
icon.classList.add('fa-phone');
fab.style.background = 'linear-gradient(135deg, #0a84ff, #5e5ce6)';
}
}
function handleSMS(message, sender, isiMessage) {
const responseLanguage = businessSettings.responseLanguage;
const greeting = businessSettings.personalGreeting;
// Set timeout for auto-response
setTimeout(() => {
if (!message.includes('RE:')) { // If no reply was sent
const response = greeting ||
(responseLanguage === 'es' ?
"Gracias por su mensaje. ¿En qué puedo ayudarle hoy?" :
"Thanks for your message. How can I help you today?");
showResponseMessage(`Auto-reply sent after ${businessSettings.smsResponseTime} mins: ${response}`);
}
}, businessSettings.smsResponseTime * 60 * 1000);
return handleSMSResponse(message, sender, isiMessage);
}
// Enhanced training functions
function saveTraining() {
localStorage.setItem('AI_Training', JSON.stringify(trainingData));
// Show persistent notification
const notification = document.createElement('div');
notification.className = 'fixed top-40 left-1/2 transform -translate-x-1/2 bg-gradient-to-r from-green-400 to-blue-500 text-white px-6 py-3 rounded-xl animate-fadeIn z-50 shadow-lg';
notification.innerHTML = `
<div class="flex items-center">
<i class="fas fa-check-circle mr-2"></i>
<span>AI training saved! New responses will be active immediately.</span>
</div>
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
showScreen('homeScreen');
}, 3000);
// Update home screen with new training count
const statsElement = document.querySelector('#homeScreen .w-10.h-10 + div p');
statsElement.textContent = `Trained on ${Object.keys(trainingData.trainedPatterns).length} patterns`;
}
// Add animation to elements when they appear
function addAppearAnimations() {
document.querySelectorAll('.animate-fadeIn').forEach((el, index) => {
el.style.animationDelay = `${index * 0.1}s`;
});
}
// Smart Training Functions
function submitTraining() {
const input = document.getElementById('trainingInput');
if (input.value.trim() !== '') {
trainingData.responses.push(input.value);
// Add the user's message to the chat
const chatContainer = document.querySelector('#trainingScreen .flex-1.overflow-y-auto');
const userBubble = document.createElement('div');
userBubble.className = 'user-bubble mt-4 animate-fadeIn';
userBubble.innerHTML = `<p>${input.value}</p>`;
chatContainer.appendChild(userBubble);
// Process training input with smart logic
const processed = processTrainingInput(input.value);
// Generate and add AI response
setTimeout(() => {
const aiBubble = document.createElement('div');
aiBubble.className = 'ai-bubble mt-4 animate-fadeIn';
if (processed.success) {
aiBubble.innerHTML = `
<p>Understood! I'll handle calls that way now.</p>
<div class="mt-2 bg-green-50 dark:bg-green-900 rounded-lg p-3">
<h4 class="font-medium">New Response Pattern:</h4>
<p class="mt-1"><strong>When caller says:</strong> ${processed.triggerPhrases.join(' OR ')}</p>
<p class="mt-1"><strong>I will:</strong> ${processed.response}</p>
</div>
<p class="mt-2">Would you like to review any other scenarios?</p>
`;
storeTrainingPattern(processed.triggerPhrases[0], processed.response);
} else {
aiBubble.innerHTML = `
<p>I think you want me to handle calls differently when:</p>
<div class="mt-2 bg-blue-50 dark:bg-blue-900 rounded-lg p-3">
<p>${processed.response}</p>
</div>
<p class="mt-2">Can you confirm or clarify how you'd like me to respond?</p>
`;
}
chatContainer.appendChild(aiBubble);
chatContainer.scrollTop = chatContainer.scrollHeight;
}, 800);
input.value = '';
}
}
function handleTrainingKeyPress(e) {
if (e.key === 'Enter') {
submitTraining();
}
}
// Advanced Reasoning Engine
function processTrainingInput(inputText) {
// Use NLP to analyze input
const tokens = inputText.toLowerCase().split(/\s+/);
const intent = detectIntent(inputText);
const context = buildContext(inputText);
// Apply reasoning rules
const reasoningChain = buildReasoningChain(intent, context);
// Generate response variants
const responseOptions = generateResponseVariants(intent, context);
return {
success: true,
intent: intent,
reasoning: reasoningChain,
responseVariants: responseOptions,
confidence: 0.92, // AI's confidence in this solution
suggestedActions: generateFollowUpQuestions(intent)
};
}
function detectIntent(text) {
// Enhanced intent detection with fuzzy matching
const normalized = text.toLowerCase();
// Business vs personal detection
if (/business|client|customer|service/i.test(normalized)) {
return 'business_call';
} else if (/family|friend|personal/i.test(normalized)) {
return 'personal_call';
}
// Action detection
if (/transfer|connect|put through/i.test(normalized)) {
return 'call_transfer';
} else if (/message|text|notify/i.test(normalized)) {
return 'message_relay';
}
// Default to learning new response
return 'new_response_pattern';
}
function buildReasoningChain(intent, context) {
// Build logical reasoning steps
const chains = {
'business_call': [
"Detected business context → Using formal tone",
"Verified working hours → Will mention availability",
"Analyzed keywords → Focus on professional responses"
],
'personal_call': [
"Identified personal connection → Using friendly tone",
"Checked relationship markers → Adjusting familiarity level",
"Assessed urgency → Determining response priority"
]
};
return chains[intent] || [
"No specific pattern found → Learning new behavior",
"Analyzing word frequency → Building response map",
"Cross-referencing with existing knowledge → Creating adaptive response"
];
}
// First analyze diction and emotional context
analyzeDiction(inputText);
// Grok-style pattern matching with contextual learning
const context = getCurrentContext();
const patterns = [
// Direct instructions
/(when|if) (.+?) (then|do|respond with) (.+)/i,
// Examples
/(for|when) (\w+) calls?,? (say|respond) "(.+?)"/i,
// Behavioral patterns
/(treat|handle) (.+?) (as|like) (\w+)/i,
// Tone adjustments
/(make|be) (more|less) (\w+) (when|for) (\w+)/i
];
for (const pattern of patterns) {
const match = inputText.match(pattern);
if (match) {
const [, triggerType, trigger, action, response] = match;
storeBehaviorPattern(trigger, response, context);
return {
success: true,
triggerPhrases: [trigger],
response: enhanceResponse(response, context),
context: context
};
}
}
// Fallback to Grok's adaptive learning
return adaptiveLearning(inputText);
}
// Grok-style diction analysis
// Voice Training System
let voiceRecording = false;
let mediaRecorder;
let audioChunks = [];
function startVoiceTraining() {
const button = document.getElementById('startRecording');
if (!voiceRecording) {
// Start recording
button.innerHTML = '<i class="fas fa-stop mr-2"></i> Stop Recording';
button.classList.add('bg-red-600');
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start();
voiceRecording = true;
mediaRecorder.ondataavailable = e => {
audioChunks.push(e.data);
};
mediaRecorder.onstop = () => {
processVoiceRecording();
};
});
} else {
// Stop recording
button.innerHTML = '<i class="fas fa-microphone mr-2"></i> Record Response';
button.classList.remove('bg-red-600');
mediaRecorder.stop();
voiceRecording = false;
}
}
function processVoiceRecording() {
const audioBlob = new Blob(audioChunks);
audioChunks = [];
// In a real implementation, you would send this to a voice processing API
const feedback = document.getElementById('voiceFeedback');
const bubble = document.createElement('div');
bubble.className = 'ai-bubble mt-4 animate-fadeIn';
bubble.innerHTML = `
<p>Recording received! Analysis:</p>
<ul class="ml-5 mt-2 list-disc">
<li>Tone: Professional</li>
<li>Clarity: 85%</li>
<li>Recommended pacing adjustment: Slightly slower</li>
</ul>
<p class="mt-2">Would you like to save this as a standard response?</p>
`;
feedback.appendChild(bubble);
// Save to training data
trainingData.voiceTraining.voiceResponses.push({
timestamp: new Date().toISOString(),
blob: URL.createObjectURL(audioBlob),
analysis: {
tone: 'professional',
clarity: 0.85,
pace: 0.72
}
});
localStorage.setItem('AI_Training', JSON.stringify(trainingData));
}
const words = text.toLowerCase().split(/\s+/);
const contractions = text.match(/\w+'?\w*/g) || [];
// Update diction profile
if (!trainingData.dictionProfiles.user) {
trainingData.dictionProfiles.user = {
wordUsage: {},
contractionRate: 0,
formalityScore: 0,
complexityScore: 0
};
}
contractions.forEach(word => {
if (word.includes("'")) {
trainingData.dictionProfiles.user.contractionRate++;
}
});
// Word usage stats
words.forEach(word => {
trainingData.dictionProfiles.user.wordUsage[word] =
(trainingData.dictionProfiles.user.wordUsage[word] || 0) + 1;
});
// Save updated profile
localStorage.setItem('AI_DictionProfile', JSON.stringify(trainingData.dictionProfiles));
}
function interpretTrainingIntent(text) {
const lower = text.toLowerCase();
if (lower.includes('family') || lower.includes('mom') || lower.includes('dad')) {
return trainingData.customResponses.personal;
}
else if (lower.includes('work') || lower.includes('business') || lower.includes('colleague')) {
return trainingData.customResponses.business;
}
else if (lower.includes('appointment') || lower.includes('schedule')) {
return trainingData.customResponses.appointments;
}
else if (lower.includes('urgent') || lower.includes('emergency')) {
return trainingData.customResponses.urgent;
}
else if (lower.includes('callback') || lower.includes('call back')) {
return trainingData.customResponses.callbacks;
}
else if (lower.includes('spam') || lower.includes('scam') || lower.includes('block')) {
return trainingData.customResponses.spam;
}
else {
return detectNewPattern(text);
}
}
function detectNewPattern(text) {
// Use simple NLP to extract key phrases
const keywords = text.match(/\b(\w{4,})\b/g) || [];
const commands = ['transfer', 'ask', 'tell', 'say', 'respond', 'record'];
const action = commands.find(cmd => text.toLowerCase().includes(cmd)) || 'handle';
const context = keywords.filter(w =>
!commands.includes(w.toLowerCase()) &&
w.length > 3
).join(', ');
return `When call relates to ${context || "this situation"}, I will ${action} accordingly.`;
}
// Initialize when page loads
document.addEventListener('DOMContentLoaded', () => {
// Load settings immediately when page loads
if (localStorage.getItem('JMW_Settings')) {
businessSettings = JSON.parse(localStorage.getItem('JMW_Settings'));
loadSettings();
}
// Load saved training data
const savedData = localStorage.getItem('AI_Training');
if (savedData) {
trainingData = JSON.parse(savedData);
}
document.getElementById('saveTraining').addEventListener('click', saveTraining);
addAppearAnimations();
simulateAIProgress();
// Enhanced screen initialization
document.querySelectorAll('[onclick^="showScreen"]').forEach(btn => {
btn.addEventListener('click', function() {
const screenName = this.getAttribute('onclick').match(/'(.*?)'/)[1];
setTimeout(() => {
if (screenName === 'trainingScreen') {
document.getElementById('trainingInput').focus();
populateTrainingHistory();
}
else if (screenName === 'settingsScreen') {
// Make sure settings are loaded
loadSettings();
}
}, 300);
});
});
// Direct phone link initialization
const phoneLink = document.createElement('a');
phoneLink.href = 'tel:5622289429';
phoneLink.id = 'directPhoneLink';
document.body.appendChild(phoneLink);
});
// Enhanced settings save/load
function saveSettings() {
businessSettings = {
maxRingTime: parseInt(document.getElementById('maxRingTime').value),
smsResponseTime: parseInt(document.getElementById('smsResponseTime').value),
responseLanguage: document.getElementById('responseLanguage').value,
personalGreeting: document.getElementById('personalGreeting').value,
businessHours: {
open: document.getElementById('openTime').value,
close: document.getElementById('closeTime').value
},
responses: {
missedCall: {
en: document.getElementById('missedCallResponse').value,
es: document.getElementById('missedCallResponseEs').value
},
afterHours: {
en: document.getElementById('afterHoursResponse').value,
es: document.getElementById('afterHoursResponseEs').value
}
}
};
localStorage.setItem('JMW_Settings', JSON.stringify(businessSettings));
// Show success message
const notification = document.createElement('div');
notification.className = 'fixed top-40 left-1/2 transform -translate-x-1/2 bg-green-500 text-white px-6 py-3 rounded-xl animate-fadeIn z-50 shadow-lg';
notification.innerHTML = '<i class="fas fa-check-circle mr-2"></i> Settings saved successfully!';
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 3000);
// Test phone connection
document.getElementById('directPhoneLink').click();
}
// Mobile detailing knowledge base
const detailingKnowledge = {
website: "https://www.jaysmobilewash.com",
pricing: {
basic: { car: 60, suv: 70 },
luxury: { car: 130, suv: 140 },
max: { car: 200, suv: 210 }
},
packages: {
basic: "2-Step Hand Wash, Tornador Blast, Rim Cleaning, Interior Wipe-Down",
luxury: "Basic + Ceramic Wax, Dust Repellent, Vinyl Restoration",
max: "Luxury + Graphene Wax, Steam Sanitization, Leather Conditioning"
},
features: [
"Eco-Friendly Self-Sufficient Service",
"Deionized Spot-Free Water",
"Tornador Z-007 Compressed Air Cleaning",
"Customizable Packages"
]
};
function handleChatKeyPress(e) {
if (e.key === 'Enter') {
submitChatMessage();
}
}
function submitChatMessage() {
const input = document.getElementById('chatInput');
if (input.value.trim() !== '') {
// Add user message
const chatContainer = document.getElementById('chatMessages');
const userBubble = document.createElement('div');
userBubble.className = 'user-bubble mt-4 animate-fadeIn';
userBubble.innerHTML = `<p>${input.value}</p>`;
chatContainer.appendChild(userBubble);
// Process and generate AI response
setTimeout(() => {
const aiResponse = generateAIResponse(input.value);
const aiBubble = document.createElement('div');
aiBubble.className = 'ai-bubble mt-4 animate-fadeIn';
aiBubble.innerHTML = `<p>${aiResponse}</p>`;
chatContainer.appendChild(aiBubble);
chatContainer.scrollTop = chatContainer.scrollHeight;
// Learn from the interaction
learnFromChat(input.value, aiResponse);
}, 500);
input.value = '';
}
}
function generateAIResponse(message) {
const lowerMsg = message.toLowerCase();
// Check for pricing questions
if (lowerMsg.includes('price') || lowerMsg.includes('cost') || lowerMsg.includes('how much')) {
if (lowerMsg.includes('basic')) {
return `Our Basic package is ${detailingKnowledge.pricing.basic.car} for cars and ${detailingKnowledge.pricing.basic.suv} for SUVs. It includes: ${detailingKnowledge.packages.basic}`;
} else if (lowerMsg.includes('luxury')) {
return `Our Luxury package is ${detailingKnowledge.pricing.luxury.car} for cars and ${detailingKnowledge.pricing.luxury.suv} for SUVs. Includes: ${detailingKnowledge.packages.luxury}`;
} else if (lowerMsg.includes('max')) {
return `Our Max package is ${detailingKnowledge.pricing.max.car} for cars and ${detailingKnowledge.pricing.max.suv} for SUVs. Everything in: ${detailingKnowledge.packages.max}`;
}
return `We offer three main packages:\nBasic: ${detailingKnowledge.pricing.basic.car}-${detailingKnowledge.pricing.basic.suv}\nLuxury: ${detailingKnowledge.pricing.luxury.car}-${detailingKnowledge.pricing.luxury.suv}\nMax: ${detailingKnowledge.pricing.max.car}-${detailingKnowledge.pricing.max.suv}`;
}
// Check for service questions
if (lowerMsg.includes('service') || lowerMsg.includes('include') || lowerMsg.includes('wash')) {
return `Our services include:\n- ${detailingKnowledge.features.join('\n- ')}\n\nWould you like details on a specific package?`;
}
// Check for appointment scheduling
if (lowerMsg.includes('book') || lowerMsg.includes('schedule') || lowerMsg.includes('appointment')) {
return `To schedule an appointment, please call or text us at (562) 228-9429 with your preferred date, time, and package selection.`;
}
// Check for website questions
if (lowerMsg.includes('website') || lowerMsg.includes('online')) {
return `You can find more information on our website: ${detailingKnowledge.website}`;
}
// Default response if no matches
return `I'm still learning about Jay's Mobile Wash. Could you clarify your question or teach me the correct response? For example:\n- "For basic washes, say 'Our Basic package starts at $60'"\n\nYou can also visit our website: ${detailingKnowledge.website}`;
}
function learnFromChat(message, response) {
// Save new knowledge
if (!trainingData.detailingKnowledge) {
trainingData.detailingKnowledge = [];
}
trainingData.detailingKnowledge.push({
question: message,
answer: response,
timestamp: new Date().toISOString()
});
localStorage.setItem('AI_Training', JSON.stringify(trainingData));
}
function populateTrainingHistory() {
const chatContainer = document.querySelector('#trainingScreen .flex-1.overflow-y-auto');
chatContainer.innerHTML = '';
if (trainingData.responses.length === 0) {
const welcomeBubble = document.createElement('div');
welcomeBubble.className = 'ai-bubble animate-fadeIn';
welcomeBubble.innerHTML = `
<p>Hi! I'm your call assistant. Teach me how to handle calls by:</p>
<ol class="list-decimal pl-5 mt-2 space-y-1">
<li>Setting response templates</li>
<li>Training me on specific call types</li>
<li>Testing responses in real calls</li>
</ol>
<p class="mt-2">Try saying something like: "If caller says 'urgent', mark as important"</p>
`;
chatContainer.appendChild(welcomeBubble);
} else {
trainingData.responses.forEach(item => {
const userBubble = document.createElement('div');
userBubble.className = 'user-bubble mt-4 animate-fadeIn';
userBubble.innerHTML = `<p>${item}</p>`;
chatContainer.appendChild(userBubble);
});
}
}
</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=jjmandog/jaysx" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>