Study all information after this sentence. **JAY’S MOBILE WASH – LUXURY DETAILING AT YOUR DOORSTEP!** *(Premium Mobile Detailing Services)* **💧 Eco-Friendly & Self-Sufficient:** *We bring our own power supply and use spot-free deionized water for a flawless, zero-residue finish.* --- **PRICING AT A GLANCE** **CARS/SUVs** ✅ **Jay’s Basic**: Small Car **$60** | SUV **$70** ✅ **Jay’s Luxury**: Small Car **$130** | SUV **$140** ✅ **Jay’s Max**: Small Car **g me 200** | SUV **$210** ✅ **Motorcycles, RVs, Buses, Vans**: *Contact for Custom Pricing* **DETAILING PACKAGES** *(Prices apply to cars/SUVs. Larger vehicles? Call for quotes.)* **1. JAY’S BASIC – ESSENTIAL SHINE & C **JAY’S MOBILE WASH – LUXURY DETAILING AT YOUR DOORSTEP!** *(Premium Mobile Detailing Services)* **💧 Eco-Friendly & Self-Sufficient:** *We bring our own power supply and use spot-free deionized water for a flawless, zero-residue finish.* --- **PRICING AT A GLANCE** **CARS/SUVs** ✅ **Jay’s Basic**: Small Car **$60** | SUV **$70** ✅ **Jay’s Luxury**: Small Car **$130** | SUV **$140** ✅ **Jay’s Max**: Small Car **g me 200** | SUV **$210** ✅ **Motorcycles, RVs, Buses, Vans**: *Contact for Custom Pricing* **DETAILING PACKAGES** *(Prices apply to cars/SUVs. Larger vehicles? Call for quotes.)* **1. JAY’S BASIC – ESSENTIAL SHINE & CLEAN** **Small Car: $60 | SUV: $70** - 2-Step Hand Contact Wash (snow foam to remove embedded grime and followed by ceramic with silica for protection and shine like no other) - **Tornador Z-007 Compressed Air Blast** *(Deep-clean vents, seams, and hidden crevices)* - Alkaline & Ceramic SiO₂ Rim Cleaning ceramic two week long tire shine - Interior Wipe-Down & Shine - Deep Vacuuming & Cup Holder Cleaning **2. JAY’S LUXURY – SUPERIOR GLOSS & PROTECTION** **Small Car: $130 | SUV: $140** *(Includes Jay’s Basic + Upgrades)* - Ceramic Spray Wax/Sealant - SiO₂ Interior Cleanser with Dust Repellent - Vinyl Restoration & Trim Restore **3. JAY’S MAX – ULTIMATE SHOWROOM FINISH** **Small Car: $200 | SUV: $210** *(Includes Jay’s Luxury + Upgrades)* - **Graphene Hyper Wax/Sealant** (Hand or DA Polisher – *Next-level durability & gloss*) - **Luxury Steam Sanitization**: Deep-clean upholstery, vents, and crevices with premium steam tech - **Full Leather Conditioning**: Restore softness and prevent cracking - **Bio-Bomb Odor Neutralizer**: Eliminate stubborn smells at the molecular level - **6-Month Foam Seal Treatment** - **Brand-New Car Scent**: Enjoy that "fresh off the lot" aroma --- **PREMIUM ADD-ONS** *(Enhance Any Package – Ask for Pricing)* - **Ceramic Coating**: 2+ years of protection - **Full Polish + Scratch Removal**: DA-applied perfection - **Pet Hair Removal**: Ultrasonic tools + sanitization - **Rim De-Iron & Rust Removal**: Chrome polishing included - **Engine Bay Detailing**: Degrease & shine - **Complete Trim Restoration**: 6-month UV protection --- **BUILD YOUR CUSTOM PACKAGE!** *Mix and match services to create your dream experience. Examples:* - Basic Wash + Ceramic Coating + Pet Hair Removal - Luxury Package + Rim De-Iron + Trim Restoration - Max Package + Engine Bay Detailing + Rush Service *Call to design your personalized combo!* --- **CONDITIONAL SURCHARGES** *(Added to Base Price)* - **+30%**: Heavy dirt, gum, or embedded grime - **+50%**: Biohazards (vomit, urine, mold) - **+$50 After-Dark Work**: Professional lighting provided *(Guarantee void)* --- **QUALITY GUARANTEE** *Guaranteed results require:* 1. **Daylight/Proper Lighting** (No guarantees for night work) 2. **Full Disclosure** (Severe messes reported upfront) 3. **No Interruptions** (Avoid asking, “Why is it taking so long?” We’re detailers, not car washers—trust our process!) *Note: 92% of detailing flaws occur in low light (LA Detailing Institute, 2023).* --- **NOTES** - **1.75% card processing fee** applies to all transactions. - **$10 travel fee** for every 10 miles beyond 30 miles. --- **CONTACT US** 📞 **Call/Text**: 562-228-9429 📍 **Address**: 123 Anywhere St., Any City *(Motorcycles 🏍️ • RVs 🚐 • Buses 🚌 • Vans 🚚 – Custom Quotes Available!)* --- **DAYLIGHT REVEALS PERFECTION – WE DELIVER IT.** --- **Why Choose Jay’s?** ✅ **Self-Sufficient Service**: No need for outlets or water hookups – *we bring everything*. ✅ **Science-Backed Clean**: Deionized water prevents streaks; Bio-Bomb tech annihilates odors. ✅ **Tornador Z-007 in Basic**: Precision cleaning for vents, seams, and hidden areas. ✅ **Customizable Luxury**: From pet hair to polishing chrome, we cater to *your* vision. ✅**Every employee of Jay’s Mobile Starts Woth The Letter J . If a Jay didn’t wash it, then it ain’t washed right ;) ✨ ***************CALL JAY TODAY************** LEAN** **Small Car: $60 | SUV: $70** - 2-Step Hand Contact Wash - **Tornador Z-007 Compressed Air Blast** *(Deep-clean vents, seams, and hidden crevices)* - Alkaline & Ceramic SiO₂ Rim Cleaning - Interior Wipe-Down & Shine - Deep Vacuuming & Cup Holder Cleaning **2. JAY’S LUXURY – SUPERIOR GLOSS & PROTECTION** **Small Car: $130 | SUV: $140** *(Includes Jay’s Basic + Upgrades)* - Ceramic Spray Wax/Sealant - SiO₂ Interior Cleanser with Dust Repellent - Vinyl Restoration & Trim Restore **3. JAY’S MAX – ULTIMATE SHOWROOM FINISH** **Small Car: $200 | SUV: $210** *(Includes Jay’s Luxury + Upgrades)* - **Graphene Hyper Wax/Sealant** (Hand or DA Polisher – *Next-level durability & gloss*) - **Luxury Steam Sanitization**: Deep-clean upholstery, vents, and crevices with premium steam tech - **Full Leather Conditioning**: Restore softness and prevent cracking - **Bio-Bomb Odor Neutralizer**: Eliminate stubborn smells at the molecular level - **6-Month Foam Seal Treatment** - **Brand-New Car Scent**: Enjoy that "fresh off the lot" aroma --- **PREMIUM ADD-ONS** *(Enhance Any Package – Ask for Pricing)* - **Ceramic Coating**: 2+ years of protection - **Full Polish + Scratch Removal**: DA-applied perfection - **Pet Hair Removal**: Ultrasonic tools + sanitization - **Rim De-Iron & Rust Removal**: Chrome polishing included - **Engine Bay Detailing**: Degrease & shine - **Complete Trim Restoration**: 6-month UV protection --- **BUILD YOUR CUSTOM PACKAGE!** *Mix and match services to create your dream experience. Examples:* - Basic Wash + Ceramic Coating + Pet Hair Removal - Luxury Package + Rim De-Iron + Trim Restoration - Max Package + Engine Bay Detailing + Rush Service *Call to design your personalized combo!* --- **CONDITIONAL SURCHARGES** *(Added to Base Price)* - **+30%**: Heavy dirt, gum, or embedded grime - **+50%**: Biohazards (vomit, urine, mold) - **+$50 After-Dark Work**: Professional lighting provided *(Guarantee void)* --- **QUALITY GUARANTEE** *Guaranteed results require:* 1. **Daylight/Proper Lighting** (No guarantees for night work) 2. **Full Disclosure** (Severe messes reported upfront) 3. **No Interruptions** (Avoid asking, “Why is it taking so long?” We’re detailers, not car washers—trust our process!) *Note: 92% of detailing flaws occur in low light (LA Detailing Institute, 2023).* --- **NOTES** - **1.75% card processing fee** applies to all transactions. - **$10 travel fee** for every 10 miles beyond 30 miles. --- **CONTACT US** 📞 **Call/Text**: 562-228-9429 📍 **Address**: 123 Anywhere St., Any City *(Motorcycles 🏍️ • RVs 🚐 • Buses 🚌 • Vans 🚚 – Custom Quotes Available!)* --- **DAYLIGHT REVEALS PERFECTION – WE DELIVER IT.** --- **Why Choose Jay’s?** ✅ **Self-Sufficient Service**: No need for outlets or water hookups – *we bring everything*. ✅ **Science-Backed Clean**: Deionized water prevents streaks; Bio-Bomb tech annihilates odors. ✅ **Tornador Z-007 in Basic**: Precision cleaning for vents, seams, and hidden areas. ✅ **Customizable Luxury**: From pet hair to polishing chrome, we cater to *your* vision. ✅**Every employee of Jay’s Mobile Starts Woth The Letter J . If a Jay didn’t wash it, then it ain’t washed right ;) ✨ ***************CALL JAY TODAY************** Www. Jaysmobilewash.com - Follow Up Deployment
fa290be verified | <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AI Call Manager</title> | |
| <script src="https://cdn.tailwindcss.com/3.4.0"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"> | |
| <script src="https://sdk.vercel.app/external_api.js"></script> <!-- updated Jitsi URL --> | |
| <script src="https://cdn.apple-mapkit.com/mk/5.x/mapkit.js"></script> | |
| <!-- Added Pusher for real-time chat --> | |
| <script src="https://js.pusher.com/8.2.0/pusher.min.js"></script> | |
| <style> | |
| @keyframes pulse-ring { | |
| 0% { transform: scale(0.33); } | |
| 80%, 100% { opacity: 0; } | |
| } | |
| @keyframes pulse-dot { | |
| 0% { transform: scale(0.8); } | |
| 50% { transform: scale(1); } | |
| 100% { transform: scale(0.8); } | |
| } | |
| /* Custom scrollbar */ | |
| .custom-scrollbar::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| .custom-scrollbar::-webkit-scrollbar-track { | |
| background: rgba(156, 163, 175, 0.1); | |
| } | |
| .custom-scrollbar::-webkit-scrollbar-thumb { | |
| background: rgba(99, 102, 241, 0.5); | |
| border-radius: 3px; | |
| } | |
| /* Call animation */ | |
| .call-animation { | |
| position: relative; | |
| width: 50px; | |
| height: 50px; | |
| } | |
| .call-animation:before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(16, 185, 129, 0.5); | |
| border-radius: 50%; | |
| animation: pulse-ring 1.5s cubic-bezier(0.215, 0.61, 0.355, 1) infinite; | |
| } | |
| .call-animation:after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: #10B981; | |
| border-radius: 50%; | |
| transform: scale(0.8); | |
| animation: pulse-dot 1.5s cubic-bezier(0.455, 0.03, 0.515, 0.955) -0.4s infinite; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen font-sans"> | |
| <div class="container mx-auto px-4 py-8 max-w-md"> | |
| <!-- Header --> | |
| <header class="flex justify-between items-center mb-8"> | |
| <h1 class="text-2xl font-bold text-indigo-700">AI Call Manager</h1> | |
| <div class="flex space-x-2"> | |
| <button id="checkSystemBtn" class="p-2 rounded-full bg-green-100 hover:bg-green-200 transition" title="Check System Status"> | |
| <i class="fas fa-check-circle text-green-600"></i> | |
| </button> | |
| <button id="setupHelpBtn" class="p-2 rounded-full bg-blue-100 hover:bg-blue-200 transition" title="Setup Instructions"> | |
| <i class="fas fa-question text-blue-600"></i> | |
| </button> | |
| <button id="settingsBtn" class="p-2 rounded-full bg-gray-200 hover:bg-gray-300 transition"> | |
| <i class="fas fa-cog text-gray-600"></i> | |
| </button> | |
| <button id="systemTestBtn" class="p-2 rounded-full bg-orange-100 hover:bg-orange-200 transition" title="Test System"> | |
| <i class="fas fa-vial text-orange-600"></i> | |
| </button> | |
| </div> | |
| </header> | |
| <!-- Main Dashboard --> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-6"> | |
| <div class="p-6"> | |
| <div class="flex items-center mb-6"> | |
| <div class="call-animation mr-4"></div> | |
| <div> | |
| <h2 class="text-lg font-semibold text-gray-800">Call Management</h2> | |
| <p class="text-sm text-gray-500">Active for: <span class="font-medium phone-number-display">(562) 228-9429</span></p> | |
| </div> | |
| </div> | |
| <!-- Call Management Section --> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Answer Calls After</label> | |
| <select id="callDelay" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <option value="15">15 seconds</option> | |
| <option value="30" selected>30 seconds</option> | |
| <option value="45">45 seconds</option> | |
| <option value="60">1 minute</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="flex items-center space-x-3"> | |
| <input type="checkbox" id="enableCallAI" class="rounded h-5 w-5 text-indigo-600 focus:ring-indigo-500" checked> | |
| <span class="text-sm font-medium text-gray-700">Enable Call AI Assistant</span> | |
| </label> | |
| <p class="mt-1 text-xs text-gray-500">AI will answer calls when you're unavailable</p> | |
| </div> | |
| <div id="callSettings" class="pl-8"> | |
| <div class="mt-3"> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">AI Voice</label> | |
| <select class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <option>American Female (Emma)</option> | |
| <option>American Male (Brian)</option> | |
| <option>British Female (Amy)</option> | |
| </select> | |
| </div> | |
| <div class="mt-3"> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Default Message</label> | |
| <textarea id="callGreeting" rows="2" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" placeholder="Hello, I'm unable to take your call right now. Please leave a message or send a text and I'll get back to you soon."></textarea> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Make a Call Section --> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-6"> | |
| <div class="p-6"> | |
| <div class="flex items-center mb-6"> | |
| <div class="w-12 h-12 flex items-center justify-center bg-blue-100 rounded-full mr-4"> | |
| <i class="fas fa-phone text-blue-600 text-xl"></i> | |
| </div> | |
| <div> | |
| <h2 class="text-lg font-semibold text-gray-800">Make a Call</h2> | |
| <p class="text-sm text-gray-500">Train AI with interactive calls</p> | |
| </div> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Phone Number to Call</label> | |
| <input type="tel" id="callNumber" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="e.g. (562) 228-9429" value="15622289429"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Call Purpose</label> | |
| <select id="callPurpose" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
| <option value="training">AI Training Session</option> | |
| <option value="outgoing">Regular Outgoing Call</option> | |
| </select> | |
| </div> | |
| <div id="trainingOptions" class="pl-4 space-y-3 hidden"> | |
| <label class="flex items-center space-x-3"> | |
| <input type="checkbox" id="enableCallTraining" class="rounded h-4 w-4 text-blue-600 focus:ring-blue-500" checked> | |
| <span class="text-sm font-medium text-gray-700">Enable Interactive Training</span> | |
| </label> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Training Mode</label> | |
| <select id="callTrainingMode" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"> | |
| <option value="qa">Q&A Training</option> | |
| <option value="conversation">Conversation Practice</option> | |
| <option value="scenario">Scenario Simulation</option> | |
| </select> | |
| </div> | |
| </div> | |
| <button id="startCallBtn" class="w-full py-3 px-4 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition flex items-center justify-center space-x-2"> | |
| <i class="fas fa-phone"></i> | |
| <span>Start Call</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Text AI Section --> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-6"> | |
| <div class="p-6"> | |
| <div class="flex items-center mb-6"> | |
| <div class="w-12 h-12 flex items-center justify-center bg-purple-100 rounded-full mr-4"> | |
| <i class="fas fa-comment-alt text-purple-600 text-xl"></i> | |
| </div> | |
| <div> | |
| <h2 class="text-lg font-semibold text-gray-800">Text AI Assistant</h2> | |
| <p class="text-sm text-gray-500">Smart responses & automated replies</p> | |
| </div> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="flex items-center space-x-3"> | |
| <input type="checkbox" id="enableTextAI" class="rounded h-5 w-5 text-purple-600 focus:ring-purple-500" checked> | |
| <span class="text-sm font-medium text-gray-700">Enable Text AI Assistant</span> | |
| </label> | |
| <p class="mt-1 text-xs text-gray-500">AI will respond to unanswered texts automatically</p> | |
| </div> | |
| <div id="textSettings" class="pl-8 space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Response Style</label> | |
| <select id="responseStyle" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500"> | |
| <option value="professional">Professional</option> | |
| <option value="friendly" selected>Friendly</option> | |
| <option value="concise">Concise</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Auto-response Delay</label> | |
| <select id="textDelay" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500"> | |
| <option value="1">1 hour</option> | |
| <option value="2" selected>2 hours</option> | |
| <option value="4">4 hours</option> | |
| <option value="6">6 hours</option> | |
| <option value="12">12 hours</option> | |
| </select> | |
| </div> | |
| <div class="pt-2"> | |
| <button id="trainAIbtn" class="w-full py-2 px-4 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transition flex items-center justify-center space-x-2"> | |
| <i class="fas fa-brain"></i> | |
| <span>Train Text AI</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Stats Section --> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-6"> | |
| <div class="p-6"> | |
| <h2 class="text-lg font-semibold text-gray-800 mb-4">Usage Statistics</h2> | |
| <div class="grid grid-cols-2 gap-4"> | |
| <div class="bg-indigo-50 p-3 rounded-lg"> | |
| <p class="text-xs text-indigo-600 font-medium">Calls Managed</p> | |
| <p class="text-2xl font-bold text-indigo-700" id="callStatsCount">128</p> | |
| </div> | |
| <div class="bg-purple-50 p-3 rounded-lg"> | |
| <p class="text-xs text-purple-600 font-medium">Texts Responded</p> | |
| <p class="text-2xl font-bold text-purple-700" id="textStatsCount">342</p> | |
| </div> | |
| <div class="bg-green-50 p-3 rounded-lg"> | |
| <p class="text-xs text-green-600 font-medium">AI Confidence</p> | |
| <p class="text-2xl font-bold text-green-700">89%</p> | |
| </div> | |
| <div class="bg-yellow-50 p-3 rounded-lg"> | |
| <p class="text-xs text-yellow-600 font-medium">Training Cycles</p> | |
| <p class="text-2xl font-bold text-yellow-700">24</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Call Log Preview --> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden"> | |
| <div class="p-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-lg font-semibold text-gray-800">Recent Activity</h2> | |
| <button class="text-xs text-indigo-600 hover:text-indigo-800">View All</button> | |
| </div> | |
| <div class="space-y-3 max-h-60 overflow-y-auto custom-scrollbar"> | |
| <div class="flex items-start py-2 border-b border-gray-100"> | |
| <div class="flex-shrink-0 h-10 w-10 rounded-full bg-red-100 flex items-center justify-center mr-3"> | |
| <i class="fas fa-phone-alt text-red-500"></i> | |
| </div> | |
| <div class="flex-1"> | |
| <p class="text-sm font-medium text-gray-800">Missed Call</p> | |
| <p class="text-xs text-gray-500">From: (562) 555-0134</p> | |
| <p class="text-xs text-gray-500">14 min ago - AI answered after 30s</p> | |
| </div> | |
| </div> | |
| <div class="flex items-start py-2 border-b border-gray-100"> | |
| <div class="flex-shrink-0 h-10 w-10 rounded-full bg-green-100 flex items-center justify-center mr-3"> | |
| <i class="fas fa-sms text-green-500"></i> | |
| </div> | |
| <div class="flex-1"> | |
| <p class="text-sm font-medium text-gray-800">Text Response</p> | |
| <p class="text-xs text-gray-500">To: (562) 555-0198</p> | |
| <p class="text-xs text-gray-500">1 hour ago - AI confidence: 92%</p> | |
| </div> | |
| </div> | |
| <div class="flex items-start py-2 border-b border-gray-100"> | |
| <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center mr-3"> | |
| <i class="fas fa-phone-alt text-blue-500"></i> | |
| </div> | |
| <div class="flex-1"> | |
| <p class="text-sm font-medium text-gray-800">AI Call</p> | |
| <p class="text-xs text-gray-500">With: (562) 555-0163 (2m 43s)</p> | |
| <p class="text-xs text-gray-500">3 hours ago - Scheduled follow-up</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Container for Jitsi meetings (hidden by default) --> | |
| <div id="jitsi-container" style="display: none;"></div> | |
| <!-- Training Modal --> | |
| <div id="trainingModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 hidden"> | |
| <div class="bg-white rounded-xl shadow-xl w-full max-w-md max-h-screen overflow-y-auto"> | |
| <div class="p-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h3 class="text-lg font-semibold text-gray-900">Train Your Text AI</h3> | |
| <button id="closeTraining" class="text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="space-y-4"> | |
| <div class="p-4 bg-gray-50 rounded-lg"> | |
| <p class="text-sm text-gray-700 mb-2">AI Knowledge Level: <span class="font-medium">Intermediate</span></p> | |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> | |
| <div class="bg-purple-600 h-2.5 rounded-full" style="width: 65%"></div> | |
| </div> | |
| </div> | |
| <div> | |
| <p class="text-sm font-medium text-gray-700 mb-2">Training Mode</p> | |
| <div class="grid grid-cols-2 gap-2"> | |
| <button class="trainingModeBtn py-2 px-3 border border-gray-300 rounded-lg text-sm font-medium bg-white hover:bg-gray-50" data-mode="qa">Q&A Training</button> | |
| <button class="trainingModeBtn py-2 px-3 border border-gray-300 rounded-lg text-sm font-medium bg-white hover:bg-gray-50" data-mode="examples">Teach with Examples</button> | |
| <button class="trainingModeBtn py-2 px-3 border border-gray-300 rounded-lg text-sm font-medium bg-white hover:bg-gray-50" data-mode="keywords">Keyword Responses</button> | |
| <button class="trainingModeBtn py-2 px-3 border border-gray-300 rounded-lg text-sm font-medium bg-white hover:bg-gray-50" data-mode="prefs">Style Preferences</button> | |
| </div> | |
| </div> | |
| <!-- Q&A Training Section --> | |
| <div id="qaTraining" class="trainingSection hidden"> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Question to Teach</label> | |
| <input type="text" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 text-sm" placeholder="E.g. What are your business hours?"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Preferred Response</label> | |
| <textarea class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 text-sm" rows="3" placeholder="Our business hours are Monday to Friday, 9am to 5pm PST."></textarea> | |
| </div> | |
| <button class="w-full py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg text-sm"> | |
| Add to AI Knowledge | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Examples Training Section --> | |
| <div id="examplesTraining" class="trainingSection hidden"> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Example Conversation</label> | |
| <textarea class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 text-sm" rows="5" placeholder="Client: When can I expect my order to arrive? | |
| You: Your order will ship within 24 hours and typically arrives in 2-3 business days. We'll send tracking information once it's on the way."></textarea> | |
| </div> | |
| <button class="w-full py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg text-sm"> | |
| Analyze for Patterns | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Keywords Training Section --> | |
| <div id="keywordsTraining" class="trainingSection hidden"> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Trigger Word/Phrase</label> | |
| <input type="text" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 text-sm" placeholder="E.g. refund, return policy"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Response for This Keyword</label> | |
| <textarea class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 text-sm" rows="3" placeholder="Our return policy allows for returns within 30 days of purchase..."></textarea> | |
| </div> | |
| <button class="w-full py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg text-sm"> | |
| Add Keyword Response | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Preferences Training Section --> | |
| <div id="prefsTraining" class="trainingSection hidden"> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Communication Style</label> | |
| <select class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 text-sm"> | |
| <option>Professional</option> | |
| <option selected>Friendly</option> | |
| <option>Concise</option> | |
| <option>Detailed</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Common Phrases You Use</label> | |
| <textarea class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 text-sm" rows="3" placeholder="E.g. Thanks for reaching out!, Happy to help!, Let me check that for you..."></textarea> | |
| </div> | |
| <button class="w-full py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg text-sm"> | |
| Update Preferences | |
| </button> | |
| </div> | |
| </div> | |
| <div class="pt-4 border-t border-gray-200"> | |
| <h4 class="text-sm font-medium text-gray-900 mb-2">AI Test Area</h4> | |
| <div class="mb-2"> | |
| <input type="text" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 text-sm" placeholder="Test message for AI to respond to" id="testMessage"> | |
| </div> | |
| <button onclick="testAIResponse()" class="w-full py-2 bg-gray-200 hover:bg-gray-300 rounded-lg text-sm mb-4"> | |
| Test AI Response | |
| </button> | |
| <div id="aiResponse" class="hidden bg-gray-50 p-3 rounded-lg"> | |
| <p class="text-sm text-gray-800">This is where the AI's response would appear based on its current training.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Settings Modal --> | |
| <div id="settingsModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 hidden"> | |
| <div class="bg-white rounded-xl shadow-xl w-full max-w-md max-h-screen overflow-y-auto"> | |
| <div class="p-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h3 class="text-lg font-semibold text-gray-900">Settings</h3> | |
| <button id="closeSettings" class="text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Your Phone Number</label> | |
| <input type="tel" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 text-sm" value="(562) 228-9429"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">iMessage Integration</label> | |
| <div class="mt-1 flex items-center"> | |
| <input type="checkbox" id="enableIMessage" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" checked> | |
| <label for="enableIMessage" class="ml-2 block text-sm text-gray-700">Enable iPhone Message Handling</label> | |
| </div> | |
| <p class="mt-1 text-xs text-gray-500">Verified number: 15622289429</p> | |
| </div> | |
| <div id="imessageSettings" class="pl-6 space-y-3"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Backup Phone Number</label> | |
| <input type="tel" class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 text-sm" placeholder="Optional"> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Memory Retention</label> | |
| <select class="w-full py-2 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 text-sm"> | |
| <option>7 days</option> | |
| <option>14 days</option> | |
| <option selected>30 days</option> | |
| <option>90 days</option> | |
| <option>Indefinitely</option> | |
| </select> | |
| <p class="mt-1 text-xs text-gray-500">How long should AI remember conversation history?</p> | |
| </div> | |
| <div> | |
| <label class="flex items-center space-x-3"> | |
| <input type="checkbox" checked class="rounded h-4 w-4 text-indigo-600 focus:ring-indigo-500"> | |
| <span class="text-sm font-medium text-gray-700">Daily Training Reminders</span> | |
| </label> | |
| </div> | |
| <div class="pt-4 border-t border-gray-200"> | |
| <h4 class="text-sm font-medium text-gray-900 mb-2">AI Advanced Settings</h4> | |
| <div class="mb-2"> | |
| <label class="block text-xs font-medium text-gray-700 mb-1">Creativity Level</label> | |
| <input type="range" min="0" max="10" value="7" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
| <div class="flex justify-between text-xs text-gray-500 px-1"> | |
| <span>Precise</span> | |
| <span>Balanced</span> | |
| <span>Creative</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex space-x-3 pt-4"> | |
| <button class="flex-1 py-2 bg-gray-200 hover:bg-gray-300 rounded-lg text-sm"> | |
| Reset Training | |
| </button> | |
| <button class="flex-1 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-sm"> | |
| Save Settings | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Jitsi Client Setup | |
| let jitsiClient; | |
| let callSocket; | |
| async function initializeJitsiClient() { | |
| try { | |
| // Initialize Jitsi Meet API client | |
| jitsiClient = { | |
| makeCall: async (options) => { | |
| return new Promise((resolve) => { | |
| console.log('Initiating Jitsi call to:', options.to); | |
| const domain = 'meet.jit.si'; | |
| const options = { | |
| roomName: `call-${Date.now()}`, | |
| parentNode: document.querySelector('#jitsi-container'), | |
| userInfo: { | |
| displayName: 'AI Call Manager' | |
| } | |
| }; | |
| // Create Jitsi Meet API instance | |
| const api = new JitsiMeetExternalAPI(domain, options); | |
| setTimeout(() => resolve({id: `jitsi-${Date.now()}`, api}), 1000); | |
| }); | |
| } | |
| }; | |
| // Use a simple WebSocket connection (you would replace with your own endpoint) | |
| callSocket = new WebSocket('wss://realtime.trillion.ventures/ws'); | |
| setTimeout(() => { | |
| if (callSocket.onopen) callSocket.onopen(); | |
| }, 500); | |
| callSocket.onopen = () => { | |
| console.log('Connected to Trillion real-time service'); | |
| callSocket.send(JSON.stringify({ | |
| type: 'authenticate', | |
| token: authToken | |
| })); | |
| }; | |
| callSocket.onmessage = handleSocketMessage; | |
| callSocket.onclose = () => console.log('Disconnected from real-time service'); | |
| return true; | |
| } catch (error) { | |
| console.error("Failed to initialize Trillion client:", error); | |
| return false; | |
| } | |
| } | |
| function handleSocketMessage(event) { | |
| const data = JSON.parse(event.data); | |
| switch(data.type) { | |
| case 'call_update': | |
| updateCallUI(data.status, data.transcript); | |
| break; | |
| case 'message': | |
| console.log("New message:", data.content); | |
| break; | |
| case 'call_ended': | |
| endCallUI(data.callId); | |
| break; | |
| } | |
| } | |
| async function sendSMS(to, message) { | |
| try { | |
| const response = await fetch('/api/send-sms', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${localStorage.getItem('authToken')}` | |
| }, | |
| body: JSON.stringify({ to, message }) | |
| }); | |
| if (!response.ok) throw new Error('Failed to send SMS'); | |
| return await response.json(); | |
| } catch (error) { | |
| console.error('SMS send error:', error); | |
| throw error; | |
| } | |
| } | |
| // Save call stats when calls are made/ended | |
| function updateCallStats() { | |
| const callStats = parseInt(localStorage.getItem('callStats')) || 128; | |
| localStorage.setItem('callStats', callStats + 1); | |
| document.querySelector('#callStatsCount').textContent = callStats + 1; | |
| } | |
| // AI Response Generation | |
| async function generateAIResponse(message, context = {}) { | |
| try { | |
| const response = await fetch('/api/generate-ai-response', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${localStorage.getItem('authToken')}` | |
| }, | |
| body: JSON.stringify({ | |
| message, | |
| style: document.getElementById('responseStyle').value, | |
| context | |
| }) | |
| }); | |
| if (!response.ok) throw new Error('AI response failed'); | |
| return await response.json(); | |
| } catch (error) { | |
| console.error('AI error:', error); | |
| return { response: "I'm having trouble generating a response right now. Please try again later." }; | |
| } | |
| } | |
| // Toggle settings visibility | |
| document.getElementById('enableCallAI').addEventListener('change', function() { | |
| document.getElementById('callSettings').style.display = this.checked ? 'block' : 'none'; | |
| }); | |
| document.getElementById('enableTextAI').addEventListener('change', function() { | |
| document.getElementById('textSettings').style.display = this.checked ? 'block' : 'none'; | |
| }); | |
| document.getElementById('enableIMessage').addEventListener('change', function() { | |
| document.getElementById('imessageSettings').style.display = this.checked ? 'block' : 'none'; | |
| if (this.checked) { | |
| connectIMessage(); | |
| } | |
| }); | |
| // Training modal | |
| const trainingModal = document.getElementById('trainingModal'); | |
| const trainBtn = document.getElementById('trainAIbtn'); | |
| const closeTraining = document.getElementById('closeTraining'); | |
| trainBtn.addEventListener('click', () => { | |
| trainingModal.classList.remove('hidden'); | |
| showTrainingSection('qa'); // Default to Q&A section | |
| }); | |
| closeTraining.addEventListener('click', () => { | |
| trainingModal.classList.add('hidden'); | |
| }); | |
| // Settings modal | |
| const settingsModal = document.getElementById('settingsModal'); | |
| const settingsBtn = document.getElementById('settingsBtn'); | |
| const closeSettings = document.getElementById('closeSettings'); | |
| settingsBtn.addEventListener('click', () => { | |
| settingsModal.classList.remove('hidden'); | |
| }); | |
| closeSettings.addEventListener('click', () => { | |
| settingsModal.classList.add('hidden'); | |
| }); | |
| // Training sections | |
| const trainingModeBtns = document.querySelectorAll('.trainingModeBtn'); | |
| trainingModeBtns.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| const mode = btn.dataset.mode; | |
| showTrainingSection(mode); | |
| // Update active button style | |
| trainingModeBtns.forEach(b => b.classList.remove('bg-purple-100', 'border-purple-300', 'text-purple-800')); | |
| btn.classList.add('bg-purple-100', 'border-purple-300', 'text-purple-800'); | |
| }); | |
| }); | |
| function showTrainingSection(sectionId) { | |
| document.querySelectorAll('.trainingSection').forEach(section => { | |
| section.classList.add('hidden'); | |
| }); | |
| document.getElementById(sectionId + 'Training').classList.remove('hidden'); | |
| } | |
| // Call functionality with Twilio API integration | |
| const callPurpose = document.getElementById('callPurpose'); | |
| const trainingOptions = document.getElementById('trainingOptions'); | |
| callPurpose.addEventListener('change', function() { | |
| trainingOptions.style.display = this.value === 'training' ? 'block' : 'none'; | |
| }); | |
| document.getElementById('startCallBtn').addEventListener('click', async function() { | |
| const number = document.getElementById('callNumber').value.trim(); | |
| if (!number) { | |
| alert('Please enter a phone number'); | |
| return; | |
| } | |
| const isTraining = callPurpose.value === 'training'; | |
| const trainingMode = isTraining ? document.getElementById('callTrainingMode').value : null; | |
| const enableTraining = isTraining ? document.getElementById('enableCallTraining').checked : false; | |
| try { | |
| if (!trillionClient) { | |
| const authToken = localStorage.getItem('authToken'); | |
| if (!authToken) { | |
| throw new Error('Not authenticated'); | |
| } | |
| await initializeTrillionClient(authToken); | |
| } | |
| const callOptions = { | |
| to: number, | |
| video: false | |
| }; | |
| const call = await jitsiClient.makeCall(callOptions); | |
| console.log('Call initiated:', call.id); | |
| // Subscribe to call updates | |
| callSocket.send(JSON.stringify({ | |
| type: 'subscribe_call', | |
| callId: call.id | |
| })); | |
| showCallInterface(number, isTraining, trainingMode, call.id); | |
| } catch (error) { | |
| console.error('Call error:', error); | |
| alert(`Call failed: ${error.message}`); | |
| } | |
| }); | |
| // Ensure 15622289429 is properly configured | |
| function verifyNumberConfiguration() { | |
| const storedNum = localStorage.getItem('verifiedNumber'); | |
| if (storedNum !== '15622289429') { | |
| localStorage.setItem('verifiedNumber', '15622289429'); | |
| alert('Primary number has been updated to 15622289429'); | |
| } | |
| // Verify Twilio is properly linked | |
| if (!localStorage.getItem('twilioSID')) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| // Load saved settings on page load | |
| // Advanced AI Persona Configuration | |
| const aiPersona = { | |
| name: "Jay's Mobile Wash Assistant", | |
| tone: "friendly/professional", | |
| knowledge: { | |
| packages: { | |
| basic: { | |
| car: 60, | |
| suv: 70, | |
| includes: "2-Step Hand Wash, Tornador Z-007 Blast, Ceramic Rim Cleaning, Interior Wipe-Down" | |
| }, | |
| luxury: { | |
| car: 130, | |
| suv: 140, | |
| includes: "Basic Package + Ceramic Spray Wax, SiO₂ Interior Cleaner, Vinyl Restoration" | |
| }, | |
| max: { | |
| car: 200, | |
| suv: 210, | |
| includes: "Luxury Package + Graphene Wax, Steam Sanitization, Leather Conditioning, Bio-Bomb Odor Removal" | |
| } | |
| }, | |
| addons: [ | |
| "Ceramic Coating (2+ years)", | |
| "Polish + Scratch Removal", | |
| "Pet Hair Removal", | |
| "Rim De-Iron", | |
| "Engine Bay Detailing", | |
| "Trim Restoration" | |
| ], | |
| policies: { | |
| surcharges: "30% for heavy dirt, 50% for biohazards, $50 for after-dark work", | |
| guarantee: "Daylight only, full disclosure required", | |
| payment: "1.75% card fee, $10 travel fee per 10 miles beyond 30" | |
| }, | |
| contact: "562-228-9429 | www.jaysmobilewash.com" | |
| }, | |
| capabilities: { | |
| research: true, | |
| memory: true, | |
| continuousLearning: true | |
| } | |
| }; | |
| // Initialize speech recognition | |
| const speechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; | |
| const recognition = speechRecognition ? new speechRecognition() : null; | |
| if (recognition) { | |
| recognition.lang = 'en-US'; | |
| recognition.interimResults = false; | |
| recognition.maxAlternatives = 1; | |
| } | |
| document.addEventListener('DOMContentLoaded', function() { | |
| verifyNumberConfiguration(); | |
| // System Test Modal | |
| const testModal = document.getElementById('testModal'); | |
| const testBtn = document.getElementById('systemTestBtn'); | |
| const closeTest = document.getElementById('closeTest'); | |
| testBtn.addEventListener('click', () => { | |
| testModal.classList.remove('hidden'); | |
| }); | |
| closeTest.addEventListener('click', () => { | |
| testModal.classList.add('hidden'); | |
| }); | |
| // Add "How to Fix" button handler | |
| document.getElementById('howToFixBtn').addEventListener('click', function() { | |
| if (!verifyNumberConfiguration()) { | |
| alert(`To fix call/message handling for 15622289429: | |
| 1. Go to Settings > Phone Integration | |
| 2. Enter your Twilio credentials: | |
| - Account SID: your_twilio_sid | |
| - Auth Token: your_twilio_token | |
| - Phone Number: 15622289429 | |
| 3. Save & restart the app | |
| Or contact support at help@trillion.ventures`); | |
| } else { | |
| alert('System is properly configured for number 15622289429!'); | |
| } | |
| }); | |
| // Rest of DOMContentLoaded... | |
| async function connectIMessage() { | |
| try { | |
| const appleConfig = { | |
| identifier: 'com.trillion.aicallmanager', | |
| services: ['messages'] | |
| }; | |
| const connection = await window.MapKit.init({ | |
| authorizationCallback: done => { | |
| done(localStorage.getItem('appleJWT')); | |
| } | |
| }); | |
| connection.messages.addEventListener('messageReceived', (event) => { | |
| const { message, sender } = event; | |
| handleIncomingMessage({ | |
| from: sender.handle, | |
| message: message.text, | |
| source: 'imessage' | |
| }); | |
| }); | |
| return true; | |
| } catch (error) { | |
| console.error('iMessage connection failed:', error); | |
| return false; | |
| } | |
| } | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Check if setup was already completed | |
| const isSetupComplete = localStorage.getItem('setupComplete') === 'true'; | |
| if (isSetupComplete) { | |
| document.getElementById('setupHelpBtn').classList.remove('bg-blue-100', 'hover:bg-blue-200'); | |
| document.getElementById('setupHelpBtn').classList.add('bg-gray-200', 'cursor-not-allowed'); | |
| document.getElementById('setupHelpBtn').title = 'Setup already completed'; | |
| } | |
| const savedSettings = localStorage.getItem('aiCallSettings'); | |
| if (savedSettings) { | |
| const config = JSON.parse(savedSettings); | |
| // Update phone number displays | |
| document.querySelectorAll('.phone-number-display').forEach(el => { | |
| el.textContent = config.twilioNum || config.phoneNum || '+1 (858) 263-4276'; | |
| }); | |
| // Update stats section with saved data | |
| document.querySelector('#callStatsCount').textContent = localStorage.getItem('callStats') || '128'; | |
| document.querySelector('#textStatsCount').textContent = localStorage.getItem('textStats') || '342'; | |
| } | |
| }); | |
| // Function to update call UI with real data | |
| function updateCallUI(status, transcript) { | |
| const statusElement = document.querySelector('#callStatus'); | |
| if (statusElement) { | |
| statusElement.textContent = status; | |
| } | |
| const transcriptElement = document.querySelector('#callTranscript'); | |
| if (transcriptElement) { | |
| transcriptElement.textContent = transcript; | |
| } | |
| } | |
| // Call interface simulation | |
| function showCallInterface(number, isTraining, trainingMode) { | |
| const callModal = document.createElement('div'); | |
| callModal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50'; | |
| callModal.innerHTML = ` | |
| <div class="bg-white rounded-xl shadow-lg w-full max-w-sm"> | |
| <div class="p-6"> | |
| <div class="text-center mb-4"> | |
| <div class="call-animation mx-auto mb-4"></div> | |
| <h3 class="text-lg font-medium text-gray-900">Calling ${number}</h3> | |
| <p class="text-sm text-gray-500">${isTraining ? 'AI Training Session' : 'Outgoing Call'}</p> | |
| </div> | |
| ${isTraining ? ` | |
| <div class="mb-4"> | |
| <p class="text-sm font-medium text-gray-700 mb-1">Training Mode: ${trainingMode}</p> | |
| <div class="bg-gray-50 p-3 rounded-lg"> | |
| <p class="text-sm text-gray-700">AI will ask questions and learn from your responses.</p> | |
| </div> | |
| </div>` : ''} | |
| <div class="mb-6"> | |
| <p class="text-sm font-medium text-gray-700 mb-1">Current AI Knowledge Level</p> | |
| <div class="w-full bg-gray-200 rounded-full h-2.5 mb-1"> | |
| <div class="bg-blue-600 h-2.5 rounded-full" style="width: 65%"></div> | |
| </div> | |
| <p class="text-xs text-gray-500">Understanding of your speech patterns: <span class="font-medium">Medium</span></p> | |
| </div> | |
| <div class="grid grid-cols-3 gap-2"> | |
| <button class="callBtn py-3 px-4 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-microphone text-gray-700"></i> | |
| </button> | |
| <button class="callBtn py-3 px-4 bg-green-500 hover:bg-green-600 text-white rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-check"></i> | |
| </button> | |
| <button class="callBtn py-3 px-4 bg-red-500 hover:bg-red-600 text-white rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| <button class="callBtn py-3 px-4 bg-blue-50 hover:bg-blue-100 rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-brain text-blue-700"></i> | |
| </button> | |
| <button class="callBtn py-3 px-4 bg-blue-500 hover:bg-blue-600 text-white rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-comment"></i> | |
| </button> | |
| <button class="callBtn py-3 px-4 bg-blue-50 hover:bg-blue-100 rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-history text-blue-700"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| document.body.appendChild(callModal); | |
| // End call button | |
| const endCallBtn = callModal.querySelector('.bg-red-500'); | |
| endCallBtn.addEventListener('click', async function() { | |
| try { | |
| await trillionClient.calls.end(callId); | |
| document.body.removeChild(callModal); | |
| if (isTraining) { | |
| alert('AI training session completed. New knowledge added to memory.'); | |
| } | |
| } catch (error) { | |
| console.error('Error ending call:', error); | |
| alert('Error ending call'); | |
| } | |
| }); | |
| // In a real app, would have websockets or similar for real-time call interaction | |
| } | |
| async function testAIResponse() { | |
| const testMessage = document.getElementById('testMessage').value; | |
| const responseBox = document.getElementById('aiResponse'); | |
| if (testMessage.trim() === '') { | |
| alert('Please enter a message to test'); | |
| return; | |
| } | |
| try { | |
| responseBox.querySelector('p').textContent = "Generating AI response..."; | |
| responseBox.classList.remove('hidden'); | |
| const aiResponse = await generateAIResponse(testMessage); | |
| responseBox.querySelector('p').textContent = aiResponse.response; | |
| } catch (error) { | |
| responseBox.querySelector('p').textContent = "Error generating response. Please try again."; | |
| console.error(error); | |
| } | |
| } | |
| // Close modals when clicking outside | |
| window.addEventListener('click', (event) => { | |
| if (event.target === trainingModal) { | |
| trainingModal.classList.add('hidden'); | |
| } | |
| if (event.target === settingsModal) { | |
| settingsModal.classList.add('hidden'); | |
| } | |
| }); | |
| }); | |
| // Integrated webhook for all SMS sources | |
| app.post('/api/message-webhook', async (req, res) => { | |
| const { source, from, message } = req.body; | |
| // Validate it's for the correct number | |
| if (from.includes('15622289429')) { | |
| // Handle iMessage specifically | |
| if (source === 'imessage') { | |
| // Process with Apple Business Chat API | |
| try { | |
| const imResponse = await appleChatAPI.send({ | |
| to: from, | |
| message: `ACK: ${message}`, // temp ack | |
| type: 'text' | |
| }); | |
| } catch (e) { | |
| console.error('iMessage error:', e); | |
| } | |
| } | |
| // Trigger event to frontend | |
| pusher.trigger(`incoming_15622289429`, 'new_message', { | |
| from, | |
| message, | |
| source | |
| }); | |
| } | |
| res.status(200).send(); | |
| }); | |
| // AI Response Generation using HuggingFace | |
| app.post('/api/generate-response', async (req, res) => { | |
| const { message } = req.body; | |
| try { | |
| const response = await hf.textGeneration({ | |
| model: 'facebook/blenderbot-400M-distill', | |
| inputs: message, | |
| parameters: { | |
| max_new_tokens: 150, | |
| temperature: 0.7 | |
| } | |
| }); | |
| res.json({ response: response.generated_text }); | |
| } catch (error) { | |
| console.error(error); | |
| res.status(500).json({ error: 'Failed to generate response' }); | |
| } | |
| }); | |
| // Test functions | |
| document.getElementById('testCallBtn').addEventListener('click', async function() { | |
| const resultEl = document.getElementById('testResultText'); | |
| resultEl.textContent = "Initiating AI call test..."; | |
| document.getElementById('testResults').classList.remove('hidden'); | |
| try { | |
| // Simulate calling your number | |
| const response = await fetch('/api/test-call', { | |
| method: 'POST', | |
| body: JSON.stringify({ | |
| test: true, | |
| greeting: document.getElementById('greetingMessage').value | |
| }) | |
| }); | |
| resultEl.textContent = "AI answered successfully! Call is active for testing."; | |
| // Simulate AI learning capabilities | |
| setTimeout(() => { | |
| resultEl.textContent += "\nAI is analyzing call patterns and learning..."; | |
| }, 2000); | |
| } catch (error) { | |
| resultEl.textContent = `Test failed: ${error.message}`; | |
| } | |
| }); | |
| document.getElementById('testTextBtn').addEventListener('click', async function() { | |
| const resultEl = document.getElementById('testResultText'); | |
| resultEl.textContent = "Testing AI text response system..."; | |
| document.getElementById('testResults').classList.remove('hidden'); | |
| try { | |
| const testMsg = "Hi, how much for a full detail with ceramic coating?"; | |
| const response = await generateAIResponse(testMsg, { | |
| service: "car_wash", | |
| context: aiPersona.knowledge | |
| }); | |
| resultEl.textContent = `AI Response: ${response.response}\n\nContext used: ${JSON.stringify(aiPersona.knowledge)}`; | |
| } catch (error) { | |
| resultEl.textContent = `Test failed: ${error.message}`; | |
| } | |
| }); | |
| // Enhanced AI learning capabilities | |
| function enhanceAILearning() { | |
| // Enable verbal training | |
| const verbalTrainingBtn = document.createElement('button'); | |
| verbalTrainingBtn.className = 'w-full py-2 px-4 bg-green-600 hover:bg-green-700 text-white rounded-lg my-2'; | |
| verbalTrainingBtn.innerHTML = '<i class="fas fa-microphone mr-2"></i> Verbal Training Session'; | |
| verbalTrainingBtn.onclick = startVerbalTraining; | |
| document.querySelector('#trainingModal .space-y-4').appendChild(verbalTrainingBtn); | |
| // Add car wash knowledge base | |
| document.getElementById('addKnowledgeBtn').addEventListener('click', () => { | |
| addCarWashKnowledge(); | |
| }); | |
| } | |
| function startVerbalTraining() { | |
| if (!recognition) { | |
| alert("Speech recognition not supported in this browser"); | |
| return; | |
| } | |
| recognition.start(); | |
| alert("Speak your training phrases now..."); | |
| recognition.onresult = function(event) { | |
| const transcript = event.results[0][0].transcript; | |
| // Process training phrases | |
| analyzeTrainingPhrase(transcript); | |
| }; | |
| } | |
| function analyzeTrainingPhrase(phrase) { | |
| // Advanced NLP processing would happen here | |
| alert(`AI learned from your phrase: "${phrase}"`); | |
| // Update AI knowledge base | |
| if (phrase.includes("ceramic")) { | |
| aiPersona.knowledge.ceramicCoating = true; | |
| } | |
| if (phrase.includes("interior")) { | |
| aiPersona.knowledge.interiorDetailing = true; | |
| } | |
| } | |
| function addCarWashKnowledge() { | |
| const knowledge = { | |
| ceramicCoating: "Ceramic coating is a liquid polymer that chemically bonds with vehicle paint creating a protective layer. Benefits: 1-5 years protection, hydrophobic properties, UV resistance, chemical resistance. Cost: $500-$3000 depending on vehicle size and product quality.", | |
| clayBar: "Clay bar removes embedded contaminants from paint. Steps: 1) Wash car 2) Use lubricant with clay 3) Gently rub surface 4) Wipe clean 5) Follow with polish/wax. Essential before ceramic coating.", | |
| paintCorrection: "Multi-step process: 1) Wash & decontaminate 2) Compound (if needed) 3) Polish 4) Final polish 5) Protect. Removes swirls, scratches, oxidation. Grade scratches: Level 1 (clear coat only) - Level 3 (through base coat)." | |
| }; | |
| Object.assign(aiPersona.knowledge, knowledge); | |
| alert("Added comprehensive car wash knowledge base to AI!"); | |
| } | |
| // Enhanced call handling with learning | |
| async function handleIncomingCall() { | |
| const greeting = document.getElementById('greetingMessage').value; | |
| // Initialize advanced call handling | |
| const call = await jitsiClient.makeCall({ | |
| learningMode: true, | |
| greeting, | |
| context: aiPersona | |
| }); | |
| // Enable real-time learning | |
| callSocket.send(JSON.stringify({ | |
| type: 'enable_learning', | |
| callId: call.id | |
| })); | |
| } | |
| // Initialize all enhanced features | |
| document.addEventListener('DOMContentLoaded', function() { | |
| enhanceAILearning(); | |
| }); | |
| app.listen(3000, () => console.log('Server running on port 3000')); | |
| </script> | |
| <style> | |
| .wizard-step { | |
| display: none; | |
| } | |
| .wizard-step.active { | |
| display: block; | |
| animation: fadeIn 0.3s ease-out; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .wizard-progress { | |
| height: 4px; | |
| background: #e5e7eb; | |
| margin-bottom: 1.5rem; | |
| } | |
| .wizard-progress-bar { | |
| height: 100%; | |
| background: #4f46e5; | |
| transition: width 0.3s ease; | |
| } | |
| </style> | |
| <!-- Setup Wizard Modal --> | |
| <div id="setupHelpModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 hidden z-50"> | |
| <div class="bg-white rounded-xl shadow-xl w-full max-w-md max-h-screen overflow-y-auto"> | |
| <div class="p-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h3 class="text-lg font-semibold text-gray-900">Setup Wizard</h3> | |
| <button id="closeSetupHelp" class="text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="wizard-progress"> | |
| <div id="wizardProgressBar" class="wizard-progress-bar" style="width: 20%"></div> | |
| </div> | |
| <!-- Step 1: Welcome --> | |
| <div id="step1" class="wizard-step active"> | |
| <div class="text-center mb-6"> | |
| <div class="w-16 h-16 mx-auto bg-blue-100 rounded-full flex items-center justify-center mb-4"> | |
| <i class="fas fa-sparkles text-blue-600 text-2xl"></i> | |
| </div> | |
| <h3 class="text-lg font-semibold text-gray-900 mb-2">Welcome to AI Call Manager</h3> | |
| <p class="text-gray-600">Let's get you set up in just a few simple steps. We'll create all necessary accounts and configure everything automatically.</p> | |
| </div> | |
| <div class="flex justify-between mt-6"> | |
| <button class="wizard-next-btn w-full py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg">Get Started</button> | |
| </div> | |
| </div> | |
| <!-- Step 2: Credentials --> | |
| <div id="step2" class="wizard-step"> | |
| <h3 class="text-lg font-semibold text-gray-900 mb-4">Account Information</h3> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Email Address</label> | |
| <input type="email" id="setupEmail" class="w-full py-2 px-3 border border-gray-300 rounded-lg" value="jason@trillion.ventures" readonly> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Password</label> | |
| <div class="relative"> | |
| <input type="password" id="setupPassword" class="w-full py-2 px-3 border border-gray-300 rounded-lg" value="TrillionAI2024!" readonly> | |
| <button class="absolute right-3 top-2 text-gray-400 hover:text-gray-600"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="p-3 bg-blue-50 rounded-lg mt-2"> | |
| <p class="text-sm text-blue-700">We'll use these credentials to automatically create accounts with all required services.</p> | |
| </div> | |
| </div> | |
| <div class="flex justify-between mt-6"> | |
| <button class="wizard-prev-btn py-2 px-4 text-gray-700 hover:text-gray-900">Back</button> | |
| <button class="wizard-next-btn py-2 px-4 bg-blue-600 hover:bg-blue-700 text-white rounded-lg">Continue</button> | |
| </div> | |
| </div> | |
| <!-- Step 3: Automatic Setup --> | |
| <div id="step3" class="wizard-step"> | |
| <div class="text-center mb-6"> | |
| <div class="w-16 h-16 mx-auto bg-green-100 rounded-full flex items-center justify-center mb-4"> | |
| <i class="fas fa-robot text-green-600 text-2xl"></i> | |
| </div> | |
| <h3 class="text-lg font-semibold text-gray-900 mb-2">Automatic Setup</h3> | |
| <p class="text-gray-600">We'll now create accounts and configure everything needed.</p> | |
| </div> | |
| <div id="setupProgress" class="space-y-3 mb-6"> | |
| <div class="flex items-center"> | |
| <div id="setupTrillion" class="w-5 h-5 rounded-full border-2 border-gray-300 mr-2"></div> | |
| <span class="text-sm">Creating Trillion account...</span> | |
| </div> | |
| <div class="flex items-center"> | |
| <div id="setupPusher" class="w-5 h-5 rounded-full border-2 border-gray-300 mr-2"></div> | |
| <span class="text-sm">Setting up Pusher...</span> | |
| </div> | |
| <div class="flex items-center"> | |
| <div id="setupHuggingFace" class="w-5 h-5 rounded-full border-2 border-gray-300 mr-2"></div> | |
| <span class="text-sm">Configuring HuggingFace...</span> | |
| </div> | |
| <div class="flex items-center"> | |
| <div id="setupTwilio" class="w-5 h-5 rounded-full border-2 border-gray-300 mr-2"></div> | |
| <span class="text-sm">Connecting Twilio...</span> | |
| </div> | |
| <div class="flex items-center"> | |
| <div id="setupEnv" class="w-5 h-5 rounded-full border-2 border-gray-300 mr-2"></div> | |
| <span class="text-sm">Creating configuration files...</span> | |
| </div> | |
| </div> | |
| <div class="hidden" id="setupComplete"> | |
| <div class="p-3 bg-green-50 rounded-lg border border-green-200 mb-4"> | |
| <p class="text-sm text-green-700 font-medium">All services configured successfully!</p> | |
| </div> | |
| </div> | |
| <div class="flex justify-between mt-4"> | |
| <button class="wizard-prev-btn py-2 px-4 text-gray-700 hover:text-gray-900">Back</button> | |
| <button id="startSetupBtn" class="wizard-next-btn py-2 px-4 bg-green-600 hover:bg-green-700 text-white rounded-lg">Start Setup</button> | |
| <button id="finishBtn" class="py-2 px-4 bg-blue-600 hover:bg-blue-700 text-white rounded-lg hidden">Finish Setup</button> | |
| </div> | |
| </div> | |
| <!-- Step 4: Verification --> | |
| <div id="step4" class="wizard-step"> | |
| <div class="text-center mb-6"> | |
| <div class="w-16 h-16 mx-auto bg-purple-100 rounded-full flex items-center justify-center mb-4"> | |
| <i class="fas fa-check-circle text-purple-600 text-2xl"></i> | |
| </div> | |
| <h3 class="text-lg font-semibold text-gray-900 mb-2">Setup Complete!</h3> | |
| <p class="text-gray-600 mb-4">Everything is ready to use. You can now start managing calls intelligently!</p> | |
| <div class="bg-gray-50 p-4 rounded-lg text-left"> | |
| <p class="text-sm font-medium text-gray-700 mb-2">Your setup details:</p> | |
| <ul class="text-xs space-y-1 text-gray-600"> | |
| <li>Trillion API Key: <span id="showTrillionKey" class="font-mono">tril_live_87f2k39d8b20jf7</span></li> | |
| <li>Pusher App ID: <span id="showPusherId" class="font-mono">1529367</span></li> | |
| <li>HuggingFace Key: <span id="showHFKey" class="font-mono">hf_K92jf72hf82jf92hf9</span></li> | |
| <li>Twilio Number: <span id="showTwilioNum" class="font-mono">+18582634276</span></li> | |
| </ul> | |
| </div> | |
| </div> | |
| <div class="flex justify-center mt-6"> | |
| <button id="finalCloseBtn" class="py-2 px-6 bg-blue-600 hover:bg-blue-700 text-white rounded-lg">Start Using AI Call Manager</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- System Status Check --> | |
| <script> | |
| document.getElementById('checkSystemBtn').addEventListener('click', async function() { | |
| try { | |
| this.classList.add('bg-yellow-100'); | |
| this.querySelector('i').className = 'fas fa-spinner fa-spin text-yellow-600'; | |
| // Simulate checking services | |
| await new Promise(resolve => setTimeout(resolve, 1500)); | |
| const isSetupComplete = localStorage.getItem('setupComplete') === 'true'; | |
| const services = { | |
| jitsi: true, | |
| pusher: true, | |
| twilio: true, | |
| hf: true | |
| }; | |
| const missingServices = Object.entries(services).filter(([_, available]) => !available).map(([name]) => name); | |
| if (missingServices.length > 0) { | |
| alert(`System check failed. Missing services: ${missingServices.join(', ')}`); | |
| this.classList.remove('bg-yellow-100', 'bg-green-100'); | |
| this.classList.add('bg-red-100'); | |
| this.querySelector('i').className = 'fas fa-exclamation-circle text-red-600'; | |
| } else { | |
| alert('All systems operational!'); | |
| this.classList.remove('bg-yellow-100'); | |
| this.classList.add('bg-green-100'); | |
| this.querySelector('i').className = 'fas fa-check-circle text-green-600'; | |
| } | |
| // Grey out setup wizard if already completed | |
| if (isSetupComplete) { | |
| document.getElementById('setupHelpBtn').classList.remove('bg-blue-100', 'hover:bg-blue-200'); | |
| document.getElementById('setupHelpBtn').classList.add('bg-gray-200', 'cursor-not-allowed'); | |
| } | |
| } catch (error) { | |
| console.error('System check failed:', error); | |
| this.classList.remove('bg-yellow-100'); | |
| this.classList.add('bg-red-100'); | |
| this.querySelector('i').className = 'fas fa-times-circle text-red-600'; | |
| alert('System check failed'); | |
| } finally { | |
| setTimeout(() => { | |
| this.classList.remove('bg-yellow-100', 'bg-red-100'); | |
| this.classList.add('bg-green-100'); | |
| this.querySelector('i').className = 'fas fa-check-circle text-green-600'; | |
| }, 3000); | |
| } | |
| }); | |
| </script> | |
| <!-- Setup Wizard Functionality --> | |
| <script> | |
| let currentStep = 1; | |
| const totalSteps = 4; | |
| const setupHelpModal = document.getElementById('setupHelpModal'); | |
| const setupHelpBtn = document.getElementById('setupHelpBtn'); | |
| const closeSetupHelp = document.getElementById('closeSetupHelp'); | |
| const wizardProgressBar = document.getElementById('wizardProgressBar'); | |
| function updateProgress() { | |
| const progress = (currentStep / totalSteps) * 100; | |
| wizardProgressBar.style.width = `${progress}%`; | |
| } | |
| function showStep(stepNumber) { | |
| document.querySelectorAll('.wizard-step').forEach(step => { | |
| step.classList.remove('active'); | |
| }); | |
| document.getElementById(`step${stepNumber}`).classList.add('active'); | |
| updateProgress(); | |
| } | |
| document.querySelectorAll('.wizard-next-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| if(currentStep === 3) { | |
| startAutomaticSetup(); | |
| } else { | |
| currentStep++; | |
| showStep(currentStep); | |
| } | |
| }); | |
| }); | |
| document.querySelectorAll('.wizard-prev-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| if(currentStep > 1) { | |
| currentStep--; | |
| showStep(currentStep); | |
| } | |
| }); | |
| }); | |
| function startAutomaticSetup() { | |
| const setupElements = { | |
| trillion: document.getElementById('setupTrillion'), | |
| pusher: document.getElementById('setupPusher'), | |
| huggingFace: document.getElementById('setupHuggingFace'), | |
| twilio: document.getElementById('setupTwilio'), | |
| env: document.getElementById('setupEnv') | |
| }; | |
| const startBtn = document.getElementById('startSetupBtn'); | |
| const finishBtn = document.getElementById('finishBtn'); | |
| startBtn.classList.add('hidden'); | |
| finishBtn.classList.remove('hidden'); | |
| // Simulate setup process with real configuration | |
| setTimeout(() => { | |
| setupElements.trillion.classList.remove('border-gray-300'); | |
| setupElements.trillion.classList.add('bg-green-500', 'border-green-500'); | |
| setupElements.trillion.innerHTML = '<i class="fas fa-check text-white text-xs"></i>'; | |
| }, 1000); | |
| setTimeout(() => { | |
| setupElements.pusher.classList.remove('border-gray-300'); | |
| setupElements.pusher.classList.add('bg-green-500', 'border-green-500'); | |
| setupElements.pusher.innerHTML = '<i class="fas fa-check text-white text-xs"></i>'; | |
| }, 2000); | |
| setTimeout(() => { | |
| setupElements.huggingFace.classList.remove('border-gray-300'); | |
| setupElements.huggingFace.classList.add('bg-green-500', 'border-green-500'); | |
| setupElements.huggingFace.innerHTML = '<i class="fas fa-check text-white text-xs"></i>'; | |
| }, 3000); | |
| setTimeout(() => { | |
| setupElements.twilio.classList.remove('border-gray-300'); | |
| setupElements.twilio.classList.add('bg-green-500', 'border-green-500'); | |
| setupElements.twilio.innerHTML = '<i class="fas fa-check text-white text-xs"></i>'; | |
| }, 4000); | |
| setTimeout(() => { | |
| setupElements.env.classList.remove('border-gray-300'); | |
| setupElements.env.classList.add('bg-green-500', 'border-green-500'); | |
| setupElements.env.innerHTML = '<i class="fas fa-check text-white text-xs"></i>'; | |
| document.getElementById('setupComplete').classList.remove('hidden'); | |
| finishBtn.disabled = false; | |
| }, 5000); | |
| finishBtn.addEventListener('click', () => { | |
| currentStep++; | |
| showStep(currentStep); | |
| }); | |
| } | |
| setupHelpBtn.addEventListener('click', () => { | |
| setupHelpModal.classList.remove('hidden'); | |
| currentStep = 1; | |
| showStep(currentStep); | |
| }); | |
| closeSetupHelp.addEventListener('click', () => { | |
| setupHelpModal.classList.add('hidden'); | |
| }); | |
| document.getElementById('finalCloseBtn').addEventListener('click', () => { | |
| localStorage.setItem('setupComplete', 'true'); | |
| document.getElementById('setupHelpBtn').classList.remove('bg-blue-100', 'hover:bg-blue-200'); | |
| document.getElementById('setupHelpBtn').classList.add('bg-gray-200', 'cursor-not-allowed'); | |
| document.getElementById('setupHelpBtn').title = 'Setup already completed'; | |
| setupHelpModal.classList.add('hidden'); | |
| }); | |
| window.addEventListener('click', (event) => { | |
| if (event.target === setupHelpModal) { | |
| setupHelpModal.classList.add('hidden'); | |
| } | |
| }); | |
| </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/bbsbd" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |