// Create EmailBadger namespace
window.EmailBadger = window.EmailBadger || {};
// Global variables
let currentUser = null;
let analyzedEmails = [];
let userSettings = {
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
dark_mode: false,
vt_api_key: "",
tc_api_key: "",
vt_service_enabled: false,
tc_service_enabled: false
};
// Add VirusTotal API rate limiter and cache
const vtRateLimiter = {
queue: [],
processing: false,
lastRequestTime: 0,
minInterval: 15000, // 15 seconds between requests (4 per minute)
cache: new Map(), // Cache for VT results
async processQueue() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
const now = Date.now();
const timeSinceLastRequest = now - this.lastRequestTime;
if (timeSinceLastRequest < this.minInterval) {
await new Promise(resolve => setTimeout(resolve, this.minInterval - timeSinceLastRequest));
}
const { type, identifier, resolve, reject } = this.queue.shift();
try {
const result = await this.checkVT(type, identifier);
this.lastRequestTime = Date.now();
resolve(result);
} catch (error) {
reject(error);
}
this.processing = false;
this.processQueue();
},
async checkVT(type, identifier) {
// Check cache first
const cacheKey = `${type}:${identifier}`;
if (this.cache.has(cacheKey)) {
console.log(`Cache hit for ${cacheKey}`);
return this.cache.get(cacheKey);
}
// Add to queue if not in cache
return new Promise((resolve, reject) => {
this.queue.push({ type, identifier, resolve, reject });
this.processQueue();
});
},
// Add result to cache
cacheResult(type, identifier, result) {
const cacheKey = `${type}:${identifier}`;
this.cache.set(cacheKey, result);
}
};
// Function to check multiple items with VT
async function checkWithVirusTotal(items) {
const results = new Map();
const uniqueItems = new Set();
// Collect unique items to check
items.forEach(item => {
if (item.sha1) uniqueItems.add(`hash:${item.sha1}`);
if (item.url) uniqueItems.add(`url:${item.url}`);
if (item.base_domain) uniqueItems.add(`domain:${item.base_domain}`);
if (item.fqdn) uniqueItems.add(`fqdn:${item.fqdn}`);
});
// Process each unique item
for (const item of uniqueItems) {
const [type, identifier] = item.split(':');
try {
const result = await vtRateLimiter.checkVT(type, identifier);
results.set(item, result);
} catch (error) {
console.error(`Error checking ${type} ${identifier}:`, error);
results.set(item, { detections: 0, total: 0 });
}
}
return results;
}
// Function to load current user information
async function loadCurrentUser() {
try {
console.log('Loading current user information...');
const response = await fetch('/auth/current-user/', {
credentials: 'include'
});
if (!response.ok) {
if (response.status === 401) {
console.warn('Authentication required. Redirecting to login page.');
if (window.location.pathname !== '/login') {
window.location.href = '/login';
}
return false;
}
throw new Error(`Failed to load user info: ${response.statusText}`);
}
const userInfo = await response.json();
console.log('Loaded user info:', userInfo);
// Update currentUser variable
currentUser = userInfo;
// Update admin-only elements visibility
const adminOnlyElements = document.querySelectorAll('.admin-only');
if (currentUser && currentUser.role === 'Administrator') {
adminOnlyElements.forEach(element => {
element.style.display = '';
});
} else {
adminOnlyElements.forEach(element => {
element.style.display = 'none';
});
}
return true;
} catch (error) {
console.error('Error loading current user:', error);
return false;
}
}
// Define missing functions if they don't exist
if (typeof loadUserSettings !== 'function') {
window.loadUserSettings = async function() {
console.log('Loading user settings...');
try {
const response = await fetch('/settings/', {
credentials: 'include'
});
if (!response.ok) {
// If unauthorized, redirect to login page
if (response.status === 401) {
console.warn('Authentication required. Redirecting to login page.');
// Only redirect if not already on login page
if (window.location.pathname !== '/login') {
window.location.href = '/login';
}
return false;
}
throw new Error(`Failed to load settings: ${response.statusText}`);
}
const settings = await response.json();
console.log('Loaded user settings:', settings);
// Update user settings object
userSettings.timezone = settings.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone;
userSettings.dark_mode = settings.dark_mode || false;
userSettings.vt_api_key = settings.vt_api_key || "";
userSettings.tc_api_key = settings.tc_api_key || "";
userSettings.vt_service_enabled = settings.vt_service_enabled || false;
userSettings.tc_service_enabled = settings.tc_service_enabled || false;
// Apply dark mode if needed
toggleDarkMode(userSettings.dark_mode);
// Update service toggle states in the UI
const vtToggle = document.getElementById('vt-service-toggle');
const tcToggle = document.getElementById('tc-service-toggle');
if (vtToggle) {
vtToggle.checked = userSettings.vt_service_enabled;
}
if (tcToggle) {
tcToggle.checked = userSettings.tc_service_enabled;
}
return true;
} catch (error) {
console.error('Error loading user settings:', error);
return false;
}
};
}
if (typeof initializeFileUpload !== 'function') {
window.initializeFileUpload = function() {
console.log('Initializing file upload...');
// Set up file input change handler
const fileInput = document.getElementById('email-file');
if (fileInput) {
console.log('Found email-file input, adding change event listener');
// Remove any existing listeners by cloning and replacing
const newFileInput = fileInput.cloneNode(true);
fileInput.parentNode.replaceChild(newFileInput, fileInput);
newFileInput.addEventListener('change', function() {
console.log('File input changed:', this.files[0]?.name);
const fileName = this.files[0]?.name || 'Choose file';
const label = this.nextElementSibling;
if (label) {
label.textContent = fileName;
}
});
} else {
console.error('Could not find email-file input element');
}
// Set up upload form submit handler
const uploadForm = document.getElementById('upload-form');
if (uploadForm) {
console.log('Found upload-form, removing any existing listeners and adding new submit event listener');
// Remove any existing listeners by cloning and replacing the form
const newForm = uploadForm.cloneNode(true);
uploadForm.parentNode.replaceChild(newForm, uploadForm);
// Re-initialize the file input after form replacement
const newFileInput = newForm.querySelector('#email-file');
if (newFileInput) {
console.log('Re-initializing file input after form replacement');
newFileInput.addEventListener('change', function() {
console.log('File input changed after form replacement:', this.files[0]?.name);
const fileName = this.files[0]?.name || 'Choose file';
const label = this.nextElementSibling;
if (label) {
label.textContent = fileName;
}
});
}
newForm.addEventListener('submit', async function(e) {
e.preventDefault();
console.log('Upload form submitted');
// Prevent double submission
if (this.dataset.submitting === 'true') {
console.log('Form already submitting, ignoring duplicate submission');
return;
}
this.dataset.submitting = 'true';
const fileInput = this.querySelector('#email-file');
if (!fileInput || !fileInput.files || !fileInput.files[0]) {
alert('Please select a file to analyze.');
this.dataset.submitting = 'false';
return;
}
const file = fileInput.files[0];
const progressDiv = document.getElementById('upload-progress');
const resultsContainer = document.getElementById('results-container');
const analysisResults = document.getElementById('analysis-results');
if (progressDiv) {
progressDiv.innerHTML = '
Starting analysis of file: ' + file.name + '
';
progressDiv.style.minHeight = 'auto';
progressDiv.style.height = 'auto';
progressDiv.style.overflow = 'visible';
}
try {
// Set a flag to prevent dashboard refresh from triggering analysis
window.skipAnalysis = true;
// Create FormData object to send the file
const formData = new FormData();
formData.append('file', file);
// Update progress
if (progressDiv) {
progressDiv.innerHTML += '
Uploading file...
';
}
// Send the file to the server
const response = await fetch('/upload/', {
method: 'POST',
credentials: 'include',
body: formData
});
if (!response.ok) {
throw new Error(`Upload failed: ${response.statusText}`);
}
// Get the response data
const data = await response.json();
console.log('Upload response:', data);
// Update progress
if (progressDiv) {
progressDiv.innerHTML += '
File uploaded successfully.
';
progressDiv.innerHTML += '
Analyzing email content...
';
}
// Get the message_id from the response
const messageId = data.message_id;
// Fetch the analysis results
console.log('Fetching analysis for message ID:', messageId);
const analysisResponse = await fetch(`/analysis/${messageId}`, {
credentials: 'include'
});
if (!analysisResponse.ok) {
throw new Error(`Analysis failed: ${analysisResponse.statusText}`);
}
// Get the analysis data
const analysisData = await analysisResponse.json();
console.log('Analysis data received:', {
messageId: analysisData.message_id,
hasHops: !!(analysisData && analysisData.hops),
hopsLength: analysisData.hops ? analysisData.hops.length : 0,
hopData: analysisData.hops,
dataKeys: Object.keys(analysisData)
});
// Update progress
if (progressDiv) {
progressDiv.innerHTML += '
Analysis complete!
';
}
// Show the results container
if (resultsContainer) {
resultsContainer.classList.remove('d-none');
}
// Reset the file input and form
newForm.reset();
const label = newForm.querySelector('.custom-file-label');
if (label) {
label.textContent = 'Choose file';
}
// Display the analysis results
if (analysisResults) {
console.log('Calling displayAnalysisResults with data:', analysisData);
displayAnalysisResults(analysisData, messageId);
}
// Update the dashboard table without triggering a new analysis
if (window.location.hash === '#dashboard') {
const tableBody = document.getElementById('emails-table-body');
if (tableBody) {
// Add the new email to the top of the table
const newRow = `
`;
// Insert at the top of the table
const firstRow = tableBody.querySelector('tr');
if (firstRow) {
firstRow.insertAdjacentHTML('beforebegin', newRow);
} else {
tableBody.innerHTML = newRow;
}
// Add click event to the new row
const newRowElement = tableBody.querySelector(`tr[data-message-id="${messageId}"]`);
if (newRowElement) {
newRowElement.addEventListener('click', function() {
showEmailAnalysis(messageId);
});
}
}
}
// Reset the skip analysis flag after a short delay
setTimeout(() => {
window.skipAnalysis = false;
newForm.dataset.submitting = 'false';
}, 1000);
} catch (error) {
console.error('Error uploading and analyzing file:', error);
// Reset the skip analysis flag and form state
window.skipAnalysis = false;
newForm.dataset.submitting = 'false';
if (progressDiv) {
progressDiv.innerHTML += `
Error: ${error.message}
`;
}
// Show a fallback message
if (resultsContainer) {
resultsContainer.classList.remove('d-none');
}
if (analysisResults) {
analysisResults.innerHTML = `
An error occurred while analyzing the email: ${error.message}
Please try again or contact support if the problem persists.
`;
}
}
});
} else {
console.error('Could not find upload-form element');
}
};
}
// Rest of the file remains unchanged...
// Function to show email analysis for a specific message ID
async function showEmailAnalysis(messageId) {
try {
console.log('Starting showEmailAnalysis for message ID:', messageId);
// First ensure we're in the analyze view
if (window.location.hash !== '#analyze') {
console.log('Switching to analyze view first');
window.location.hash = '#analyze';
// Wait for the view to load
await new Promise(resolve => setTimeout(resolve, 500));
}
// Show loading indicator in main content
const mainContent = document.getElementById('main-content');
if (mainContent) {
mainContent.innerHTML = `
Loading email analysis...
`;
}
// Fetch the analysis results
console.log('Fetching analysis data from /analysis/' + messageId);
const response = await fetch(`/analysis/${messageId}`, {
credentials: 'include'
});
if (!response.ok) {
throw new Error(`Failed to load analysis: ${response.statusText}`);
}
// Get the analysis data
const analysisData = await response.json();
console.log('Analysis data received:', {
messageId: analysisData.message_id,
hasHops: !!(analysisData && analysisData.hops),
hopsLength: analysisData.hops ? analysisData.hops.length : 0,
hopData: analysisData.hops,
fullData: analysisData
});
// Instead of waiting for the analyze view elements, create our own results view
if (mainContent) {
mainContent.innerHTML = `
Email Analysis Results
`;
}
// Now we can safely get the analysis results container
const analysisResults = document.getElementById('analysis-results');
if (!analysisResults) {
throw new Error('Failed to create analysis results container');
}
// Format and display the results
console.log('Calling displayAnalysisResults with data:', analysisData);
displayAnalysisResults(analysisData, messageId);
} catch (error) {
console.error('Error showing email analysis:', error);
// Show error message in the main content
const mainContent = document.getElementById('main-content');
if (mainContent) {
mainContent.innerHTML = `
Error Loading Analysis
${error.message}
`;
} else {
alert(`Error loading email analysis: ${error.message}`);
}
}
}
// Function to display analysis results
function displayAnalysisResults(data, messageId) {
const analysisResults = document.getElementById('analysis-results');
if (!analysisResults) {
console.error('Analysis results container not found');
return;
}
// Specific logging for hop data
console.log('=== Hop Card Debug ===');
console.log('1. Raw hop data:', data.hops);
console.log('2. Hop data type:', typeof data.hops);
console.log('3. Is hops array?', Array.isArray(data.hops));
console.log('4. Data object keys:', Object.keys(data));
if (data.hops) {
console.log('5. Number of hops:', data.hops.length);
console.log('6. First hop:', data.hops[0]);
console.log('7. First hop keys:', Object.keys(data.hops[0] || {}));
}
// Check each condition separately
const hasData = !!data;
const hasHopsProperty = !!(data && data.hops);
const isArray = !!(data && data.hops && Array.isArray(data.hops));
const hasLength = !!(data && data.hops && Array.isArray(data.hops) && data.hops.length > 0);
console.log('8. Hop card conditions:', {
hasData,
hasHopsProperty,
isArray,
hasLength,
shouldShowCard: hasData && hasHopsProperty && isArray && hasLength
});
console.log('===================');
// More detailed debug logging
console.log('Full analysis data:', JSON.stringify(data, null, 2));
console.log('Data type:', typeof data);
console.log('Hops property exists:', 'hops' in data);
console.log('Hops value:', data.hops);
console.log('Is hops an array?', Array.isArray(data.hops));
if (data.hops) {
console.log('Number of hops:', data.hops.length);
console.log('First hop:', data.hops[0]);
}
// Format the date in the user's timezone
const formatDate = (dateString) => {
if (!dateString) return 'N/A';
try {
const date = new Date(dateString);
return date.toLocaleString();
} catch (e) {
console.warn('Error formatting date:', e);
return dateString;
}
};
// Create the HTML content
let html = `
`;
// Add screenshot section (always show)
html += `
Email Preview
`;
if (data.screenshot_path) {
html += `
`;
} else if (data.content_type === 'text/plain') {
html += `
This is a plain text email. No preview is available.
`;
} else {
html += `
No preview is available for this email.
`;
}
html += `
`;
// Close the card
html += `
`;
// Update the results container
analysisResults.innerHTML = html;
}
// Helper function to format file size
function formatFileSize(bytes) {
if (!bytes) return 'N/A';
const units = ['B', 'KB', 'MB', 'GB'];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(1)} ${units[unitIndex]}`;
}
// Toggle dark mode function
function toggleDarkMode(enabled) {
console.log('Toggling dark mode:', enabled);
if (enabled) {
document.body.classList.add('dark-mode');
} else {
document.body.classList.remove('dark-mode');
}
// Save preference to localStorage
localStorage.setItem('dark-mode', enabled);
// Update user settings object
userSettings.dark_mode = enabled;
}
function validateServiceSettings() {
const vtApiKey = document.getElementById('vt-api-key').value.trim();
const tcApiKey = document.getElementById('tc-api-key').value.trim();
const vtToggle = document.getElementById('vt-service-toggle');
const tcToggle = document.getElementById('tc-service-toggle');
// Disable service toggles if no API key is provided
if (vtToggle) {
vtToggle.disabled = !vtApiKey;
if (!vtApiKey) {
vtToggle.checked = false;
}
}
if (tcToggle) {
tcToggle.disabled = !tcApiKey;
if (!tcApiKey) {
tcToggle.checked = false;
}
}
}
function setupServiceToggleValidation() {
const vtApiKeyInput = document.getElementById('vt-api-key');
const tcApiKeyInput = document.getElementById('tc-api-key');
const vtToggle = document.getElementById('vt-service-toggle');
const tcToggle = document.getElementById('tc-service-toggle');
if (vtApiKeyInput && vtToggle) {
vtApiKeyInput.addEventListener('input', validateServiceSettings);
vtToggle.addEventListener('change', function() {
if (this.checked && !vtApiKeyInput.value.trim()) {
alert('Please provide a VirusTotal API key before enabling the service.');
this.checked = false;
}
});
}
if (tcApiKeyInput && tcToggle) {
tcApiKeyInput.addEventListener('input', validateServiceSettings);
tcToggle.addEventListener('change', function() {
if (this.checked && !tcApiKeyInput.value.trim()) {
alert('Please provide a ThreatConnect API key before enabling the service.');
this.checked = false;
}
});
}
}
// Define functions first
function formatDateInUserTimezone(dateString) {
if (!dateString) return "";
const date = new Date(dateString);
const options = {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: true
};
return date.toLocaleString("en-US", options);
}
function checkUnauthorized(response) {
if (response.status === 401) {
// Only redirect if not already on login page
if (window.location.pathname !== '/login') {
window.location.href = "/login";
}
return false;
}
return true;
}
async function loadAnalyzedEmails() {
try {
console.log('Loading analyzed emails...');
// If we're skipping analysis (after a new upload), just return
if (window.skipAnalysis) {
console.log('Skipping analysis as requested');
return;
}
// Try to use the inbox endpoint which now supports GET requests
let response;
try {
console.log('Fetching from /inbox endpoint...');
response = await fetch('/inbox', {
credentials: 'include',
headers: {
'Accept': 'application/json'
}
});
console.log('Response status:', response.status);
console.log('Response headers:', Object.fromEntries(response.headers.entries()));
if (!response.ok) {
console.warn(`API returned error: ${response.status} ${response.statusText}`);
// Check if unauthorized
if (response.status === 401) {
console.log('Unauthorized access, redirecting to login...');
// Only redirect if not already on login page
if (window.location.pathname !== '/login') {
window.location.href = '/login';
}
return;
}
// Show error message but don't fall back to sample data
const tableBody = document.getElementById('emails-table-body');
if (tableBody) {
tableBody.innerHTML = `
Error loading emails: ${response.statusText}
`;
}
return;
}
// Parse the response
const emails = await response.json();
console.log('Loaded emails from /inbox:', emails);
if (!Array.isArray(emails)) {
console.error('Expected array of emails but got:', typeof emails);
const tableBody = document.getElementById('emails-table-body');
if (tableBody) {
tableBody.innerHTML = `
Invalid response format from server
`;
}
return;
}
// Store in global variable
analyzedEmails = emails;
// Populate the table
const tableBody = document.getElementById('emails-table-body');
if (!tableBody) {
console.error('emails-table-body element not found');
return;
}
if (emails.length === 0) {
console.log('No emails found in database');
tableBody.innerHTML = '
No emails analyzed yet
';
return;
}
console.log('Populating table with', emails.length, 'emails');
// Update table header to show admin column
const tableHeader = document.querySelector('#emails-table thead tr');
if (tableHeader) {
// First, check if the admin header exists
let adminHeader = tableHeader.querySelector('.admin-only');
if (!adminHeader) {
// If it doesn't exist, create it
adminHeader = document.createElement('th');
adminHeader.className = 'admin-only';
adminHeader.textContent = 'Uploaded By';
// Insert it as the last column
tableHeader.appendChild(adminHeader);
}
// Always update its visibility based on user role
adminHeader.style.display = currentUser && currentUser.role === 'Administrator' ? '' : 'none';
// Also update the column header text
const dateHeader = tableHeader.querySelector('th:first-child');
if (dateHeader) {
dateHeader.textContent = 'Date Analyzed';
}
}
// Clear existing click event listeners
const existingRows = document.querySelectorAll('.email-row');
existingRows.forEach(row => {
row.replaceWith(row.cloneNode(true));
});
// Generate table HTML with message_id in data attribute
tableBody.innerHTML = emails.map(email => {
// Use the stored counts directly from the email record
const numAttachments = email.num_attachments || 0;
const numUrls = email.num_hyperlinks || 0;
// Ensure message_id is available
if (!email.message_id) {
console.warn('Email missing message_id:', email);
return '';
}
return `
${formatDateInUserTimezone(email.created_at)}
${email.recipient || 'N/A'}
${email.sender || 'N/A'}
${email.subject || 'N/A'}
${numAttachments}
${numUrls}
${email.uploaded_by || 'N/A'}
`;
}).join('');
// Set up click handlers for the new rows
setupEmailRowClickHandlers();
} catch (error) {
console.error('Failed to fetch from /inbox:', error);
// Show error message but don't fall back to sample data
const tableBody = document.getElementById('emails-table-body');
if (tableBody) {
tableBody.innerHTML = `
Error loading emails: ${error.message}
`;
}
return;
}
} catch (error) {
console.error('Error loading analyzed emails:', error);
// Show error message but don't fall back to sample data
const tableBody = document.getElementById('emails-table-body');
if (tableBody) {
tableBody.innerHTML = `
Error loading emails: ${error.message}
`;
}
}
}
// Initialize settings functionality
if (typeof initializeSettings !== 'function') {
window.initializeSettings = function() {
console.log('Initializing settings...');
// Populate timezone dropdown
const timezoneSelect = document.getElementById('timezone-select');
if (timezoneSelect) {
// Get all available timezones
const timezones = [
'UTC',
'America/New_York',
'America/Chicago',
'America/Denver',
'America/Los_Angeles',
'America/Anchorage',
'America/Honolulu',
'America/Puerto_Rico',
'Europe/London',
'Europe/Paris',
'Europe/Berlin',
'Europe/Moscow',
'Asia/Tokyo',
'Asia/Shanghai',
'Asia/Kolkata',
'Asia/Dubai',
'Australia/Sydney',
'Australia/Perth',
'Pacific/Auckland'
];
// Sort timezones alphabetically
timezones.sort();
// Add options to select
timezones.forEach(timezone => {
const option = document.createElement('option');
option.value = timezone;
option.textContent = timezone;
timezoneSelect.appendChild(option);
});
// Set current timezone
timezoneSelect.value = userSettings.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone;
}
// Set dark mode toggle
const darkModeSelect = document.getElementById('dark-mode-select');
if (darkModeSelect) {
// Set the initial state based on user settings
darkModeSelect.checked = userSettings.dark_mode;
// Add event listener to immediately apply dark mode changes when toggled
darkModeSelect.addEventListener('change', function() {
toggleDarkMode(this.checked);
console.log('Dark mode toggled in settings to:', this.checked);
});
}
// Handle settings form submission
const settingsForm = document.getElementById('settings-form');
if (settingsForm) {
settingsForm.addEventListener('submit', async function(e) {
e.preventDefault();
const timezone = timezoneSelect.value;
const darkMode = darkModeSelect.checked;
const statusDiv = document.getElementById('settings-status');
try {
// Prepare data for API
const settingsData = {
timezone: timezone,
dark_mode: darkMode,
vt_api_key: null,
tc_api_key: null
};
// Save settings via API
const response = await fetch('/settings/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify(settingsData)
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || `Failed to save settings: ${response.statusText}`);
}
// Show success message
if (statusDiv) {
statusDiv.innerHTML = `
Settings saved successfully
`;
}
// Update user settings object
userSettings.timezone = timezone;
userSettings.dark_mode = darkMode;
// Apply dark mode if changed
toggleDarkMode(darkMode);
} catch (error) {
console.error('Error saving settings:', error);
if (statusDiv) {
statusDiv.innerHTML = `
`;
// Add screenshot section (always show)
html += `
Email Preview
`;
if (data.screenshot_path) {
html += `
`;
} else if (data.content_type === 'text/plain') {
html += `
This is a plain text email. No preview is available.
`;
} else {
html += `
No preview is available for this email.
`;
}
html += `
`;
// Close the card
html += `
`;
// Update the results container
analysisResults.innerHTML = html;
// Save the PDF
doc.save(`email-analysis-${messageId}.pdf`);
// Reset button state
button.innerHTML = originalContent;
button.disabled = false;
} catch (error) {
console.error('Error generating PDF:', error);
alert(`Error generating PDF: ${error.message}`);
// Reset button state
const button = document.querySelector('button[onclick^="exportToPDF"]');
if (button) {
button.innerHTML = ' Export to PDF';
button.disabled = false;
}
}
}
// Add function to update sidebar active state
function updateSidebarActiveState(hash) {
// Remove active class from all sidebar items
document.querySelectorAll('.nav-link').forEach(link => {
link.classList.remove('active');
});
// Add active class to current section
const currentSection = hash.replace('#', '');
const activeLink = document.querySelector(`.nav-link[href="#${currentSection}"]`);
if (activeLink) {
activeLink.classList.add('active');
// Also add active class to parent li if it exists
const parentLi = activeLink.closest('li');
if (parentLi) {
parentLi.classList.add('active');
}
}
// Handle admin-only visibility
const adminOnlyElements = document.querySelectorAll('.admin-only');
if (currentUser && currentUser.role === 'Administrator') {
adminOnlyElements.forEach(element => {
element.style.display = '';
});
} else {
adminOnlyElements.forEach(element => {
element.style.display = 'none';
});
}
}
// Add event listener for hash changes
window.addEventListener('hashchange', function() {
updateSidebarActiveState(window.location.hash);
});
// Initialize sidebar state and admin visibility on page load
document.addEventListener('DOMContentLoaded', function() {
// Set initial sidebar state
updateSidebarActiveState(window.location.hash || '#dashboard');
// Ensure admin-only elements are properly hidden/shown
const adminOnlyElements = document.querySelectorAll('.admin-only');
if (currentUser && currentUser.role === 'Administrator') {
adminOnlyElements.forEach(element => {
element.style.display = '';
});
} else {
adminOnlyElements.forEach(element => {
element.style.display = 'none';
});
}
});
// Update the existing loadView function
if (typeof EmailBadger.loadView !== 'function') {
EmailBadger.loadView = async function(view) {
console.log('Loading view:', view);
try {
// Get the view content
const response = await fetch(`/views/${view}.html`);
if (!response.ok) {
console.error(`Failed to load ${view} view:`, response.statusText);
return;
}
const content = await response.text();
const mainContent = document.getElementById('main-content');
if (mainContent) {
mainContent.innerHTML = content;
// Initialize view-specific functionality
if (view === 'analyze') {
// Check if we have a message_id in the URL
const urlParams = new URLSearchParams(window.location.search);
const messageId = urlParams.get('message_id');
if (messageId) {
console.log('Found message_id in URL:', messageId);
// Wait for the view to be fully loaded
await new Promise(resolve => setTimeout(resolve, 100));
// Show the analysis
await showEmailAnalysis(messageId);
} else {
// Initialize the upload form for new analysis
initializeFileUpload();
}
} else if (view === 'dashboard') {
loadAnalyzedEmails();
} else if (view === 'settings') {
initializeSettings();
} else if (view === 'administration' && currentUser && currentUser.role === 'Administrator') {
initializeAdministration();
initializeUserManagement();
}
}
} catch (error) {
console.error(`Error loading ${view} view:`, error);
const mainContent = document.getElementById('main-content');
if (mainContent) {
mainContent.innerHTML = `
Error Loading View
${error.message}
`;
}
}
};
}
// Update the email row click handler
function setupEmailRowClickHandlers() {
console.log('Setting up click handlers for email rows');
document.querySelectorAll('.email-row').forEach(row => {
row.addEventListener('click', async function(e) {
e.preventDefault();
e.stopPropagation();
const messageId = this.getAttribute('data-message-id');
console.log('Clicked email row with message_id:', messageId);
if (!messageId) {
console.error('No message_id found for clicked row');
return;
}
try {
// First switch to analyze view and wait for it to load
console.log('Switching to analyze view');
window.location.hash = '#analyze';
// Wait for the view to load
await new Promise(resolve => setTimeout(resolve, 500));
// Show loading state
const mainContent = document.getElementById('main-content');
if (mainContent) {
mainContent.innerHTML = `
Loading email analysis...
`;
}
// Fetch and display the analysis
await showEmailAnalysis(messageId);
// Update the URL to include the message ID
const newUrl = new URL(window.location.href);
newUrl.searchParams.set('message_id', messageId);
window.history.pushState({}, '', newUrl);
} catch (error) {
console.error('Error handling email click:', error);
alert(`Error loading email analysis: ${error.message}`);
}
});
});
}