PRISM / templates /single.html
devranx's picture
Upload 20 files
03e275e verified
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Single Classification</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<style>
html.dark {
--bg-color: #1a202c;
--text-color: #e2e8f0;
--card-bg-color: #2d3748;
--card-border-color: #4a5568;
--btn-bg-color: #4a5568;
--btn-hover-bg-color: #2c5282;
--btn-text-color: #e2e8f0;
}
html.light {
--bg-color: #f7fafc;
--text-color: #2d3748;
--card-bg-color: #ffffff;
--card-border-color: #e2e8f0;
--btn-bg-color: #3182ce;
--btn-hover-bg-color: #2c5282;
--btn-text-color: #ffffff;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
}
.card {
background-color: var(--card-bg-color);
border: 1px solid var(--card-border-color);
}
.btn {
background-color: var(--btn-bg-color);
color: var(--btn-text-color);
}
.btn:hover {
background-color: var(--btn-hover-bg-color);
}
.loading {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
.result-table {
font-family: monospace;
white-space: pre;
}
.classification-text {
font-size: 1.5rem;
font-weight: bold;
}
.btn-classify {
background-color: #10b981;
color: white;
font-size: 1.1rem;
padding: 0.75rem 1.5rem;
border-radius: 9999px;
}
.btn-classify:hover {
background-color: #059669;
}
.btn-remove {
background-color: #ef4444;
color: white;
font-size: 1.1rem;
padding: 0.75rem 1.5rem;
border-radius: 9999px;
}
.btn-remove:hover {
background-color: #dc2626;
}
.results-container {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.classification-result {
margin: 1.5rem 0;
text-align: center;
}
.table-container {
width: 100%;
display: flex;
justify-content: center;
text-align: left;
}
/* Loading overlay styles */
.loading-overlay {
background: rgba(0, 0, 0, 0.7);
color: white;
}
.loading-text {
color: white;
font-size: 1.2rem;
font-weight: 500;
}
</style>
</head>
<body class="min-h-screen">
<div class="container mx-auto px-4 py-8">
<div class="flex justify-end mb-4">
<button id="themeToggle" class="bg-gray-800 text-white px-4 py-2 rounded hover:bg-gray-600">
☀️
</button>
</div>
<h1 class="text-3xl font-bold text-center mb-8">Single Image Classification</h1>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Left Column: Image Upload -->
<div class="card p-6 rounded-lg shadow-md">
<div id="uploadSection" class="mb-4">
<label class="block text-sm font-bold mb-2">Upload Image</label>
<input type="file" id="imageInput" accept=".jpg,.jpeg,.png" class="hidden">
<button onclick="document.getElementById('imageInput').click()" class="btn w-full py-2 px-4 rounded">
Select Image
</button>
</div>
<div id="imagePreview" class="mt-4 hidden">
<img id="preview" class="max-w-full h-auto rounded-lg">
<div class="mt-4 flex space-x-4">
<button onclick="removeImage()" class="btn btn-remove flex-1">
Remove
</button>
<button onclick="classifyImage()" class="btn btn-classify flex-1">
Classify
</button>
</div>
</div>
</div>
<!-- Right Column: Results -->
<div class="card p-6 rounded-lg shadow-md">
<h2 class="text-xl font-bold mb-4 text-center">Classification Results</h2>
<div id="results" class="results-container"></div>
</div>
</div>
</div>
<!-- Loading Overlay -->
<div id="loading" class="loading flex items-center justify-center">
<div class="loading-overlay p-8 rounded-lg text-center">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-white mx-auto"></div>
<p id="loadingText" class="mt-4 loading-text">Processing...</p>
</div>
</div>
<script>
// Theme handling
const themeToggle = document.getElementById('themeToggle');
const htmlElement = document.documentElement;
// Initialize theme
if (!localStorage.getItem('theme')) {
localStorage.setItem('theme', 'dark');
}
// Apply saved theme or default to dark
const currentTheme = localStorage.getItem('theme') || 'dark';
htmlElement.classList.remove('light', 'dark');
htmlElement.classList.add(currentTheme);
themeToggle.textContent = currentTheme === 'dark' ? '☀️' : '🌙';
// Theme toggle handler
themeToggle.addEventListener('click', () => {
const isDark = htmlElement.classList.contains('dark');
htmlElement.classList.remove('dark', 'light');
const newTheme = isDark ? 'light' : 'dark';
htmlElement.classList.add(newTheme);
localStorage.setItem('theme', newTheme);
themeToggle.textContent = newTheme === 'dark' ? '☀️' : '🌙';
});
// File handling
let currentFile = null;
document.getElementById('imageInput').addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
uploadFile(file);
}
});
function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
showLoading('Uploading...');
fetch('/upload_single', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.filename) {
currentFile = data.filename;
document.getElementById('preview').src = `/static/uploads/single/${data.filename}`;
document.getElementById('imagePreview').classList.remove('hidden');
document.getElementById('uploadSection').classList.add('hidden');
} else {
alert(data.error || 'Upload failed');
}
})
.catch(error => {
console.error('Error:', error);
alert('Upload failed');
})
.finally(() => {
hideLoading();
});
}
function removeImage() {
document.getElementById('imageInput').value = '';
document.getElementById('imagePreview').classList.add('hidden');
document.getElementById('uploadSection').classList.remove('hidden');
document.getElementById('results').innerHTML = '';
currentFile = null;
}
function classifyImage() {
if (!currentFile) {
alert('Please upload an image first');
return;
}
showLoading('Classifying...');
fetch('/classify_single', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ filename: currentFile })
})
.then(response => response.json())
.then(data => {
if (data.error) {
throw new Error(data.error);
}
const resultsDiv = document.getElementById('results');
resultsDiv.innerHTML = `
<div class="classification-result">
<span class="font-bold">Classification: </span>
<span class="classification-text ${data.classification === 'Pass' ? 'text-green-600' : 'text-red-600'}">
${data.classification}
</span>
</div>
<div class="table-container">
<pre class="whitespace-pre-wrap font-mono text-sm">${data.result_table}</pre>
</div>
`;
})
.catch(error => {
console.error('Error:', error);
alert('Classification failed: ' + error.message);
})
.finally(() => {
hideLoading();
});
}
function showLoading(text) {
document.getElementById('loading').style.display = 'flex';
document.getElementById('loadingText').textContent = text;
}
function hideLoading() {
document.getElementById('loading').style.display = 'none';
}
</script>
</body>
</html>