moreter / index.html
shaike123's picture
Add 3 files
f429cb9 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Negotiation Coach - AI Sales Practice</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Inter', sans-serif;
background-color: #f8fafc;
}
.character-avatar {
transition: all 0.3s ease;
filter: grayscale(100%);
}
.character-avatar:hover, .character-avatar.selected {
filter: grayscale(0%);
transform: scale(1.05);
}
.typing-indicator span {
display: inline-block;
width: 8px;
height: 8px;
background-color: #4b5563;
border-radius: 50%;
margin: 0 2px;
animation: bounce 1.4s infinite ease-in-out;
}
.typing-indicator span:nth-child(2) {
animation-delay: 0.2s;
}
.typing-indicator span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes bounce {
0%, 60%, 100% { transform: translateY(0); }
30% { transform: translateY(-5px); }
}
.message-bubble {
max-width: 80%;
border-radius: 1.125rem;
line-height: 1.25;
position: relative;
word-wrap: break-word;
}
.user-message {
background-color: #3b82f6;
color: white;
border-bottom-right-radius: 0.25rem;
margin-left: auto;
}
.bot-message {
background-color: #e2e8f0;
color: #1f2937;
border-bottom-left-radius: 0.25rem;
margin-right: auto;
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
}
.scene-card {
transition: all 0.3s ease;
}
.scene-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.scene-card.selected {
border: 2px solid #3b82f6;
transform: translateY(-5px);
}
</style>
</head>
<body class="min-h-screen bg-gray-50">
<div class="container mx-auto px-4 py-8 max-w-6xl">
<header class="text-center mb-12">
<h1 class="text-4xl font-bold text-gray-800 mb-2">Negotiation Coach Pro</h1>
<p class="text-lg text-gray-600">Practice your sales pitch with realistic AI characters</p>
</header>
<div id="app" class="bg-white rounded-xl shadow-lg overflow-hidden">
<!-- Setup Screen -->
<div id="setup-screen" class="p-8">
<div class="mb-10">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">Select Your Character</h2>
<div class="grid grid-cols-2 md:grid-cols-5 gap-6">
<!-- Character 1 -->
<div class="character-option cursor-pointer text-center" data-character="1">
<div class="character-avatar rounded-full overflow-hidden mx-auto mb-3 w-24 h-24 border-4 border-gray-200">
<img src="https://randomuser.me/api/portraits/women/44.jpg" alt="Sarah - Marketing Director" class="w-full h-full object-cover">
</div>
<h3 class="font-medium text-gray-800">Sarah</h3>
<p class="text-sm text-gray-500">Marketing Director</p>
</div>
<!-- Character 2 -->
<div class="character-option cursor-pointer text-center" data-character="2">
<div class="character-avatar rounded-full overflow-hidden mx-auto mb-3 w-24 h-24 border-4 border-gray-200">
<img src="https://randomuser.me/api/portraits/men/32.jpg" alt="James - CFO" class="w-full h-full object-cover">
</div>
<h3 class="font-medium text-gray-800">James</h3>
<p class="text-sm text-gray-500">CFO</p>
</div>
<!-- Character 3 -->
<div class="character-option cursor-pointer text-center" data-character="3">
<div class="character-avatar rounded-full overflow-hidden mx-auto mb-3 w-24 h-24 border-4 border-gray-200">
<img src="https://randomuser.me/api/portraits/women/68.jpg" alt="Priya - Startup Founder" class="w-full h-full object-cover">
</div>
<h3 class="font-medium text-gray-800">Priya</h3>
<p class="text-sm text-gray-500">Startup Founder</p>
</div>
<!-- Character 4 -->
<div class="character-option cursor-pointer text-center" data-character="4">
<div class="character-avatar rounded-full overflow-hidden mx-auto mb-3 w-24 h-24 border-4 border-gray-200">
<img src="https://randomuser.me/api/portraits/men/75.jpg" alt="Carlos - IT Manager" class="w-full h-full object-cover">
</div>
<h3 class="font-medium text-gray-800">Carlos</h3>
<p class="text-sm text-gray-500">IT Manager</p>
</div>
<!-- Character 5 -->
<div class="character-option cursor-pointer text-center" data-character="5">
<div class="character-avatar rounded-full overflow-hidden mx-auto mb-3 w-24 h-24 border-4 border-gray-200">
<img src="https://randomuser.me/api/portraits/women/90.jpg" alt="Linda - Small Business Owner" class="w-full h-full object-cover">
</div>
<h3 class="font-medium text-gray-800">Linda</h3>
<p class="text-sm text-gray-500">Small Business Owner</p>
</div>
</div>
</div>
<div class="mb-10">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">Select a Scenario</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<!-- Scenario 1 -->
<div class="scene-card p-6 bg-white rounded-lg border border-gray-200 cursor-pointer" data-scene="1">
<div class="flex items-center mb-4">
<div class="bg-blue-100 p-3 rounded-full mr-4">
<i class="fas fa-briefcase text-blue-600 text-xl"></i>
</div>
<h3 class="font-medium text-gray-800">Enterprise Software</h3>
</div>
<p class="text-gray-600 text-sm">Sell your SaaS platform to a corporate decision maker who's skeptical about ROI.</p>
</div>
<!-- Scenario 2 -->
<div class="scene-card p-6 bg-white rounded-lg border border-gray-200 cursor-pointer" data-scene="2">
<div class="flex items-center mb-4">
<div class="bg-green-100 p-3 rounded-full mr-4">
<i class="fas fa-home text-green-600 text-xl"></i>
</div>
<h3 class="font-medium text-gray-800">Home Security</h3>
</div>
<p class="text-gray-600 text-sm">Convince a homeowner to upgrade to your premium security system package.</p>
</div>
<!-- Scenario 3 -->
<div class="scene-card p-6 bg-white rounded-lg border border-gray-200 cursor-pointer" data-scene="3">
<div class="flex items-center mb-4">
<div class="bg-purple-100 p-3 rounded-full mr-4">
<i class="fas fa-heart text-purple-600 text-xl"></i>
</div>
<h3 class="font-medium text-gray-800">Health Insurance</h3>
</div>
<p class="text-gray-600 text-sm">Overcome price objections for a comprehensive health insurance plan.</p>
</div>
</div>
</div>
<div class="text-center">
<button id="start-session" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-8 rounded-full text-lg transition duration-300 transform hover:scale-105">
Start Negotiation Session
</button>
</div>
</div>
<!-- Chat Screen (Hidden Initially) -->
<div id="chat-screen" class="hidden h-full flex flex-col">
<!-- Chat Header -->
<div class="bg-gray-800 text-white p-4 flex items-center justify-between">
<div class="flex items-center">
<img id="current-character-avatar" src="" alt="Character" class="w-10 h-10 rounded-full mr-3">
<div>
<h3 id="current-character-name" class="font-medium"></h3>
<p id="current-scenario" class="text-xs text-gray-300"></p>
</div>
</div>
<div class="flex items-center space-x-3">
<div class="text-sm bg-gray-700 px-3 py-1 rounded-full">
<i class="fas fa-microphone mr-1"></i>
<span id="recording-status">Ready</span>
</div>
<button id="end-session" class="text-xs bg-red-500 hover:bg-red-600 px-3 py-1 rounded-full transition">
<i class="fas fa-times mr-1"></i> End Session
</button>
</div>
</div>
<!-- Chat Messages -->
<div id="chat-messages" class="flex-1 p-6 overflow-y-auto space-y-4" style="max-height: 60vh;">
<!-- Messages will be inserted here -->
</div>
<!-- Input Area -->
<div class="border-t border-gray-200 p-4 bg-gray-50">
<div class="flex items-center">
<button id="voice-toggle" class="bg-blue-600 text-white p-3 rounded-full mr-3 hover:bg-blue-700 transition pulse">
<i class="fas fa-microphone text-xl"></i>
</button>
<div class="flex-1 relative">
<input id="text-input" type="text" placeholder="Type your message or speak..." class="w-full border border-gray-300 rounded-full py-3 px-5 pr-12 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<button id="send-text" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-blue-600">
<i class="fas fa-paper-plane text-xl"></i>
</button>
</div>
</div>
<div class="mt-3 text-xs text-gray-500 flex justify-between">
<div>
<span id="connection-status" class="inline-flex items-center">
<span class="w-2 h-2 rounded-full bg-green-500 mr-1"></span>
Connected
</span>
</div>
<div>
<button id="hint-button" class="text-blue-600 hover:text-blue-800">
<i class="fas fa-lightbulb mr-1"></i> Get a hint
</button>
</div>
</div>
</div>
</div>
<!-- Results Screen (Hidden Initially) -->
<div id="results-screen" class="hidden p-8 text-center">
<div class="max-w-md mx-auto">
<div class="bg-green-100 text-green-800 p-4 rounded-full w-24 h-24 flex items-center justify-center mx-auto mb-6">
<i class="fas fa-trophy text-4xl"></i>
</div>
<h2 class="text-2xl font-bold text-gray-800 mb-3">Negotiation Complete!</h2>
<div class="bg-white rounded-xl shadow-sm p-6 mb-6">
<div class="grid grid-cols-2 gap-4 text-left mb-4">
<div>
<p class="text-sm text-gray-500">Character</p>
<p id="result-character" class="font-medium"></p>
</div>
<div>
<p class="text-sm text-gray-500">Scenario</p>
<p id="result-scenario" class="font-medium"></p>
</div>
<div>
<p class="text-sm text-gray-500">Duration</p>
<p id="result-duration" class="font-medium"></p>
</div>
<div>
<p class="text-sm text-gray-500">Outcome</p>
<p id="result-outcome" class="font-medium"></p>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-sm p-6 mb-8 text-left">
<h3 class="font-semibold text-gray-800 mb-3">Feedback</h3>
<p id="feedback-text" class="text-gray-600 mb-4"></p>
<div class="space-y-3">
<div>
<p class="text-sm font-medium text-gray-700 mb-1">Strengths</p>
<ul id="strengths-list" class="text-sm text-gray-600 list-disc pl-5 space-y-1"></ul>
</div>
<div>
<p class="text-sm font-medium text-gray-700 mb-1">Areas to Improve</p>
<ul id="improvements-list" class="text-sm text-gray-600 list-disc pl-5 space-y-1"></ul>
</div>
</div>
</div>
<div class="flex justify-center space-x-4">
<button id="new-session" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-6 rounded-full transition">
<i class="fas fa-redo mr-2"></i> New Session
</button>
<button id="view-transcript" class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-6 rounded-full transition">
<i class="fas fa-file-alt mr-2"></i> View Transcript
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Hint Modal -->
<div id="hint-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-xl p-6 max-w-md w-full mx-4">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-semibold text-gray-800">Negotiation Hint</h3>
<button id="close-hint" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div id="hint-content" class="text-gray-600 mb-4">
<!-- Hint content will be inserted here -->
</div>
<div class="text-right">
<button id="next-hint" class="text-blue-600 hover:text-blue-800 text-sm font-medium">
Next Hint <i class="fas fa-arrow-right ml-1"></i>
</button>
</div>
</div>
</div>
<!-- Transcript Modal -->
<div id="transcript-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-xl p-6 max-w-2xl w-full mx-4 max-h-[80vh] flex flex-col">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-semibold text-gray-800">Session Transcript</h3>
<button id="close-transcript" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div id="transcript-content" class="overflow-y-auto flex-1 mb-4 space-y-3">
<!-- Transcript content will be inserted here -->
</div>
<div class="border-t border-gray-200 pt-4 flex justify-end">
<button id="download-transcript" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-full text-sm">
<i class="fas fa-download mr-2"></i> Download
</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const setupScreen = document.getElementById('setup-screen');
const chatScreen = document.getElementById('chat-screen');
const resultsScreen = document.getElementById('results-screen');
const chatMessages = document.getElementById('chat-messages');
const textInput = document.getElementById('text-input');
const sendText = document.getElementById('send-text');
const voiceToggle = document.getElementById('voice-toggle');
const startSession = document.getElementById('start-session');
const endSession = document.getElementById('end-session');
const newSession = document.getElementById('new-session');
const viewTranscript = document.getElementById('view-transcript');
const hintButton = document.getElementById('hint-button');
const hintModal = document.getElementById('hint-modal');
const closeHint = document.getElementById('close-hint');
const nextHint = document.getElementById('next-hint');
const hintContent = document.getElementById('hint-content');
const transcriptModal = document.getElementById('transcript-modal');
const closeTranscript = document.getElementById('close-transcript');
const transcriptContent = document.getElementById('transcript-content');
const downloadTranscript = document.getElementById('download-transcript');
const recordingStatus = document.getElementById('recording-status');
// State variables
let selectedCharacter = null;
let selectedScenario = null;
let isListening = false;
let recognition;
let speechSynthesis = window.speechSynthesis;
let sessionStartTime;
let messages = [];
let currentHints = [];
let currentHintIndex = 0;
// Character data
const characters = {
1: {
name: "Sarah Johnson",
role: "Marketing Director",
avatar: "https://randomuser.me/api/portraits/women/44.jpg",
personality: "Data-driven, focused on ROI and customer acquisition costs. Skeptical of fluffy claims.",
voice: "UK English Female",
pitch: 1,
rate: 1
},
2: {
name: "James Wilson",
role: "CFO",
avatar: "https://randomuser.me/api/portraits/men/32.jpg",
personality: "All about the numbers. Wants clear cost-benefit analysis and payment flexibility.",
voice: "US English Male",
pitch: 0.9,
rate: 0.9
},
3: {
name: "Priya Patel",
role: "Startup Founder",
avatar: "https://randomuser.me/api/portraits/women/68.jpg",
personality: "Visionary but cash-strapped. Needs to see how this fits her long-term strategy.",
voice: "Indian English Female",
pitch: 1.1,
rate: 1.1
},
4: {
name: "Carlos Mendez",
role: "IT Manager",
avatar: "https://randomuser.me/api/portraits/men/75.jpg",
personality: "Technical but overworked. Worried about implementation complexity and support.",
voice: "Spanish English Male",
pitch: 1,
rate: 1
},
5: {
name: "Linda Thompson",
role: "Small Business Owner",
avatar: "https://randomuser.me/api/portraits/women/90.jpg",
personality: "Practical and hands-on. Needs simple solutions that won't disrupt her daily operations.",
voice: "Australian English Female",
pitch: 1,
rate: 1
}
};
// Scenario data
const scenarios = {
1: {
title: "Enterprise Software Sale",
description: "Sell your SaaS platform to a corporate decision maker",
product: "Enterprise CRM Software",
price: "$120,000/year",
features: [
"Advanced analytics dashboard",
"Custom workflow automation",
"Enterprise-grade security",
"24/7 dedicated support"
],
objections: [
"We already have a system that works",
"This is too expensive for what you're offering",
"I'm not convinced this will integrate well with our other tools",
"Our team won't adopt this easily"
],
hints: [
"Focus on ROI - calculate how much time/money they'll save",
"Offer a phased implementation plan to reduce risk",
"Provide case studies from similar companies",
"Highlight unique features competitors don't have"
]
},
2: {
title: "Home Security System",
description: "Convince a homeowner to upgrade to premium package",
product: "UltraSecure Home System",
price: "$1,499 + $49/month",
features: [
"24/7 professional monitoring",
"Smart home integration",
"4K cameras with facial recognition",
"Lifetime equipment warranty"
],
objections: [
"I already have a basic system that's fine",
"This seems too expensive for what I need",
"I'm worried about privacy with all these cameras",
"Installation sounds disruptive"
],
hints: [
"Calculate cost per day - less than a cup of coffee for peace of mind",
"Share crime statistics for their neighborhood",
"Offer a limited-time discount for signing up today",
"Highlight insurance premium discounts they may qualify for"
]
},
3: {
title: "Health Insurance Plan",
description: "Overcome price objections for comprehensive plan",
product: "Platinum Health Coverage",
price: "$850/month individual, $2,200/month family",
features: [
"$0 deductible option",
"Access to top-tier hospitals",
"Alternative therapy coverage",
"24/7 telehealth services"
],
objections: [
"I'm healthy and don't need this much coverage",
"This is way more than I pay now",
"My current doctor might not be in network",
"The paperwork looks complicated"
],
hints: [
"Calculate worst-case scenario costs without this coverage",
"Highlight preventive care benefits that keep them healthy",
"Show network directory to prove their doctors are covered",
"Offer to handle all paperwork for them"
]
}
};
// Initialize speech recognition
function initSpeechRecognition() {
recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
recognition.continuous = true;
recognition.interimResults = true;
recognition.lang = 'en-US';
recognition.onstart = function() {
isListening = true;
recordingStatus.textContent = "Listening...";
voiceToggle.classList.add('bg-red-600', 'pulse');
voiceToggle.classList.remove('bg-blue-600');
voiceToggle.innerHTML = '<i class="fas fa-microphone-slash text-xl"></i>';
};
recognition.onend = function() {
if (isListening) {
recognition.start(); // Restart if still supposed to be listening
}
};
recognition.onresult = function(event) {
let interimTranscript = '';
let finalTranscript = '';
for (let i = event.resultIndex; i < event.results.length; i++) {
const transcript = event.results[i][0].transcript;
if (event.results[i].isFinal) {
finalTranscript += transcript;
} else {
interimTranscript += transcript;
}
}
textInput.value = finalTranscript || interimTranscript;
if (finalTranscript) {
sendUserMessage(finalTranscript);
}
};
recognition.onerror = function(event) {
console.error('Speech recognition error', event.error);
stopListening();
recordingStatus.textContent = "Error: " + event.error;
setTimeout(() => {
recordingStatus.textContent = "Ready";
}, 3000);
};
}
// Initialize speech synthesis
function speak(text, character) {
const charData = characters[character];
const utterance = new SpeechSynthesisUtterance(text);
// Set voice characteristics based on character
utterance.rate = charData.rate;
utterance.pitch = charData.pitch;
// Try to find a matching voice
const voices = speechSynthesis.getVoices();
const preferredVoice = voices.find(v => v.name.includes(charData.voice));
if (preferredVoice) {
utterance.voice = preferredVoice;
}
// Show typing indicator while speaking
const typingIndicator = document.createElement('div');
typingIndicator.className = 'typing-indicator mb-4';
typingIndicator.innerHTML = '<span></span><span></span><span></span>';
chatMessages.appendChild(typingIndicator);
utterance.onend = function() {
typingIndicator.remove();
};
speechSynthesis.speak(utterance);
}
// Start listening
function startListening() {
if (!recognition) {
initSpeechRecognition();
}
try {
recognition.start();
} catch (e) {
console.error('Speech recognition start error:', e);
recordingStatus.textContent = "Error starting mic";
setTimeout(() => {
recordingStatus.textContent = "Ready";
}, 3000);
}
}
// Stop listening
function stopListening() {
if (recognition) {
recognition.stop();
}
isListening = false;
recordingStatus.textContent = "Ready";
voiceToggle.classList.remove('bg-red-600', 'pulse');
voiceToggle.classList.add('bg-blue-600');
voiceToggle.innerHTML = '<i class="fas fa-microphone text-xl"></i>';
}
// Toggle voice input
function toggleVoiceInput() {
if (isListening) {
stopListening();
} else {
startListening();
}
}
// Add message to chat
function addMessage(sender, text, isUser = false) {
const messageDiv = document.createElement('div');
messageDiv.className = `message-bubble p-4 ${isUser ? 'user-message' : 'bot-message'}`;
messageDiv.textContent = text;
chatMessages.appendChild(messageDiv);
// Scroll to bottom
chatMessages.scrollTop = chatMessages.scrollHeight;
// Add to transcript
messages.push({
sender: isUser ? 'You' : characters[selectedCharacter].name,
text: text,
timestamp: new Date().toISOString()
});
}
// Send user message
function sendUserMessage(text) {
if (!text.trim()) return;
addMessage('You', text, true);
textInput.value = '';
// Simulate AI response after a delay
setTimeout(() => {
generateAIResponse(text);
}, 1000 + Math.random() * 2000); // Random delay between 1-3 seconds
}
// Generate AI response based on user input
function generateAIResponse(userInput) {
const scenario = scenarios[selectedScenario];
const character = characters[selectedCharacter];
// Simple NLP - check for keywords in user input
const input = userInput.toLowerCase();
// Check if user asked about price
if (input.includes('price') || input.includes('cost') || input.includes('how much')) {
const responses = [
`Our ${scenario.product} is priced at ${scenario.price}. I know that's a significant investment, but let me explain the value.`,
`The total cost is ${scenario.price}. Before you decide, let's discuss how this will actually save you money in the long run.`,
`It's ${scenario.price}, which I understand is a consideration. But when you factor in the productivity gains, the ROI becomes clear.`
];
const response = responses[Math.floor(Math.random() * responses.length)];
addMessage(character.name, response);
speak(response, selectedCharacter);
return;
}
// Check if user asked about features
if (input.includes('feature') || input.includes('what does it do') || input.includes('benefit')) {
const featuresList = scenario.features.map(f => `• ${f}`).join('\n');
const response = `Here are the key features of our ${scenario.product}:\n${featuresList}\n\nWhich of these would be most valuable for your situation?`;
addMessage(character.name, response);
speak(response, selectedCharacter);
return;
}
// Check if user is agreeing or showing interest
if (input.includes('yes') || input.includes('agree') || input.includes('interesting') || input.includes('like that')) {
if (Math.random() > 0.7) { // 30% chance to close the sale
const closingResponses = [
"You know what? You've convinced me. Let's move forward with this. Where do I sign?",
"I have to say, your approach has addressed all my concerns. I'm ready to approve this purchase.",
"Okay, I'm sold. Let's get the paperwork started."
];
const response = closingResponses[Math.floor(Math.random() * closingResponses.length)];
addMessage(character.name, response);
speak(response, selectedCharacter);
// End session successfully after a delay
setTimeout(() => {
endSessionSuccessfully();
}, 3000);
} else {
const positiveResponses = [
"That's a good point. I'm starting to see the value here.",
"Interesting perspective. Tell me more about how this would work in our specific case.",
"You're making a compelling case. What kind of implementation timeline are we looking at?"
];
const response = positiveResponses[Math.floor(Math.random() * positiveResponses.length)];
addMessage(character.name, response);
speak(response, selectedCharacter);
}
return;
}
// Default to a random objection or question
const objections = scenario.objections;
const randomObjection = objections[Math.floor(Math.random() * objections.length)];
const response = `Hmm, ${randomObjection.toLowerCase()} Can you address that concern for me?`;
addMessage(character.name, response);
speak(response, selectedCharacter);
}
// Start new session
function startNewSession() {
// Get selected character and scenario
const characterOption = document.querySelector('.character-option.selected');
const scenarioOption = document.querySelector('.scene-card.selected');
if (!characterOption || !scenarioOption) {
alert('Please select both a character and a scenario');
return;
}
selectedCharacter = characterOption.dataset.character;
selectedScenario = scenarioOption.dataset.scene;
// Update UI with selected character and scenario
document.getElementById('current-character-avatar').src = characters[selectedCharacter].avatar;
document.getElementById('current-character-name').textContent = `${characters[selectedCharacter].name} (${characters[selectedCharacter].role})`;
document.getElementById('current-scenario').textContent = scenarios[selectedScenario].title;
// Switch to chat screen
setupScreen.classList.add('hidden');
chatScreen.classList.remove('hidden');
resultsScreen.classList.add('hidden');
// Clear previous messages
chatMessages.innerHTML = '';
messages = [];
// Start timer
sessionStartTime = new Date();
// Generate opening message
setTimeout(() => {
const scenario = scenarios[selectedScenario];
const character = characters[selectedCharacter];
const openingLines = [
`Thanks for taking the time to discuss your ${scenario.product}. As ${character.role}, I have some specific concerns about how this would work for us.`,
`I've been reviewing information about your ${scenario.product}. At ${scenario.price}, I need to understand exactly what we're getting.`,
`Let's talk about your ${scenario.product}. I'm interested but have several questions before we proceed.`
];
const opening = openingLines[Math.floor(Math.random() * openingLines.length)];
addMessage(character.name, opening);
speak(opening, selectedCharacter);
}, 1500);
}
// End session successfully
function endSessionSuccessfully() {
stopListening();
// Calculate duration
const duration = Math.floor((new Date() - sessionStartTime) / 1000);
const minutes = Math.floor(duration / 60);
const seconds = duration % 60;
// Update results screen
document.getElementById('result-character').textContent = `${characters[selectedCharacter].name} (${characters[selectedCharacter].role})`;
document.getElementById('result-scenario').textContent = scenarios[selectedScenario].title;
document.getElementById('result-duration').textContent = `${minutes}m ${seconds}s`;
document.getElementById('result-outcome').textContent = "Success - Sale Closed";
document.getElementById('result-outcome').className = "font-medium text-green-600";
// Generate feedback
document.getElementById('feedback-text').textContent = "Congratulations! You successfully navigated the objections and closed the sale. Here's what worked well:";
const strengths = [
"You addressed pricing concerns effectively",
"You tailored your pitch to the buyer's specific role",
"You provided clear value propositions",
"You built rapport throughout the conversation"
];
const improvements = [
"Try to uncover objections earlier in the conversation",
"Consider using more specific data to support your claims",
"Practice transitioning more smoothly between topics",
"Work on your closing techniques to speed up the process"
];
const strengthsList = document.getElementById('strengths-list');
const improvementsList = document.getElementById('improvements-list');
strengthsList.innerHTML = strengths.slice(0, 2).map(s => `<li>${s}</li>`).join('');
improvementsList.innerHTML = improvements.slice(0, 2).map(i => `<li>${i}</li>`).join('');
// Switch to results screen
chatScreen.classList.add('hidden');
resultsScreen.classList.remove('hidden');
}
// End session manually
function endSessionManually() {
stopListening();
// Calculate duration
const duration = Math.floor((new Date() - sessionStartTime) / 1000);
const minutes = Math.floor(duration / 60);
const seconds = duration % 60;
// Update results screen
document.getElementById('result-character').textContent = `${characters[selectedCharacter].name} (${characters[selectedCharacter].role})`;
document.getElementById('result-scenario').textContent = scenarios[selectedScenario].title;
document.getElementById('result-duration').textContent = `${minutes}m ${seconds}s`;
document.getElementById('result-outcome').textContent = "Practice Ended";
document.getElementById('result-outcome').className = "font-medium text-blue-600";
// Generate feedback
document.getElementById('feedback-text').textContent = "You ended the session before reaching a conclusion. Here are some observations from your practice:";
const strengths = [
"You asked good probing questions",
"You demonstrated product knowledge",
"You maintained professional tone throughout",
"You listened actively to the buyer's concerns"
];
const improvements = [
"Work on handling objections more effectively",
"Practice transitioning to the close",
"Try to uncover the real decision criteria earlier",
"Use more trial closes to gauge interest"
];
const strengthsList = document.getElementById('strengths-list');
const improvementsList = document.getElementById('improvements-list');
strengthsList.innerHTML = strengths.slice(0, 2).map(s => `<li>${s}</li>`).join('');
improvementsList.innerHTML = improvements.slice(0, 2).map(i => `<li>${i}</li>`).join('');
// Switch to results screen
chatScreen.classList.add('hidden');
resultsScreen.classList.remove('hidden');
}
// Show hint
function showHint() {
if (!selectedScenario) return;
currentHints = [...scenarios[selectedScenario].hints];
currentHintIndex = 0;
hintContent.textContent = currentHints[currentHintIndex];
hintModal.classList.remove('hidden');
}
// Show next hint
function showNextHint() {
currentHintIndex = (currentHintIndex + 1) % currentHints.length;
hintContent.textContent = currentHints[currentHintIndex];
}
// Show transcript
function showTranscript() {
transcriptContent.innerHTML = messages.map(msg => {
return `<div class="mb-2">
<div class="flex justify-between items-baseline mb-1">
<span class="font-medium text-gray-800">${msg.sender}</span>
<span class="text-xs text-gray-500">${new Date(msg.timestamp).toLocaleTimeString()}</span>
</div>
<div class="text-gray-700">${msg.text}</div>
</div>`;
}).join('');
transcriptModal.classList.remove('hidden');
}
// Download transcript
function downloadSessionTranscript() {
const transcriptText = messages.map(msg => {
return `${msg.sender} (${new Date(msg.timestamp).toLocaleString()}):\n${msg.text}\n\n`;
}).join('');
const blob = new Blob([transcriptText], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `negotiation-session-${new Date().toISOString().slice(0,10)}.txt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// Event Listeners
startSession.addEventListener('click', startNewSession);
endSession.addEventListener('click', endSessionManually);
newSession.addEventListener('click', () => {
resultsScreen.classList.add('hidden');
setupScreen.classList.remove('hidden');
});
viewTranscript.addEventListener('click', showTranscript);
downloadTranscript.addEventListener('click', downloadSessionTranscript);
// Character selection
document.querySelectorAll('.character-option').forEach(option => {
option.addEventListener('click', function() {
document.querySelectorAll('.character-option').forEach(opt => {
opt.querySelector('.character-avatar').classList.remove('selected', 'border-blue-500');
});
this.querySelector('.character-avatar').classList.add('selected', 'border-blue-500');
});
});
// Scenario selection
document.querySelectorAll('.scene-card').forEach(card => {
card.addEventListener('click', function() {
document.querySelectorAll('.scene-card').forEach(c => {
c.classList.remove('selected', 'border-blue-500');
});
this.classList.add('selected', 'border-blue-500');
});
});
// Message input
sendText.addEventListener('click', () => {
sendUserMessage(textInput.value);
});
textInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendUserMessage(textInput.value);
}
});
// Voice input
voiceToggle.addEventListener('click', toggleVoiceInput);
// Hint system
hintButton.addEventListener('click', showHint);
closeHint.addEventListener('click', () => {
hintModal.classList.add('hidden');
});
nextHint.addEventListener('click', showNextHint);
// Transcript system
closeTranscript.addEventListener('click', () => {
transcriptModal.classList.add('hidden');
});
// Initialize speech synthesis voices
speechSynthesis.onvoiceschanged = function() {
// Voices are now loaded
};
// Load voices immediately if already available
if (speechSynthesis.getVoices().length > 0) {
speechSynthesis.onvoiceschanged();
}
});
</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=shaike123/moreter" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>