handwritten / static /script.js
IZERE HIRWA Roger
io
ed74fda
raw
history blame
14.9 kB
// Global variables
let categories = [];
let documents = [];
let authToken = null;
let currentUser = null;
// Initialize app
document.addEventListener('DOMContentLoaded', function() {
checkAuth();
});
// Authentication functions
function checkAuth() {
authToken = localStorage.getItem('authToken');
currentUser = localStorage.getItem('currentUser');
if (authToken && currentUser) {
showMainApp();
document.getElementById('welcomeUser').textContent = `Welcome, ${currentUser}`;
loadStats();
loadCategories();
setupFileUploads();
} else {
showLoginModal();
}
}
function showLoginModal() {
document.getElementById('loginModal').style.display = 'flex';
document.getElementById('mainApp').style.display = 'none';
}
function showMainApp() {
document.getElementById('loginModal').style.display = 'none';
document.getElementById('mainApp').style.display = 'block';
}
function logout() {
localStorage.removeItem('authToken');
localStorage.removeItem('currentUser');
authToken = null;
currentUser = null;
showLoginModal();
}
// Login form handler
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const resultDiv = document.getElementById('loginResult');
const formData = new FormData();
formData.append('username', username);
formData.append('password', password);
try {
const response = await fetch('/api/login', {
method: 'POST',
body: formData
});
const result = await response.json();
if (response.ok) {
authToken = result.access_token;
currentUser = result.username;
localStorage.setItem('authToken', authToken);
localStorage.setItem('currentUser', currentUser);
showMainApp();
document.getElementById('welcomeUser').textContent = `Welcome, ${currentUser}`;
loadStats();
loadCategories();
setupFileUploads();
} else {
showResult(resultDiv, result.detail, 'error');
}
} catch (error) {
showResult(resultDiv, 'Login failed: ' + error.message, 'error');
}
});
// API request with authentication
async function authenticatedFetch(url, options = {}) {
if (!authToken) {
throw new Error('No authentication token');
}
const defaultOptions = {
headers: {
'Authorization': `Bearer ${authToken}`,
...options.headers
}
};
const response = await fetch(url, { ...options, ...defaultOptions });
if (response.status === 401) {
logout();
throw new Error('Authentication failed');
}
return response;
}
// Tab management
function showTab(tabName) {
// Hide all tabs
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.remove('active');
});
document.querySelectorAll('.tab-button').forEach(btn => {
btn.classList.remove('active');
});
// Show selected tab
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
// Load data for specific tabs
if (tabName === 'browse') {
loadCategories();
loadAllDocuments();
}
}
// Setup file upload drag & drop
function setupFileUploads() {
const uploads = [
{ div: 'categoryUpload', input: 'categoryFile' },
{ div: 'classifyUpload', input: 'classifyFile' },
{ div: 'ocrUpload', input: 'ocrFile' }
];
uploads.forEach(upload => {
const uploadDiv = document.getElementById(upload.div);
const fileInput = document.getElementById(upload.input);
uploadDiv.addEventListener('click', () => fileInput.click());
uploadDiv.addEventListener('dragover', (e) => {
e.preventDefault();
uploadDiv.classList.add('dragover');
});
uploadDiv.addEventListener('dragleave', () => {
uploadDiv.classList.remove('dragover');
});
uploadDiv.addEventListener('drop', (e) => {
e.preventDefault();
uploadDiv.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
fileInput.files = files;
uploadDiv.querySelector('p').textContent = files[0].name;
}
});
fileInput.addEventListener('change', () => {
if (fileInput.files.length > 0) {
uploadDiv.querySelector('p').textContent = fileInput.files[0].name;
}
});
});
}
// Load dashboard stats
async function loadStats() {
try {
const response = await authenticatedFetch('/api/stats');
const stats = await response.json();
const statsHtml = `
<div class="stat-card">
<h3>${stats.total_categories}</h3>
<p><i class="fas fa-tags"></i> Total Categories</p>
</div>
<div class="stat-card">
<h3>${stats.total_documents}</h3>
<p><i class="fas fa-file"></i> Documents Archived</p>
</div>
<div class="stat-card">
<h3>35%</h3>
<p><i class="fas fa-percentage"></i> Min Confidence</p>
</div>
`;
document.getElementById('stats').innerHTML = statsHtml;
} catch (error) {
console.error('Error loading stats:', error);
}
}
// Load categories
async function loadCategories() {
try {
const response = await authenticatedFetch('/api/categories');
const data = await response.json();
categories = data.categories;
const buttonsHtml = `
<button class="category-btn active" onclick="filterDocuments('all')">
All Documents
</button>
${categories.map(cat => `
<button class="category-btn" onclick="filterDocuments('${cat}')">
${cat} (${data.counts[cat] || 0})
</button>
`).join('')}
`;
document.getElementById('categoryButtons').innerHTML = buttonsHtml;
} catch (error) {
console.error('Error loading categories:', error);
}
}
// Load all documents
async function loadAllDocuments() {
try {
const response = await authenticatedFetch('/api/documents');
const data = await response.json();
documents = data.documents;
displayDocuments(documents);
} catch (error) {
console.error('Error loading documents:', error);
}
}
// Filter documents by category
async function filterDocuments(category) {
// Update active button
document.querySelectorAll('.category-btn').forEach(btn => {
btn.classList.remove('active');
});
event.target.classList.add('active');
try {
let filteredDocs;
if (category === 'all') {
const response = await authenticatedFetch('/api/documents');
const data = await response.json();
filteredDocs = data.documents;
} else {
const response = await authenticatedFetch(`/api/documents/${category}`);
const data = await response.json();
filteredDocs = data.documents;
}
displayDocuments(filteredDocs);
} catch (error) {
console.error('Error filtering documents:', error);
}
}
// Delete document
async function deleteDocument(documentId, filename) {
if (!confirm(`Are you sure you want to delete "${filename}"? This action cannot be undone.`)) {
return;
}
try {
const response = await authenticatedFetch(`/api/documents/${documentId}`, {
method: 'DELETE'
});
const result = await response.json();
if (response.ok) {
// Refresh the current view
loadAllDocuments();
loadStats();
loadCategories();
alert('Document deleted successfully');
} else {
alert('Failed to delete document: ' + result.detail);
}
} catch (error) {
alert('Error deleting document: ' + error.message);
}
}
// Display documents
function displayDocuments(docs) {
const container = document.getElementById('documentsContainer');
if (docs.length === 0) {
container.innerHTML = '<p>No documents found for this category.</p>';
return;
}
const docsHtml = docs.map(doc => {
const similarityClass = doc.similarity >= 0.7 ? 'similarity-high' :
doc.similarity >= 0.5 ? 'similarity-medium' : 'similarity-low';
return `
<div class="document-card">
<h4><i class="fas fa-file"></i> ${doc.original_filename}</h4>
<p><strong>Category:</strong> ${doc.category}</p>
<p><strong>Confidence:</strong>
<span class="similarity-badge ${similarityClass}">
${(doc.similarity * 100).toFixed(1)}%
</span>
</p>
<p><strong>Upload Date:</strong> ${new Date(doc.upload_date).toLocaleDateString()}</p>
<p><strong>OCR Preview:</strong></p>
<div style="max-height: 100px; overflow-y: auto; background: #f8f9fa; padding: 0.5rem; border-radius: 4px; font-size: 0.8rem;">
${doc.ocr_text.substring(0, 200)}${doc.ocr_text.length > 200 ? '...' : ''}
</div>
<div class="document-actions">
<button class="btn btn-danger" onclick="deleteDocument('${doc.id}', '${doc.original_filename}')">
<i class="fas fa-trash"></i> Delete
</button>
</div>
</div>
`;
}).join('');
container.innerHTML = `<div class="document-grid">${docsHtml}</div>`;
}
// Form submissions
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
e.preventDefault();
const fileInput = document.getElementById('categoryFile');
const labelInput = document.getElementById('categoryLabel');
const resultDiv = document.getElementById('uploadResult');
if (!fileInput.files[0] || !labelInput.value.trim()) {
showResult(resultDiv, 'Please select a file and enter a label.', 'error');
return;
}
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('label', labelInput.value.trim());
showResult(resultDiv, '<div class="loading"></div> Uploading...', 'info');
try {
const response = await authenticatedFetch('/api/upload-category', {
method: 'POST',
body: formData
});
const result = await response.json();
if (response.ok) {
showResult(resultDiv, result.message, 'success');
labelInput.value = '';
fileInput.value = '';
document.querySelector('#categoryUpload p').textContent = 'Click to select or drag & drop files here';
loadStats();
loadCategories();
} else {
showResult(resultDiv, result.detail, 'error');
}
} catch (error) {
showResult(resultDiv, 'Upload failed: ' + error.message, 'error');
}
});
document.getElementById('classifyForm').addEventListener('submit', async (e) => {
e.preventDefault();
const fileInput = document.getElementById('classifyFile');
const resultDiv = document.getElementById('classifyResult');
if (!fileInput.files[0]) {
showResult(resultDiv, 'Please select a file to classify.', 'error');
return;
}
const formData = new FormData();
formData.append('file', fileInput.files[0]);
showResult(resultDiv, '<div class="loading"></div> Classifying...', 'info');
try {
const response = await authenticatedFetch('/api/classify-document', {
method: 'POST',
body: formData
});
const result = await response.json();
if (response.ok) {
const confidenceText = result.confidence === 'high' ? 'βœ… High Confidence' : '⚠️ Low Confidence';
const savedText = result.document_saved ? '\nπŸ“ Document saved to archive' : '';
let matchesText = '\n\nTop matches:\n';
result.matches.forEach(match => {
matchesText += `β€’ ${match.category}: ${(match.similarity * 100).toFixed(1)}%\n`;
});
showResult(resultDiv,
`🎯 Classification: ${result.category}\n` +
`${confidenceText} (${(result.similarity * 100).toFixed(1)}%)${savedText}${matchesText}`,
result.confidence === 'high' ? 'success' : 'warning'
);
fileInput.value = '';
document.querySelector('#classifyUpload p').textContent = 'Click to select or drag & drop files here';
loadStats();
} else {
showResult(resultDiv, result.detail, 'error');
}
} catch (error) {
showResult(resultDiv, 'Classification failed: ' + error.message, 'error');
}
});
document.getElementById('ocrForm').addEventListener('submit', async (e) => {
e.preventDefault();
const fileInput = document.getElementById('ocrFile');
const resultDiv = document.getElementById('ocrResult');
if (!fileInput.files[0]) {
showResult(resultDiv, 'Please select a file for OCR.', 'error');
return;
}
const formData = new FormData();
formData.append('file', fileInput.files[0]);
showResult(resultDiv, '<div class="loading"></div> Extracting text...', 'info');
try {
const response = await authenticatedFetch('/api/ocr', {
method: 'POST',
body: formData
});
const result = await response.json();
if (response.ok) {
showResult(resultDiv, result.text, 'success');
} else {
showResult(resultDiv, result.detail, 'error');
}
} catch (error) {
showResult(resultDiv, 'OCR failed: ' + error.message, 'error');
}
});
// Utility function to show results
function showResult(element, message, type) {
const className = type === 'success' ? 'result-success' :
type === 'error' ? 'result-error' :
type === 'warning' ? 'result-warning' : '';
element.innerHTML = `<div class="result-box ${className}">${message}</div>`;
}