enes970's picture
make it darkmode UI,cal ai api = cal_live_4412d184cc839ad6710923fe3482b1c9,
6707f60 verified
<!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">
<!-- Hero Section -->
<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>
<!-- Main Card -->
<div class="max-w-3xl mx-auto bg-gray-800 rounded-xl shadow-lg overflow-hidden border border-gray-700">
<div class="md:flex">
<!-- Voice Input Side -->
<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>
<!-- Form Side -->
<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>
<!-- Features Section -->
<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>
<!-- Footer -->
<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();
// API Keys
const CAL_API_KEY = 'cal_live_4412d184cc839ad6710923fe3482b1c9';
const ELEVENLABS_API_KEY = 'sk_18e110174de3da88a068f08fad4143abe830dbe402d957de';
// Voice Recognition
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) {
// Parse date and time from command
// This is simplified - in production you'd use a proper NLP library
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;
// Extract date
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));
}
}
// Extract time
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);
}
// Format for form inputs
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;
}
// Simple demo functionality (fallback)
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) {
// Fallback if no speech 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";
// Simulate voice recognition result
transcript.textContent = "Book an appointment for tomorrow at 2pm";
transcriptContainer.classList.remove('hidden');
// Auto-fill form with recognized data
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, // Replace with your event type ID
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!');
// Optional: Generate confirmation audio with ElevenLabs
/*
const audioResponse = await fetch('https://api.elevenlabs.io/v1/text-to-speech/your_voice_id', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'xi-api-key': ELEVENLABS_API_KEY
},
body: JSON.stringify({
text: `Your appointment has been confirmed for ${document.getElementById('date').value} at ${document.getElementById('time').value}`,
voice_settings: {
stability: 0.5,
similarity_boost: 0.5
}
})
});
const audioBlob = await audioResponse.blob();
const audioUrl = URL.createObjectURL(audioBlob);
new Audio(audioUrl).play();
*/
} else {
alert('Failed to book appointment. Please try again.');
}
});
</script>
</body>
</html>