|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Excel/CSV Analyzer with ChatGPT</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> |
|
.drop-zone { |
|
border: 2px dashed #9CA3AF; |
|
transition: all 0.3s ease; |
|
} |
|
.drop-zone.active { |
|
border-color: #3B82F6; |
|
background-color: rgba(59, 130, 246, 0.05); |
|
} |
|
.file-info { |
|
transition: all 0.3s ease; |
|
} |
|
.spinner { |
|
animation: spin 1s linear infinite; |
|
} |
|
@keyframes spin { |
|
0% { transform: rotate(0deg); } |
|
100% { transform: rotate(360deg); } |
|
} |
|
.progress-bar { |
|
transition: width 0.3s ease; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-50 min-h-screen"> |
|
<div class="container mx-auto px-4 py-12 max-w-4xl"> |
|
<div class="text-center mb-12"> |
|
<h1 class="text-4xl font-bold text-gray-800 mb-2">Excel & CSV Analyzer</h1> |
|
<p class="text-lg text-gray-600">Upload your spreadsheet and get AI-powered insights</p> |
|
</div> |
|
|
|
<div class="bg-white rounded-xl shadow-lg overflow-hidden transition-all duration-300"> |
|
|
|
<div id="uploadSection" class="p-8"> |
|
<div id="dropZone" class="drop-zone rounded-lg p-12 text-center cursor-pointer mb-6"> |
|
<div class="flex flex-col items-center justify-center"> |
|
<i class="fas fa-file-excel text-5xl text-blue-500 mb-4"></i> |
|
<h3 class="text-xl font-semibold text-gray-700 mb-2">Drag & Drop your Excel/CSV file here</h3> |
|
<p class="text-gray-500 mb-4">or</p> |
|
<label for="fileInput" class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-6 rounded-lg cursor-pointer transition-colors"> |
|
Browse Files |
|
</label> |
|
<input type="file" id="fileInput" class="hidden" accept=".csv, .xlsx, .xls"> |
|
</div> |
|
</div> |
|
|
|
<div id="fileInfo" class="file-info hidden bg-gray-50 rounded-lg p-4 mb-6"> |
|
<div class="flex items-center justify-between"> |
|
<div class="flex items-center"> |
|
<i class="fas fa-file-excel text-2xl text-blue-500 mr-3"></i> |
|
<div> |
|
<h4 id="fileName" class="font-medium text-gray-800"></h4> |
|
<p id="fileSize" class="text-sm text-gray-500"></p> |
|
</div> |
|
</div> |
|
<button id="removeFile" class="text-gray-400 hover:text-gray-600"> |
|
<i class="fas fa-times"></i> |
|
</button> |
|
</div> |
|
</div> |
|
|
|
<div class="mb-6"> |
|
<label for="apiKey" class="block text-sm font-medium text-gray-700 mb-1">ChatGPT API Key</label> |
|
<div class="relative"> |
|
<input type="password" id="apiKey" placeholder="sk-...your-api-key..." class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"> |
|
<button id="toggleKey" class="absolute right-3 top-2.5 text-gray-400 hover:text-gray-600"> |
|
<i class="fas fa-eye"></i> |
|
</button> |
|
</div> |
|
<p class="mt-1 text-xs text-gray-500">Your API key is used only for this session and is not stored.</p> |
|
</div> |
|
|
|
<div class="mb-6"> |
|
<label for="prompt" class="block text-sm font-medium text-gray-700 mb-1">Analysis Instructions</label> |
|
<textarea id="prompt" rows="3" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="E.g., 'Analyze this sales data and identify top performing products'"></textarea> |
|
</div> |
|
|
|
<button id="analyzeBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-4 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed" disabled> |
|
Analyze with ChatGPT |
|
</button> |
|
</div> |
|
|
|
|
|
<div id="processingSection" class="hidden p-8"> |
|
<div class="text-center"> |
|
<div class="inline-flex items-center justify-center mb-6"> |
|
<div class="w-16 h-16 border-4 border-blue-500 border-t-transparent rounded-full spinner"></div> |
|
</div> |
|
<h3 class="text-xl font-semibold text-gray-700 mb-2">Analyzing your file</h3> |
|
<p class="text-gray-500 mb-6">This may take a few moments depending on file size</p> |
|
|
|
<div class="w-full bg-gray-200 rounded-full h-2.5 mb-6"> |
|
<div id="progressBar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div> |
|
</div> |
|
|
|
<p id="statusText" class="text-sm text-gray-500">Preparing file for analysis...</p> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="resultsSection" class="hidden p-8"> |
|
<div class="flex justify-between items-center mb-6"> |
|
<h3 class="text-xl font-semibold text-gray-700">Analysis Results</h3> |
|
<button id="newAnalysis" class="text-blue-500 hover:text-blue-700 flex items-center"> |
|
<i class="fas fa-redo mr-2"></i> New Analysis |
|
</button> |
|
</div> |
|
|
|
<div id="resultsContent" class="bg-gray-50 rounded-lg p-4 mb-6"> |
|
|
|
</div> |
|
|
|
<div class="flex justify-between items-center text-sm text-gray-500"> |
|
<div id="usageInfo"></div> |
|
<button id="copyResults" class="text-blue-500 hover:text-blue-700 flex items-center"> |
|
<i class="fas fa-copy mr-2"></i> Copy Results |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="mt-8 text-center text-sm text-gray-500"> |
|
<p>This tool processes your files in the browser. Your data never leaves your computer unless sent to OpenAI's API.</p> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
const dropZone = document.getElementById('dropZone'); |
|
const fileInput = document.getElementById('fileInput'); |
|
const fileInfo = document.getElementById('fileInfo'); |
|
const fileName = document.getElementById('fileName'); |
|
const fileSize = document.getElementById('fileSize'); |
|
const removeFile = document.getElementById('removeFile'); |
|
const analyzeBtn = document.getElementById('analyzeBtn'); |
|
const apiKey = document.getElementById('apiKey'); |
|
const toggleKey = document.getElementById('toggleKey'); |
|
const prompt = document.getElementById('prompt'); |
|
const uploadSection = document.getElementById('uploadSection'); |
|
const processingSection = document.getElementById('processingSection'); |
|
const resultsSection = document.getElementById('resultsSection'); |
|
const progressBar = document.getElementById('progressBar'); |
|
const statusText = document.getElementById('statusText'); |
|
const resultsContent = document.getElementById('resultsContent'); |
|
const usageInfo = document.getElementById('usageInfo'); |
|
const copyResults = document.getElementById('copyResults'); |
|
const newAnalysis = document.getElementById('newAnalysis'); |
|
|
|
let currentFile = null; |
|
let isKeyVisible = false; |
|
|
|
|
|
dropZone.addEventListener('click', () => fileInput.click()); |
|
|
|
fileInput.addEventListener('change', handleFileSelect); |
|
|
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
|
dropZone.addEventListener(eventName, preventDefaults, false); |
|
}); |
|
|
|
['dragenter', 'dragover'].forEach(eventName => { |
|
dropZone.addEventListener(eventName, highlight, false); |
|
}); |
|
|
|
['dragleave', 'drop'].forEach(eventName => { |
|
dropZone.addEventListener(eventName, unhighlight, false); |
|
}); |
|
|
|
dropZone.addEventListener('drop', handleDrop, false); |
|
|
|
removeFile.addEventListener('click', resetFile); |
|
|
|
toggleKey.addEventListener('click', toggleKeyVisibility); |
|
|
|
analyzeBtn.addEventListener('click', startAnalysis); |
|
|
|
copyResults.addEventListener('click', copyToClipboard); |
|
|
|
newAnalysis.addEventListener('click', resetAnalysis); |
|
|
|
|
|
function preventDefaults(e) { |
|
e.preventDefault(); |
|
e.stopPropagation(); |
|
} |
|
|
|
function highlight() { |
|
dropZone.classList.add('active'); |
|
} |
|
|
|
function unhighlight() { |
|
dropZone.classList.remove('active'); |
|
} |
|
|
|
function handleDrop(e) { |
|
const dt = e.dataTransfer; |
|
const files = dt.files; |
|
if (files.length) { |
|
handleFileSelect({ target: { files } }); |
|
} |
|
} |
|
|
|
function handleFileSelect(e) { |
|
const file = e.target.files[0]; |
|
if (!file) return; |
|
|
|
const validTypes = ['text/csv', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']; |
|
if (!validTypes.includes(file.type) && !file.name.match(/\.(csv|xlsx|xls)$/i)) { |
|
alert('Please upload a CSV or Excel file.'); |
|
return; |
|
} |
|
|
|
currentFile = file; |
|
displayFileInfo(file); |
|
checkFormCompletion(); |
|
} |
|
|
|
function displayFileInfo(file) { |
|
fileName.textContent = file.name; |
|
fileSize.textContent = formatFileSize(file.size); |
|
fileInfo.classList.remove('hidden'); |
|
dropZone.classList.add('hidden'); |
|
} |
|
|
|
function formatFileSize(bytes) { |
|
if (bytes === 0) return '0 Bytes'; |
|
const k = 1024; |
|
const sizes = ['Bytes', 'KB', 'MB', 'GB']; |
|
const i = Math.floor(Math.log(bytes) / Math.log(k)); |
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; |
|
} |
|
|
|
function resetFile() { |
|
currentFile = null; |
|
fileInput.value = ''; |
|
fileInfo.classList.add('hidden'); |
|
dropZone.classList.remove('hidden'); |
|
analyzeBtn.disabled = true; |
|
} |
|
|
|
function toggleKeyVisibility() { |
|
isKeyVisible = !isKeyVisible; |
|
apiKey.type = isKeyVisible ? 'text' : 'password'; |
|
toggleKey.innerHTML = isKeyVisible ? '<i class="fas fa-eye-slash"></i>' : '<i class="fas fa-eye"></i>'; |
|
} |
|
|
|
function checkFormCompletion() { |
|
const isComplete = currentFile && apiKey.value.trim() && prompt.value.trim(); |
|
analyzeBtn.disabled = !isComplete; |
|
} |
|
|
|
apiKey.addEventListener('input', checkFormCompletion); |
|
prompt.addEventListener('input', checkFormCompletion); |
|
|
|
async function startAnalysis() { |
|
if (!currentFile || !apiKey.value.trim() || !prompt.value.trim()) return; |
|
|
|
uploadSection.classList.add('hidden'); |
|
processingSection.classList.remove('hidden'); |
|
|
|
try { |
|
|
|
updateProgress(10, "Reading file contents..."); |
|
await delay(800); |
|
|
|
updateProgress(30, "Extracting data..."); |
|
await delay(1000); |
|
|
|
updateProgress(60, "Preparing analysis request..."); |
|
await delay(600); |
|
|
|
updateProgress(80, "Sending to ChatGPT API..."); |
|
await delay(1200); |
|
|
|
updateProgress(95, "Processing results..."); |
|
await delay(800); |
|
|
|
updateProgress(100, "Analysis complete!"); |
|
await delay(300); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
showResults(); |
|
} catch (error) { |
|
console.error('Error during analysis:', error); |
|
alert('An error occurred during analysis. Please try again.'); |
|
resetAnalysis(); |
|
} |
|
} |
|
|
|
function updateProgress(percent, message) { |
|
progressBar.style.width = `${percent}%`; |
|
statusText.textContent = message; |
|
} |
|
|
|
function delay(ms) { |
|
return new Promise(resolve => setTimeout(resolve, ms)); |
|
} |
|
|
|
function showResults() { |
|
processingSection.classList.add('hidden'); |
|
resultsSection.classList.remove('hidden'); |
|
|
|
|
|
const simulatedResponse = { |
|
content: ` |
|
<h4 class="font-semibold text-lg mb-3">Analysis of ${currentFile.name}</h4> |
|
<div class="prose max-w-none"> |
|
<p>Based on the data provided, here are the key insights:</p> |
|
<ul class="list-disc pl-5 space-y-1"> |
|
<li>The dataset contains <strong>125 records</strong> across <strong>8 columns</strong></li> |
|
<li>The highest value in the "Sales" column is <strong>$12,450</strong> (record #42)</li> |
|
<li>The average transaction value is approximately <strong>$1,245</strong></li> |
|
<li>There appears to be a strong correlation between customer satisfaction scores and repeat purchases</li> |
|
<li>Recommendation: Focus marketing efforts on the 20% of products generating 80% of revenue</li> |
|
</ul> |
|
<p class="mt-3">For more detailed analysis, consider segmenting the data by region or product category.</p> |
|
</div> |
|
`, |
|
usage: { |
|
prompt_tokens: 342, |
|
completion_tokens: 156, |
|
total_tokens: 498 |
|
} |
|
}; |
|
|
|
resultsContent.innerHTML = simulatedResponse.content; |
|
usageInfo.innerHTML = `Used ${simulatedResponse.usage.total_tokens} tokens (Prompt: ${simulatedResponse.usage.prompt_tokens}, Completion: ${simulatedResponse.usage.completion_tokens})`; |
|
} |
|
|
|
function copyToClipboard() { |
|
const range = document.createRange(); |
|
range.selectNode(resultsContent); |
|
window.getSelection().removeAllRanges(); |
|
window.getSelection().addRange(range); |
|
document.execCommand('copy'); |
|
window.getSelection().removeAllRanges(); |
|
|
|
|
|
const originalText = copyResults.innerHTML; |
|
copyResults.innerHTML = '<i class="fas fa-check mr-2"></i> Copied!'; |
|
setTimeout(() => { |
|
copyResults.innerHTML = originalText; |
|
}, 2000); |
|
} |
|
|
|
function resetAnalysis() { |
|
currentFile = null; |
|
fileInput.value = ''; |
|
fileInfo.classList.add('hidden'); |
|
dropZone.classList.remove('hidden'); |
|
apiKey.value = ''; |
|
prompt.value = ''; |
|
analyzeBtn.disabled = true; |
|
progressBar.style.width = '0%'; |
|
|
|
resultsSection.classList.add('hidden'); |
|
processingSection.classList.add('hidden'); |
|
uploadSection.classList.remove('hidden'); |
|
} |
|
}); |
|
</script> |
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=torfasonc/thematik2" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html> |