| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>VoiceBook - Talk & Get Appointed!</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <script> |
| tailwind.config = { |
| theme: { |
| extend: { |
| colors: { |
| primary: { |
| 50: '#f0f9ff', |
| 100: '#e0f2fe', |
| 500: '#0ea5e9', |
| 600: '#0284c7', |
| 700: '#0369a1', |
| }, |
| secondary: { |
| 50: '#f5f3ff', |
| 100: '#ede9fe', |
| 500: '#8b5cf6', |
| 600: '#7c3aed', |
| 700: '#6d28d9', |
| } |
| } |
| } |
| } |
| } |
| </script> |
| <style> |
| @keyframes pulse-slow { |
| 0%, 100% { opacity: 1; } |
| 50% { opacity: 0.5; } |
| } |
| @keyframes float { |
| 0%, 100% { transform: translateY(0px); } |
| 50% { transform: translateY(-10px); } |
| } |
| .animate-float { |
| animation: float 4s ease-in-out infinite; |
| } |
| .animate-pulse-slow { |
| animation: pulse-slow 3s cubic-bezier(0.4, 0, 0.6, 1) infinite; |
| } |
| </style> |
| </head> |
| <body class="bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 min-h-screen dark:text-white transition-colors duration-300"> |
| <div id="app" class="container mx-auto px-4 py-8"> |
| |
| <div class="text-center mb-12 animate-float"> |
| <div class="inline-block p-4 bg-gray-800 rounded-full shadow-xl mb-6"> |
| <i data-feather="mic" class="w-12 h-12 text-primary-400"></i> |
| </div> |
| <h1 class="text-4xl font-bold text-white mb-3">VoiceBook</h1> |
| <p class="text-xl text-gray-300">Just speak and get your appointment booked!</p> |
| <div class="mt-6"> |
| <span class="inline-block px-3 py-1 bg-gray-800 text-primary-300 rounded-full text-sm font-medium"> |
| 🎙️ Voice-powered |
| </span> |
| <span class="inline-block px-3 py-1 bg-gray-800 text-secondary-300 rounded-full text-sm font-medium ml-2"> |
| 📅 Instant booking |
| </span> |
| </div> |
| </div> |
|
|
| |
| <div class="max-w-3xl mx-auto bg-gray-800 rounded-xl shadow-lg overflow-hidden border border-gray-700"> |
| <div class="md:flex"> |
| |
| <div class="p-8 md:w-1/2 bg-gradient-to-br from-gray-800 to-gray-700"> |
| <h2 class="text-2xl font-semibold text-white mb-6 flex items-center"> |
| <i data-feather="mic" class="mr-2 text-primary-400"></i> Speak Now |
| </h2> |
| <div class="flex flex-col items-center"> |
| <button id="recordBtn" class="relative w-24 h-24 rounded-full bg-primary-600 hover:bg-primary-500 flex items-center justify-center text-white shadow-lg transition-all duration-300 mb-4"> |
| <i data-feather="mic" class="w-8 h-8"></i> |
| <span class="absolute inset-0 rounded-full bg-primary-400 animate-pulse-slow opacity-75"></span> |
| </button> |
| <p id="recordStatus" class="text-sm text-gray-400">Tap to start recording</p> |
| </div> |
| |
| <div id="transcriptContainer" class="mt-8 hidden"> |
| <div class="bg-gray-700 rounded-lg p-4"> |
| <p class="text-sm text-gray-400 mb-1">You said:</p> |
| <p id="transcript" class="text-white"></p> |
| </div> |
| </div> |
| <div class="mt-6 bg-gray-700 rounded-lg p-4 border border-gray-600"> |
| <p class="text-sm font-medium text-primary-400 mb-2">Try saying:</p> |
| <ul class="text-sm text-primary-300 space-y-1"> |
| <li>"Book an appointment for tomorrow at 2pm"</li> |
| <li>"I need a meeting next Monday"</li> |
| <li>"Schedule for December 5th at 10:30"</li> |
| </ul> |
| </div> |
| </div> |
| |
| |
| <div class="p-8 md:w-1/2"> |
| <h2 class="text-2xl font-semibold text-white mb-6 flex items-center"> |
| <i data-feather="calendar" class="mr-2 text-secondary-400"></i> Your Details |
| </h2> |
| <form id="bookingForm" class="space-y-4"> |
| <div> |
| <label class="block text-sm font-medium text-gray-300 mb-1">Name</label> |
| <input type="text" id="name" class="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-all text-white placeholder-gray-400" placeholder="Your name" required> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-gray-300 mb-1">Email</label> |
| <input type="email" id="email" class="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-all text-white placeholder-gray-400" placeholder="your@email.com" required> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-gray-300 mb-1">Date</label> |
| <input type="date" id="date" class="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-all text-white" required> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-gray-300 mb-1">Time</label> |
| <input type="time" id="time" class="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-all text-white" required> |
| </div> |
| |
| <button type="submit" class="w-full bg-gradient-to-r from-primary-500 to-secondary-500 hover:from-primary-600 hover:to-secondary-600 text-white py-3 rounded-lg font-medium transition-all duration-300 flex items-center justify-center"> |
| <i data-feather="calendar" class="mr-2"></i> Book Appointment |
| </button> |
| </form> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="mt-16 grid md:grid-cols-3 gap-8"> |
| <div class="bg-gray-800 p-6 rounded-xl shadow-md border border-gray-700"> |
| <div class="bg-gray-700 w-12 h-12 rounded-full flex items-center justify-center mb-4"> |
| <i data-feather="mic" class="text-primary-400"></i> |
| </div> |
| <h3 class="text-xl font-semibold text-white mb-2">Voice Control</h3> |
| <p class="text-gray-300">Simply speak your appointment details and we'll handle the rest.</p> |
| </div> |
| |
| <div class="bg-gray-800 p-6 rounded-xl shadow-md border border-gray-700"> |
| <div class="bg-gray-700 w-12 h-12 rounded-full flex items-center justify-center mb-4"> |
| <i data-feather="clock" class="text-secondary-400"></i> |
| </div> |
| <h3 class="text-xl font-semibold text-white mb-2">Instant Booking</h3> |
| <p class="text-gray-300">Get your appointment confirmed immediately with real-time availability.</p> |
| </div> |
| |
| <div class="bg-gray-800 p-6 rounded-xl shadow-md border border-gray-700"> |
| <div class="bg-gray-700 w-12 h-12 rounded-full flex items-center justify-center mb-4"> |
| <i data-feather="check-circle" class="text-primary-400"></i> |
| </div> |
| <h3 class="text-xl font-semibold text-white mb-2">Smart Reminders</h3> |
| <p class="text-gray-300">We'll remind you before your appointment so you never miss it.</p> |
| </div> |
| </div> |
| |
| |
| <div class="mt-16 text-center text-gray-400 text-sm"> |
| <p>Made with <i data-feather="heart" class="w-4 h-4 inline text-red-400"></i> by VoiceBook Team</p> |
| <p class="mt-2">© 2023 VoiceBook. All rights reserved.</p> |
| </div> |
| </div> |
|
|
| <script> |
| feather.replace(); |
| |
| |
| const CAL_API_KEY = 'cal_live_4412d184cc839ad6710923fe3482b1c9'; |
| const ELEVENLABS_API_KEY = 'sk_18e110174de3da88a068f08fad4143abe830dbe402d957de'; |
| |
| |
| let recognition; |
| if ('webkitSpeechRecognition' in window) { |
| recognition = new webkitSpeechRecognition(); |
| recognition.continuous = false; |
| recognition.interimResults = false; |
| recognition.lang = 'en-US'; |
| |
| recognition.onresult = function(event) { |
| const transcript = event.results[0][0].transcript; |
| document.getElementById('transcript').textContent = transcript; |
| document.getElementById('transcriptContainer').classList.remove('hidden'); |
| processVoiceCommand(transcript); |
| }; |
| |
| recognition.onerror = function(event) { |
| console.error('Speech recognition error', event.error); |
| }; |
| } |
| |
| function processVoiceCommand(command) { |
| |
| |
| const dateRegex = /(today|tomorrow|next (monday|tuesday|wednesday|thursday|friday|saturday|sunday)|(\d{1,2}\/\d{1,2}\/\d{4}))/i; |
| const timeRegex = /(\d{1,2})(?::(\d{2}))?\s*(am|pm)?/i; |
| |
| |
| const dateMatch = command.match(dateRegex); |
| let date = new Date(); |
| |
| if (dateMatch) { |
| if (dateMatch[1].toLowerCase() === 'tomorrow') { |
| date.setDate(date.getDate() + 1); |
| } else if (dateMatch[1].toLowerCase().startsWith('next ')) { |
| const day = dateMatch[2].toLowerCase(); |
| const days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; |
| const targetDay = days.indexOf(day); |
| date.setDate(date.getDate() + ((targetDay + 7 - date.getDay()) % 7 || 7)); |
| } |
| } |
| |
| |
| const timeMatch = command.match(timeRegex); |
| if (timeMatch) { |
| let hours = parseInt(timeMatch[1]); |
| const minutes = timeMatch[2] ? parseInt(timeMatch[2]) : 0; |
| const period = timeMatch[3] ? timeMatch[3].toLowerCase() : ''; |
| |
| if (period === 'pm' && hours < 12) hours += 12; |
| if (period === 'am' && hours === 12) hours = 0; |
| |
| date.setHours(hours, minutes, 0, 0); |
| } |
| |
| |
| const dateStr = date.toISOString().split('T')[0]; |
| const timeStr = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`; |
| |
| document.getElementById('date').value = dateStr; |
| document.getElementById('time').value = timeStr; |
| } |
| |
| |
| const recordBtn = document.getElementById('recordBtn'); |
| const recordStatus = document.getElementById('recordStatus'); |
| const transcriptContainer = document.getElementById('transcriptContainer'); |
| const transcript = document.getElementById('transcript'); |
| const bookingForm = document.getElementById('bookingForm'); |
| |
| let isRecording = false; |
| |
| recordBtn.addEventListener('click', function() { |
| if (!recognition) { |
| |
| isRecording = !isRecording; |
| |
| if (isRecording) { |
| recordBtn.innerHTML = '<i data-feather="square" class="w-8 h-8"></i>' + |
| '<span class="absolute inset-0 rounded-full bg-primary-400 animate-pulse-slow opacity-75"></span>'; |
| recordStatus.textContent = "Listening... Speak now"; |
| feather.replace(); |
| } else { |
| recordBtn.innerHTML = '<i data-feather="mic" class="w-8 h-8"></i>' + |
| '<span class="absolute inset-0 rounded-full bg-primary-400 animate-pulse-slow opacity-75"></span>'; |
| recordStatus.textContent = "Tap to start recording"; |
| |
| |
| transcript.textContent = "Book an appointment for tomorrow at 2pm"; |
| transcriptContainer.classList.remove('hidden'); |
| |
| |
| const tomorrow = new Date(); |
| tomorrow.setDate(tomorrow.getDate() + 1); |
| const dateStr = tomorrow.toISOString().split('T')[0]; |
| |
| document.getElementById('name').value = "John Doe"; |
| document.getElementById('email').value = "john@example.com"; |
| document.getElementById('date').value = dateStr; |
| document.getElementById('time').value = "14:00"; |
| |
| feather.replace(); |
| } |
| return; |
| } |
| |
| if (!isRecording) { |
| recognition.start(); |
| recordBtn.innerHTML = '<i data-feather="square" class="w-8 h-8"></i>' + |
| '<span class="absolute inset-0 rounded-full bg-primary-400 animate-pulse-slow opacity-75"></span>'; |
| recordStatus.textContent = "Listening... Speak now"; |
| isRecording = true; |
| feather.replace(); |
| } else { |
| recognition.stop(); |
| recordBtn.innerHTML = '<i data-feather="mic" class="w-8 h-8"></i>' + |
| '<span class="absolute inset-0 rounded-full bg-primary-400 animate-pulse-slow opacity-75"></span>'; |
| recordStatus.textContent = "Tap to start recording"; |
| isRecording = false; |
| feather.replace(); |
| } |
| }); |
| bookingForm.addEventListener('submit', async function(e) { |
| e.preventDefault(); |
| |
| const response = await fetch('https://api.cal.com/v1/bookings', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| 'Authorization': `Bearer ${CAL_API_KEY}` |
| }, |
| body: JSON.stringify({ |
| eventTypeId: 1, |
| start: `${document.getElementById('date').value}T${document.getElementById('time').value}:00.000Z`, |
| end: `${document.getElementById('date').value}T${parseInt(document.getElementById('time').value.split(':')[0]) + 1}:${document.getElementById('time').value.split(':')[1]}:00.000Z`, |
| responses: { |
| name: document.getElementById('name').value, |
| email: document.getElementById('email').value |
| } |
| }) |
| }); |
| |
| if (response.ok) { |
| alert('Appointment booked successfully!'); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } else { |
| alert('Failed to book appointment. Please try again.'); |
| } |
| }); |
| </script> |
| </body> |
| </html> |
|
|