Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Conversation Analysis Dashboard</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> | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.3s ease-out forwards; | |
| } | |
| .sidebar { | |
| transition: transform 0.3s ease-in-out; | |
| } | |
| .sidebar-open { | |
| transform: translateX(0); | |
| } | |
| @media (max-width: 768px) { | |
| .sidebar { | |
| transform: translateX(-100%); | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| z-index: 40; | |
| height: 100vh; | |
| } | |
| .sidebar-open { | |
| transform: translateX(0); | |
| } | |
| } | |
| .conversation-card:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); | |
| } | |
| .progress-bar { | |
| transition: width 0.5s ease; | |
| } | |
| .modal-overlay { | |
| background-color: rgba(0, 0, 0, 0.5); | |
| } | |
| .modal-content { | |
| max-height: 90vh; | |
| } | |
| .line-clamp-2 { | |
| display: -webkit-box; | |
| -webkit-line-clamp: 2; | |
| -webkit-box-orient: vertical; | |
| overflow: hidden; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 font-sans antialiased"> | |
| <!-- Mobile menu button --> | |
| <div class="md:hidden fixed top-4 left-4 z-50"> | |
| <button id="mobileMenuBtn" class="p-2 rounded-md bg-white shadow-md text-gray-700"> | |
| <i class="fas fa-bars"></i> | |
| </button> | |
| </div> | |
| <!-- Sidebar --> | |
| <div id="sidebar" class="sidebar w-64 bg-white border-r border-gray-200 fixed h-full overflow-y-auto"> | |
| <div class="p-4 border-b border-gray-200"> | |
| <div class="flex items-center space-x-2"> | |
| <div class="bg-purple-600 text-white p-2 rounded-lg"> | |
| <i class="fas fa-comments text-xl"></i> | |
| </div> | |
| <h1 class="text-xl font-bold text-gray-800">ConvoAnalyzer</h1> | |
| </div> | |
| <p class="text-xs text-gray-500 mt-1">Conversation Analysis Dashboard</p> | |
| </div> | |
| <div class="p-4 space-y-6"> | |
| <div> | |
| <h3 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Configuration</h3> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Worker Count</label> | |
| <div class="flex items-center space-x-2"> | |
| <input id="workerCount" type="range" min="1" max="8" value="4" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
| <span id="workerCountDisplay" class="text-sm font-medium text-gray-700 w-8 text-center">4</span> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Analysis Model</label> | |
| <select id="modelSelect" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm rounded-md"> | |
| <option value="default">Default Model</option> | |
| <option value="advanced">Advanced Model</option> | |
| <option value="custom">Custom Model</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Actions</h3> | |
| <div class="space-y-2"> | |
| <button id="uploadBtn" class="w-full flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"> | |
| <i class="fas fa-upload mr-2"></i> Upload Conversations | |
| </button> | |
| <button id="startAnalysisBtn" class="w-full flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-purple-700 bg-white hover:bg-purple-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"> | |
| <i class="fas fa-play mr-2"></i> Start Analysis | |
| </button> | |
| <div class="flex space-x-2"> | |
| <button id="pauseBtn" disabled class="flex-1 flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-gray-700 bg-gray-200 opacity-50 cursor-not-allowed"> | |
| <i class="fas fa-pause mr-2"></i> Pause | |
| </button> | |
| <button id="stopBtn" disabled class="flex-1 flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 opacity-50 cursor-not-allowed hover:bg-red-700"> | |
| <i class="fas fa-stop mr-2"></i> Stop | |
| </button> | |
| </div> | |
| <button id="downloadBtn" class="w-full flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"> | |
| <i class="fas fa-download mr-2"></i> Download Results | |
| </button> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Agent Status</h3> | |
| <div class="space-y-2"> | |
| <div class="flex items-center justify-between p-2 bg-gray-50 rounded-md"> | |
| <div class="flex items-center space-x-2"> | |
| <i class="fas fa-smile text-blue-500"></i> | |
| <span class="text-sm font-medium text-gray-700">Sentiment</span> | |
| </div> | |
| <span id="sentimentStatus" class="text-sm font-medium text-gray-600">Idle</span> | |
| </div> | |
| <div class="flex items-center justify-between p-2 bg-gray-50 rounded-md"> | |
| <div class="flex items-center space-x-2"> | |
| <i class="fas fa-tags text-blue-500"></i> | |
| <span class="text-sm font-medium text-gray-700">Topic</span> | |
| </div> | |
| <span id="topicStatus" class="text-sm font-medium text-gray-600">Idle</span> | |
| </div> | |
| <div class="flex items-center justify-between p-2 bg-gray-50 rounded-md"> | |
| <div class="flex items-center space-x-2"> | |
| <i class="fas fa-tasks text-blue-500"></i> | |
| <span class="text-sm font-medium text-gray-700">Progress</span> | |
| </div> | |
| <span id="progressStatus" class="text-sm font-medium text-gray-600">Idle</span> | |
| </div> | |
| <div class="flex items-center justify-between p-2 bg-gray-50 rounded-md"> | |
| <div class="flex items-center space-x-2"> | |
| <i class="fas fa-file-alt text-blue-500"></i> | |
| <span class="text-sm font-medium text-gray-700">Summary</span> | |
| </div> | |
| <span id="summaryStatus" class="text-sm font-medium text-gray-600">Idle</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Main content --> | |
| <div class="md:ml-64"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <!-- Header --> | |
| <div class="pt-6 pb-4"> | |
| <div class="flex flex-col md:flex-row md:items-center md:justify-between"> | |
| <h2 class="text-2xl font-bold leading-tight text-gray-900">Conversation Analysis Dashboard</h2> | |
| <div class="mt-2 md:mt-0 flex items-center space-x-2"> | |
| <span class="text-sm text-gray-500">Last run:</span> | |
| <span id="lastRunTime" class="text-sm font-medium text-gray-700">Never</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Stats --> | |
| <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6"> | |
| <div class="bg-white overflow-hidden shadow rounded-lg"> | |
| <div class="px-4 py-5 sm:p-6"> | |
| <div class="flex items-center"> | |
| <div class="flex-shrink-0 bg-green-500 rounded-md p-3"> | |
| <i class="fas fa-check-circle text-white"></i> | |
| </div> | |
| <div class="ml-5 w-0 flex-1"> | |
| <dl> | |
| <dt class="text-sm font-medium text-gray-500 truncate">Completed</dt> | |
| <dd class="flex items-baseline"> | |
| <div class="text-2xl font-semibold text-gray-900"> | |
| <span id="completedCount">0</span> | |
| </div> | |
| </dd> | |
| </dl> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white overflow-hidden shadow rounded-lg"> | |
| <div class="px-4 py-5 sm:p-6"> | |
| <div class="flex items-center"> | |
| <div class="flex-shrink-0 bg-red-500 rounded-md p-3"> | |
| <i class="fas fa-exclamation-circle text-white"></i> | |
| </div> | |
| <div class="ml-5 w-0 flex-1"> | |
| <dl> | |
| <dt class="text-sm font-medium text-gray-500 truncate">Errors</dt> | |
| <dd class="flex items-baseline"> | |
| <div class="text-2xl font-semibold text-gray-900"> | |
| <span id="errorCount">0</span> | |
| </div> | |
| </dd> | |
| </dl> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white overflow-hidden shadow rounded-lg"> | |
| <div class="px-4 py-5 sm:p-6"> | |
| <div class="flex items-center"> | |
| <div class="flex-shrink-0 bg-blue-500 rounded-md p-3"> | |
| <i class="fas fa-spinner text-white"></i> | |
| </div> | |
| <div class="ml-5 w-0 flex-1"> | |
| <dl> | |
| <dt class="text-sm font-medium text-gray-500 truncate">Processing</dt> | |
| <dd class="flex items-baseline"> | |
| <div class="text-2xl font-semibold text-gray-900"> | |
| <span id="processingCount">0</span> | |
| </div> | |
| </dd> | |
| </dl> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white overflow-hidden shadow rounded-lg"> | |
| <div class="px-4 py-5 sm:p-6"> | |
| <div class="flex items-center"> | |
| <div class="flex-shrink-0 bg-purple-500 rounded-md p-3"> | |
| <i class="fas fa-chart-line text-white"></i> | |
| </div> | |
| <div class="ml-5 w-0 flex-1"> | |
| <dl> | |
| <dt class="text-sm font-medium text-gray-500 truncate">Progress</dt> | |
| <dd class="flex items-baseline"> | |
| <div class="text-2xl font-semibold text-gray-900"> | |
| <span id="progressPercent">0</span>% | |
| </div> | |
| </dd> | |
| </dl> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Progress bar --> | |
| <div class="mb-6"> | |
| <div class="flex justify-between mb-1"> | |
| <span class="text-sm font-medium text-gray-700">Analysis Progress</span> | |
| <span class="text-sm font-medium text-gray-700"><span id="progressPercent2">0</span>%</span> | |
| </div> | |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> | |
| <div id="progressBar" class="progress-bar bg-purple-600 h-2.5 rounded-full" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| <!-- Agent Counts --> | |
| <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6"> | |
| <div class="bg-white p-4 rounded-lg shadow"> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center space-x-2"> | |
| <div class="p-2 rounded-md bg-blue-100 text-blue-600"> | |
| <i class="fas fa-smile"></i> | |
| </div> | |
| <span class="text-sm font-medium text-gray-700">Sentiment</span> | |
| </div> | |
| <span id="sentimentCount" class="text-lg font-bold text-gray-900">0</span> | |
| </div> | |
| </div> | |
| <div class="bg-white p-4 rounded-lg shadow"> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center space-x-2"> | |
| <div class="p-2 rounded-md bg-green-100 text-green-600"> | |
| <i class="fas fa-tags"></i> | |
| </div> | |
| <span class="text-sm font-medium text-gray-700">Topics</span> | |
| </div> | |
| <span id="topicCount" class="text-lg font-bold text-gray-900">0</span> | |
| </div> | |
| </div> | |
| <div class="bg-white p-4 rounded-lg shadow"> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center space-x-2"> | |
| <div class="p-2 rounded-md bg-purple-100 text-purple-600"> | |
| <i class="fas fa-tasks"></i> | |
| </div> | |
| <span class="text-sm font-medium text-gray-700">Progress</span> | |
| </div> | |
| <span id="progressCount" class="text-lg font-bold text-gray-900">0</span> | |
| </div> | |
| </div> | |
| <div class="bg-white p-4 rounded-lg shadow"> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center space-x-2"> | |
| <div class="p-2 rounded-md bg-yellow-100 text-yellow-600"> | |
| <i class="fas fa-file-alt"></i> | |
| </div> | |
| <span class="text-sm font-medium text-gray-700">Summaries</span> | |
| </div> | |
| <span id="summaryCount" class="text-lg font-bold text-gray-900">0</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Visualizations --> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> | |
| <div class="bg-white p-4 rounded-lg shadow"> | |
| <div id="sentimentChart"> | |
| <h4 class="font-medium text-gray-900 mb-3">Sentiment Distribution</h4> | |
| <div class="text-center py-10 text-gray-400"> | |
| <i class="fas fa-chart-pie text-2xl mb-2"></i> | |
| <p class="text-sm">No sentiment data available yet</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white p-4 rounded-lg shadow"> | |
| <div id="topicChart"> | |
| <h4 class="font-medium text-gray-900 mb-3">Top Topics</h4> | |
| <div class="text-center py-10 text-gray-400"> | |
| <i class="fas fa-chart-bar text-2xl mb-2"></i> | |
| <p class="text-sm">No topic data available yet</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Results section --> | |
| <div class="bg-white shadow rounded-lg overflow-hidden mb-6"> | |
| <div class="px-4 py-5 border-b border-gray-200 sm:px-6"> | |
| <div class="flex flex-col md:flex-row md:items-center md:justify-between"> | |
| <h3 class="text-lg font-medium leading-6 text-gray-900">Conversation Results</h3> | |
| <p class="mt-1 text-sm text-gray-500 md:mt-0"> | |
| Showing <span id="showingCount" class="font-medium">0</span> of <span id="totalCount" class="font-medium">0</span> conversations | |
| </p> | |
| </div> | |
| </div> | |
| <div id="conversationResults" class="divide-y divide-gray-200"> | |
| <div class="text-center py-10 text-gray-400"> | |
| <i class="fas fa-comments text-4xl mb-2"></i> | |
| <p>No analysis results yet. Start the analysis to see results here.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Upload Modal --> | |
| <div id="uploadModal" class="fixed z-50 inset-0 overflow-y-auto hidden"> | |
| <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | |
| <div class="fixed inset-0 transition-opacity modal-overlay" aria-hidden="true"> | |
| <div class="absolute inset-0 bg-gray-500 opacity-75"></div> | |
| </div> | |
| <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> | |
| <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"> | |
| <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | |
| <div class="sm:flex sm:items-start"> | |
| <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-purple-100 sm:mx-0 sm:h-10 sm:w-10"> | |
| <i class="fas fa-file-upload text-purple-600"></i> | |
| </div> | |
| <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full"> | |
| <h3 class="text-lg leading-6 font-medium text-gray-900">Upload Conversations</h3> | |
| <div class="mt-4"> | |
| <div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md"> | |
| <div class="space-y-1 text-center"> | |
| <div class="flex text-sm text-gray-600"> | |
| <label for="fileUpload" class="relative cursor-pointer bg-white rounded-md font-medium text-purple-600 hover:text-purple-500 focus-within:outline-none"> | |
| <span>Upload a file</span> | |
| <input id="fileUpload" name="fileUpload" type="file" class="sr-only"> | |
| </label> | |
| <p class="pl-1">or drag and drop</p> | |
| </div> | |
| <p class="text-xs text-gray-500">CSV, JSON, or TXT files up to 10MB</p> | |
| </div> | |
| </div> | |
| <div class="mt-4"> | |
| <p class="text-sm text-gray-500">Selected file: <span id="fileNameDisplay" class="font-medium">None</span></p> | |
| </div> | |
| <div class="mt-4"> | |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> | |
| <div id="uploadProgress" class="bg-purple-600 h-2.5 rounded-full" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | |
| <button id="confirmUpload" type="button" disabled class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-purple-600 text-base font-medium text-white hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 sm:ml-3 sm:w-auto sm:text-sm"> | |
| Upload and Extract | |
| </button> | |
| <button id="closeUploadModal" type="button" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> | |
| Cancel | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Analysis Details Modal --> | |
| <div id="analysisModal" class="fixed z-50 inset-0 overflow-y-auto hidden"> | |
| <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | |
| <div class="fixed inset-0 transition-opacity modal-overlay" aria-hidden="true"> | |
| <div class="absolute inset-0 bg-gray-500 opacity-75"></div> | |
| </div> | |
| <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> | |
| <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-2xl sm:w-full"> | |
| <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | |
| <div class="sm:flex sm:items-start"> | |
| <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> | |
| <i class="fas fa-chart-bar text-blue-600"></i> | |
| </div> | |
| <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full"> | |
| <h3 class="text-lg leading-6 font-medium text-gray-900">Conversation Analysis Details</h3> | |
| <div class="mt-4"> | |
| <div class="bg-gray-50 p-4 rounded-lg mb-4"> | |
| <h4 class="font-medium text-gray-900 mb-2">Summary</h4> | |
| <p id="modalSummary" class="text-sm text-gray-700">Loading summary...</p> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"> | |
| <div> | |
| <h4 class="font-medium text-gray-900 mb-2">Success Indicators</h4> | |
| <div id="successIndicators" class="space-y-2"> | |
| <div class="bg-green-50 border border-green-100 p-3 rounded-lg"> | |
| <p class="text-sm text-green-800">Positive engagement throughout the conversation</p> | |
| </div> | |
| <div class="bg-green-50 border border-green-100 p-3 rounded-lg"> | |
| <p class="text-sm text-green-800">Clear communication of key points</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 class="font-medium text-gray-900 mb-2">Struggle Indicators</h4> | |
| <div id="struggleIndicators" class="space-y-2"> | |
| <div class="bg-red-50 border border-red-100 p-3 rounded-lg"> | |
| <p class="text-sm text-red-800">Some confusion around technical terms</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <h4 class="font-medium text-gray-900 mb-2">Main Topics</h4> | |
| <div id="mainTopics" class="flex flex-wrap gap-1"> | |
| <span class="inline-block bg-white px-2 py-1 rounded-md text-xs mr-1 mb-1">Product Features</span> | |
| <span class="inline-block bg-white px-2 py-1 rounded-md text-xs mr-1 mb-1">Pricing</span> | |
| <span class="inline-block bg-white px-2 py-1 rounded-md text-xs mr-1 mb-1">Implementation</span> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div class="bg-gray-50 p-3 rounded-lg"> | |
| <h4 class="font-medium text-gray-900 mb-2">Sentiment Trend</h4> | |
| <div class="flex items-center"> | |
| <i class="fas fa-smile text-green-600 text-sm mr-2"></i> | |
| <span id="sentimentTrend" class="text-sm font-medium text-green-600">Mostly Positive</span> | |
| </div> | |
| </div> | |
| <div class="bg-gray-50 p-3 rounded-lg"> | |
| <h4 class="font-medium text-gray-900 mb-2">Key Learning</h4> | |
| <p id="keyLearning" class="text-sm text-gray-700">The customer was particularly interested in integration capabilities with their existing systems.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | |
| <button id="closeModal" type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | |
| Close | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // DOM Elements | |
| const mobileMenuBtn = document.getElementById('mobileMenuBtn'); | |
| const sidebar = document.getElementById('sidebar'); | |
| const workerCountInput = document.getElementById('workerCount'); | |
| const workerCountDisplay = document.getElementById('workerCountDisplay'); | |
| const uploadBtn = document.getElementById('uploadBtn'); | |
| const uploadModal = document.getElementById('uploadModal'); | |
| const closeUploadModalBtn = document.getElementById('closeUploadModal'); | |
| const fileUploadInput = document.getElementById('fileUpload'); | |
| const fileNameDisplay = document.getElementById('fileNameDisplay'); | |
| const confirmUploadBtn = document.getElementById('confirmUpload'); | |
| const uploadProgress = document.getElementById('uploadProgress'); | |
| const startAnalysisBtn = document.getElementById('startAnalysisBtn'); | |
| const lastRunTimeSpan = document.getElementById('lastRunTime'); | |
| const completedCountSpan = document.getElementById('completedCount'); | |
| const errorCountSpan = document.getElementById('errorCount'); | |
| const processingCountSpan = document.getElementById('processingCount'); | |
| const progressPercentSpan = document.getElementById('progressPercent'); | |
| const progressPercentSpan2 = document.getElementById('progressPercent2'); | |
| const progressBarDiv = document.getElementById('progressBar'); | |
| const sentimentCountSpan = document.getElementById('sentimentCount'); | |
| const topicCountSpan = document.getElementById('topicCount'); | |
| const progressCountSpan = document.getElementById('progressCount'); | |
| const summaryCountSpan = document.getElementById('summaryCount'); | |
| const sentimentStatusSpan = document.getElementById('sentimentStatus'); | |
| const topicStatusSpan = document.getElementById('topicStatus'); | |
| const progressStatusSpan = document.getElementById('progressStatus'); | |
| const summaryStatusSpan = document.getElementById('summaryStatus'); | |
| const resultsContainer = document.getElementById('conversationResults'); | |
| const showingCountSpan = document.getElementById('showingCount'); | |
| const totalCountSpan = document.getElementById('totalCount'); | |
| const analysisModal = document.getElementById('analysisModal'); | |
| const closeModalBtn = document.getElementById('closeModal'); | |
| const modalSummary = document.getElementById('modalSummary'); | |
| const successIndicatorsDiv = document.getElementById('successIndicators'); | |
| const struggleIndicatorsDiv = document.getElementById('struggleIndicators'); | |
| const mainTopicsDiv = document.getElementById('mainTopics'); | |
| const sentimentTrendSpan = document.getElementById('sentimentTrend'); | |
| const sentimentTrendIcon = document.querySelector('#sentimentTrend').previousElementSibling; | |
| const keyLearningPara = document.getElementById('keyLearning'); | |
| const downloadBtn = document.getElementById('downloadBtn'); | |
| const pauseBtn = document.getElementById('pauseBtn'); | |
| const stopBtn = document.getElementById('stopBtn'); | |
| const modelSelect = document.getElementById('modelSelect'); | |
| const sentimentChartDiv = document.getElementById('sentimentChart'); | |
| const topicChartDiv = document.getElementById('topicChart'); | |
| // Sample data for demonstration | |
| const sampleResults = [ | |
| { | |
| filename: "Customer Support Call - 2023-05-15", | |
| date: "May 15, 2023", | |
| sentiment: "Positive", | |
| summary_snippet: "The customer was satisfied with the resolution provided and appreciated the quick response time.", | |
| topics: ["Support", "Resolution", "Response Time"], | |
| exchanges: 12, | |
| has_details: true | |
| }, | |
| { | |
| filename: "Sales Demo - Acme Corp", | |
| date: "May 10, 2023", | |
| sentiment: "Neutral", | |
| summary_snippet: "The prospect showed interest in the product but had concerns about pricing and implementation timeline.", | |
| topics: ["Pricing", "Implementation", "Features"], | |
| exchanges: 24, | |
| has_details: true | |
| }, | |
| { | |
| filename: "Team Meeting - Project Kickoff", | |
| date: "May 5, 2023", | |
| sentiment: "Positive", | |
| summary_snippet: "The team aligned on project goals and timelines with clear action items assigned.", | |
| topics: ["Goals", "Timelines", "Action Items"], | |
| exchanges: 18, | |
| has_details: true | |
| } | |
| ]; | |
| // Event Listeners | |
| mobileMenuBtn.addEventListener('click', function() { | |
| sidebar.classList.toggle('sidebar-open'); | |
| }); | |
| // Close sidebar when clicking outside on mobile | |
| document.addEventListener('click', function(event) { | |
| if (!sidebar.contains(event.target) && !mobileMenuBtn.contains(event.target) && sidebar.classList.contains('sidebar-open') && window.innerWidth <= 768) { | |
| sidebar.classList.remove('sidebar-open'); | |
| } | |
| }); | |
| // Worker count display | |
| workerCountInput.addEventListener('input', function() { | |
| workerCountDisplay.textContent = this.value; | |
| }); | |
| // File upload modal | |
| uploadBtn.addEventListener('click', function() { | |
| uploadModal.classList.remove('hidden'); | |
| }); | |
| closeUploadModalBtn.addEventListener('click', function() { | |
| uploadModal.classList.add('hidden'); | |
| resetUploadModal(); | |
| }); | |
| // Reset upload modal UI | |
| function resetUploadModal() { | |
| fileUploadInput.value = ''; | |
| fileNameDisplay.textContent = 'None'; | |
| confirmUploadBtn.disabled = true; | |
| uploadProgress.style.width = '0%'; | |
| } | |
| // File selection handler | |
| fileUploadInput.addEventListener('change', function(e) { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| fileNameDisplay.textContent = file.name; | |
| confirmUploadBtn.disabled = false; | |
| } else { | |
| resetUploadModal(); | |
| } | |
| }); | |
| // Confirm upload button | |
| confirmUploadBtn.addEventListener('click', function() { | |
| const file = fileUploadInput.files[0]; | |
| if (!file) return; | |
| // Simulate upload progress | |
| let progress = 0; | |
| const interval = setInterval(() => { | |
| progress += 10; | |
| uploadProgress.style.width = `${progress}%`; | |
| if (progress >= 100) { | |
| clearInterval(interval); | |
| setTimeout(() => { | |
| // Update UI with sample data | |
| totalCountSpan.textContent = sampleResults.length; | |
| showingCountSpan.textContent = 0; | |
| // Reset analysis progress | |
| completedCountSpan.textContent = '0'; | |
| errorCountSpan.textContent = '0'; | |
| processingCountSpan.textContent = '0'; | |
| progressPercentSpan.textContent = '0'; | |
| progressPercentSpan2.textContent = '0'; | |
| progressBarDiv.style.width = '0%'; | |
| // Update agent statuses | |
| updateAgentStatuses("Ready"); | |
| // Close modal after upload | |
| uploadModal.classList.add('hidden'); | |
| resetUploadModal(); | |
| // Show success message | |
| alert('File uploaded successfully! 3 conversations extracted.'); | |
| }, 500); | |
| } | |
| }, 200); | |
| }); | |
| // Update agent statuses | |
| function updateAgentStatuses(status) { | |
| const statusInfo = { | |
| "Idle": { text: "Idle", color: "gray" }, | |
| "Ready": { text: "Ready", color: "blue" }, | |
| "Active": { text: "Active", color: "blue" }, | |
| "Completed": { text: "Completed", color: "green" }, | |
| "Error": { text: "Error", color: "red" } | |
| }; | |
| const currentStatus = statusInfo[status] || statusInfo["Idle"]; | |
| sentimentStatusSpan.textContent = currentStatus.text; | |
| sentimentStatusSpan.className = `text-sm font-medium text-${currentStatus.color}-600`; | |
| topicStatusSpan.textContent = currentStatus.text; | |
| topicStatusSpan.className = `text-sm font-medium text-${currentStatus.color}-600`; | |
| progressStatusSpan.textContent = currentStatus.text; | |
| progressStatusSpan.className = `text-sm font-medium text-${currentStatus.color}-600`; | |
| summaryStatusSpan.textContent = currentStatus.text; | |
| summaryStatusSpan.className = `text-sm font-medium text-${currentStatus.color}-600`; | |
| } | |
| // Start analysis button | |
| startAnalysisBtn.addEventListener('click', function() { | |
| const model = modelSelect.value; | |
| // Disable button and show loading | |
| startAnalysisBtn.disabled = true; | |
| startAnalysisBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Starting...'; | |
| startAnalysisBtn.classList.add('opacity-50', 'cursor-not-allowed'); | |
| // Simulate analysis progress | |
| setTimeout(() => { | |
| // Update last run time | |
| lastRunTimeSpan.textContent = new Date().toLocaleString(); | |
| // Update agent statuses | |
| updateAgentStatuses("Active"); | |
| // Enable pause/stop buttons | |
| pauseBtn.disabled = false; | |
| stopBtn.disabled = false; | |
| // Simulate analysis progress | |
| let progress = 0; | |
| const analysisInterval = setInterval(() => { | |
| progress += 5; | |
| // Update progress | |
| progressPercentSpan.textContent = progress; | |
| progressPercentSpan2.textContent = progress; | |
| progressBarDiv.style.width = `${progress}%`; | |
| // Update counts | |
| completedCountSpan.textContent = Math.floor(progress / 100 * sampleResults.length); | |
| processingCountSpan.textContent = sampleResults.length - Math.floor(progress / 100 * sampleResults.length); | |
| // Update agent counts | |
| sentimentCountSpan.textContent = Math.floor(progress / 30); | |
| topicCountSpan.textContent = Math.floor(progress / 25); | |
| progressCountSpan.textContent = Math.floor(progress / 20); | |
| summaryCountSpan.textContent = Math.floor(progress / 35); | |
| // When analysis completes | |
| if (progress >= 100) { | |
| clearInterval(analysisInterval); | |
| // Update UI for completion | |
| updateAgentStatuses("Completed"); | |
| startAnalysisBtn.disabled = false; | |
| startAnalysisBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Analysis Complete'; | |
| startAnalysisBtn.classList.remove('opacity-50', 'cursor-not-allowed'); | |
| startAnalysisBtn.classList.add('bg-green-100', 'text-green-700', 'hover:bg-green-200'); | |
| // Disable pause/stop buttons | |
| pauseBtn.disabled = true; | |
| stopBtn.disabled = true; | |
| // Show results | |
| updateResultsList(); | |
| updateVisualizations(); | |
| } | |
| }, 300); | |
| }, 1000); | |
| }); | |
| // Update results list | |
| function updateResultsList() { | |
| resultsContainer.innerHTML = ''; | |
| sampleResults.forEach((result, index) => { | |
| const card = document.createElement('div'); | |
| card.className = 'conversation-card bg-white border border-gray-200 rounded-lg overflow-hidden shadow-sm transition cursor-pointer mb-4 fade-in'; | |
| card.style.animationDelay = `${index * 0.1}s`; | |
| // Determine sentiment color | |
| let sentimentClass = 'bg-gray-100 text-gray-800'; | |
| if (result.sentiment.toLowerCase() === 'positive') { | |
| sentimentClass = 'bg-green-100 text-green-800'; | |
| } else if (result.sentiment.toLowerCase() === 'negative') { | |
| sentimentClass = 'bg-red-100 text-red-800'; | |
| } | |
| card.innerHTML = ` | |
| <div class="p-4 border-b border-gray-200"> | |
| <div class="flex justify-between items-start"> | |
| <div> | |
| <h4 class="font-medium text-gray-900 truncate">${result.filename}</h4> | |
| <p class="text-xs text-gray-500 mt-1">${result.date}</p> | |
| </div> | |
| <span class="px-2 py-1 rounded-full text-xs font-medium ${sentimentClass}"> | |
| ${result.sentiment} | |
| </span> | |
| </div> | |
| </div> | |
| <div class="p-4"> | |
| <p class="text-sm text-gray-600 line-clamp-2">${result.summary_snippet}</p> | |
| <div class="mt-3 flex flex-wrap gap-1"> | |
| ${result.topics.map(topic => | |
| `<span class="inline-block bg-gray-100 px-2 py-1 rounded-md text-xs">${topic}</span>` | |
| ).join('')} | |
| </div> | |
| </div> | |
| <div class="px-4 py-2 bg-gray-50 border-t border-gray-200 flex justify-between items-center"> | |
| <span class="text-xs text-gray-500">${result.exchanges} exchanges</span> | |
| <button class="text-blue-600 hover:text-blue-800 text-sm font-medium" data-filename="${result.filename}"> | |
| View Details | |
| </button> | |
| </div> | |
| `; | |
| // Add click handler to show modal | |
| const detailButton = card.querySelector('button'); | |
| detailButton.addEventListener('click', function(e) { | |
| e.stopPropagation(); | |
| showAnalysisModal(result.filename); | |
| }); | |
| card.addEventListener('click', function() { | |
| showAnalysisModal(result.filename); | |
| }); | |
| resultsContainer.appendChild(card); | |
| }); | |
| showingCountSpan.textContent = sampleResults.length; | |
| totalCountSpan.textContent = sampleResults.length; | |
| } | |
| // Show analysis modal with details | |
| function showAnalysisModal(filename) { | |
| // Find the selected conversation | |
| const conversation = sampleResults.find(r => r.filename === filename); | |
| if (!conversation) return; | |
| // Update modal content | |
| modalSummary.textContent = conversation.summary_snippet; | |
| // Update success indicators | |
| successIndicatorsDiv.innerHTML = ` | |
| <div class="bg-green-50 border border-green-100 p-3 rounded-lg"> | |
| <p class="text-sm text-green-800">Positive engagement throughout the conversation</p> | |
| </div> | |
| <div class="bg-green-50 border border-green-100 p-3 rounded-lg"> | |
| <p class="text-sm text-green-800">Clear communication of key points</p> | |
| </div> | |
| `; | |
| // Update struggle indicators | |
| struggleIndicatorsDiv.innerHTML = ` | |
| <div class="bg-red-50 border border-red-100 p-3 rounded-lg"> | |
| <p class="text-sm text-red-800">Some confusion around technical terms</p> | |
| </div> | |
| `; | |
| // Update main topics | |
| mainTopicsDiv.innerHTML = conversation.topics.map(topic => | |
| `<span class="inline-block bg-white px-2 py-1 rounded-md text-xs mr-1 mb-1">${topic}</span>` | |
| ).join(''); | |
| // Update sentiment trend | |
| let sentimentIcon, sentimentColor, sentimentText; | |
| if (conversation.sentiment.toLowerCase() === 'positive') { | |
| sentimentIcon = 'fa-smile text-green-600'; | |
| sentimentColor = 'text-green-600'; | |
| sentimentText = 'Mostly Positive'; | |
| } else if (conversation.sentiment.toLowerCase() === 'negative') { | |
| sentimentIcon = 'fa-frown text-red-600'; | |
| sentimentColor = 'text-red-600'; | |
| sentimentText = 'Mostly Negative'; | |
| } else { | |
| sentimentIcon = 'fa-meh text-gray-500'; | |
| sentimentColor = 'text-gray-600'; | |
| sentimentText = 'Neutral'; | |
| } | |
| sentimentTrendIcon.className = `fas ${sentimentIcon} text-sm mr-2`; | |
| sentimentTrendSpan.textContent = sentimentText; | |
| sentimentTrendSpan.className = `text-sm font-medium ${sentimentColor}`; | |
| // Update key learning | |
| keyLearningPara.textContent = "The customer was particularly interested in integration capabilities with their existing systems."; | |
| // Show modal | |
| analysisModal.classList.remove('hidden'); | |
| } | |
| // Close modal | |
| closeModalBtn.addEventListener('click', function() { | |
| analysisModal.classList.add('hidden'); | |
| }); | |
| // Download results | |
| downloadBtn.addEventListener('click', function() { | |
| alert('Downloading analysis results...'); | |
| }); | |
| // Pause analysis | |
| pauseBtn.addEventListener('click', function() { | |
| alert('Analysis paused. Click Start to resume.'); | |
| updateAgentStatuses("Ready"); | |
| }); | |
| // Stop analysis | |
| stopBtn.addEventListener('click', function() { | |
| if (confirm('Are you sure you want to stop the analysis? Progress will be saved.')) { | |
| alert('Analysis stopped.'); | |
| updateAgentStatuses("Ready"); | |
| } | |
| }); | |
| // Update visualizations | |
| function updateVisualizations() { | |
| // Sentiment distribution | |
| const sentimentCounts = sampleResults.reduce((acc, res) => { | |
| const sentiment = res.sentiment || 'N/A'; | |
| acc[sentiment] = (acc[sentiment] || 0) + 1; | |
| return acc; | |
| }, {}); | |
| let sentimentHtml = '<h4 class="font-medium text-gray-900 mb-3">Sentiment Distribution</h4>'; | |
| sentimentHtml += '<ul class="list-disc list-inside text-sm text-gray-700">'; | |
| for (const [sentiment, count] of Object.entries(sentimentCounts)) { | |
| let colorClass = 'text-gray-600'; | |
| if (sentiment.toLowerCase() === 'positive') colorClass = 'text-green-600'; | |
| else if (sentiment.toLowerCase() === 'negative') colorClass = 'text-red-600'; | |
| sentimentHtml += `<li class="${colorClass}">${sentiment}: ${count}</li>`; | |
| } | |
| sentimentHtml += '</ul>'; | |
| sentimentChartDiv.innerHTML = sentimentHtml; | |
| // Top topics | |
| const allTopics = sampleResults.flatMap(res => res.topics || []); | |
| const topicCounts = allTopics.reduce((acc, topic) => { | |
| acc[topic] = (acc[topic] || 0) + 1; | |
| return acc; | |
| }, {}); | |
| const sortedTopics = Object.entries(topicCounts).sort(([, a], [, b]) => b - a); | |
| let topicHtml = '<h4 class="font-medium text-gray-900 mb-3">Top Topics</h4>'; | |
| topicHtml += '<ul class="list-disc list-inside text-sm text-gray-700">'; | |
| sortedTopics.slice(0, 5).forEach(([topic, count]) => { | |
| topicHtml += `<li>${topic}: ${count}</li>`; | |
| }); | |
| topicHtml += '</ul>'; | |
| topicChartDiv.innerHTML = topicHtml; | |
| } | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Set initial values | |
| workerCountDisplay.textContent = workerCountInput.value; | |
| lastRunTimeSpan.textContent = 'Never'; | |
| // Add fade-in class to cards when they're added | |
| const observer = new MutationObserver(function(mutations) { | |
| mutations.forEach(function(mutation) { | |
| mutation.addedNodes.forEach(function(node) { | |
| if (node.classList && node.classList.contains('conversation-card')) { | |
| node.classList.add('fade-in'); | |
| } | |
| }); | |
| }); | |
| }); | |
| observer.observe(resultsContainer, { | |
| childList: true, | |
| subtree: true | |
| }); | |
| }); | |
| </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=Ultronprime/conv" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |